Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/irchaosclub/FANGS/llms.txt

Use this file to discover all available pages before exploring further.

The FANGS orchestrator persists scan runs, raw eBPF events, baseline fingerprints, deviations, allowlist entries, notifier targets, and the npm package watch list in a relational database. Three backends are available: SQLite (the default, requires no external service), PostgreSQL (recommended for production and multi-runner deployments), and none (events are logged to stderr but nothing is stored). Both SQLite and PostgreSQL apply schema migrations automatically on every startup, so there is no separate migration step.

Choosing a backend

SQLite

Default. Zero external dependencies. Ideal for single-operator setups, local development, and CI pipelines. The database is a single file on disk.

PostgreSQL

Production. Handles concurrent runners, long-term retention, and high event volumes. Requires a Postgres instance reachable from the orchestrator.

None

Smoke-testing only. Events are logged to stderr but never written to disk. The Differ, UI, notifier, and watcher are all disabled.

SQLite

SQLite is selected with -storage sqlite (the default). The orchestrator creates the database file and its parent directory on startup if they do not already exist.

Configuration

-storage
string
default:"sqlite"
Must be set to sqlite (or omitted) to use this backend.
-sqlite-path
string
default:"var/lib/fangs/fangs.db"
Path to the SQLite database file. The orchestrator calls os.MkdirAll on the parent directory before opening, so the path can be nested under a directory that does not yet exist.

Startup command

./bin/fangs-orchestrator \
  -storage sqlite \
  -sqlite-path /var/lib/fangs/fangs.db

Implementation notes

  • Driver: modernc.org/sqlite (pure Go — no CGO, cross-compiles cleanly)
  • WAL mode and foreign keys are enabled on every connection via the DSN pragmas: journal_mode=WAL, foreign_keys=ON, busy_timeout=5000
  • Connection pool: max 8 open connections, max 2 idle
  • Writes serialize via SQLite’s own locking; WAL mode allows concurrent readers
SQLite is a single-file database. Back it up by copying the file while the orchestrator is stopped, or use SQLite’s online backup API. Storing the file on a network filesystem (NFS, CIFS) is not recommended — SQLite relies on POSIX file locking which many network filesystems do not implement correctly.

PostgreSQL

PostgreSQL is selected with -storage postgres. You must supply a connection string either through the -postgres-dsn flag or the $FANGS_PG_DSN environment variable. If both are set, the flag takes precedence.

Configuration

-storage
string
Set to postgres to use this backend.
-postgres-dsn
string
default:""
PostgreSQL DSN. Accepts URL form (postgres://user:pass@host:5432/dbname) or keyword form (host=db.internal dbname=fangs user=fangs password=secret). Falls back to $FANGS_PG_DSN when empty.

Startup commands

./bin/fangs-orchestrator \
  -storage postgres \
  -postgres-dsn "postgres://fangs:secret@db.internal:5432/fangs"

Implementation notes

  • Driver: github.com/jackc/pgx/v5/stdlib (pgx v5 database/sql wrapper)
  • Connection pool: max 16 open connections, max 4 idle, 1-hour max connection lifetime
  • Schema migrations use the same migration helper as SQLite, with $1-style placeholders instead of ?
  • The unique-violation detection checks for PostgreSQL error code 23505 to return storage.ErrConflict
Create a dedicated database user for FANGS with permissions scoped to the fangs database. The orchestrator only needs CREATE, SELECT, INSERT, UPDATE, and DELETE on its own tables — it does not require superuser access.

The none backend

Setting -storage none disables persistence entirely. The orchestrator starts, accepts runner registrations, dispatches jobs, and logs events to stderr, but nothing is written to disk or a database.
./bin/fangs-orchestrator -storage none
When storage is disabled, the following features are unavailable:
  • Web dashboard (/ui/) — requires a storage backend to read scan history
  • Differ — cannot compare events against a baseline without storage
  • Notifiers — webhook triggers fire after the Differ; no Differ means no webhooks
  • npm registry watcher — needs storage to track watched packages and last-seen versions
Use -storage none only for sensor smoke-tests where you want to confirm eBPF events are flowing without accumulating data.

Schema migrations

Both SQLite and PostgreSQL apply schema migrations automatically during the Migrate(ctx) call made right after Open. The migration helper in internal/orchestrator/storage walks a versioned set of SQL files and applies any that have not yet been recorded in the internal migrations table. Migrations are idempotent — restarting the orchestrator against an already-migrated database is safe and fast. There is no separate migration CLI. If the orchestrator fails to start with a migration error, check that the database user has CREATE TABLE privileges and that the database itself exists.

Event retention

Raw eBPF events accumulate quickly in high-throughput environments. FANGS includes a built-in pruner that removes old events on a configurable schedule.

How it works

  • The pruner runs as a goroutine inside the orchestrator process
  • A one-shot fires 30 seconds after startup — this gives operators an immediate confirmation in the logs that the pruner is wired correctly
  • Subsequent runs happen on the -retention-interval ticker (default: every 24h)
  • Each pass deletes all rows from the events table where ts_ns < cutoff and the event is not referenced by a row in the deviations table as evidence_event_id
  • The deletion count and cutoff timestamp are logged at INFO level after each pass

Retention flags

-retention-days
int
default:"90"
Delete raw events older than this many days. Set to 0 to disable the pruner entirely. Deviation-evidence events are always pinned — they are never deleted regardless of this value.
-retention-interval
duration
default:"24h"
Cadence of the retention pruner. Accepts standard Go duration syntax: 12h, 24h, 168h.

Example log output

{"level":"INFO","msg":"retention prune complete","deleted_rows":4821,"retain_days":90,"cutoff_ts":"2025-01-01T00:00:00Z"}

Deviation-evidence pinning

When the Differ identifies a deviation, it stores the ID of the specific event that provided the evidence in deviations.evidence_event_id. The pruner’s DELETE statement excludes any event whose id appears in that column:
DELETE FROM events
 WHERE ts_ns < ?
   AND id NOT IN (SELECT evidence_event_id FROM deviations)
The placeholder syntax is ? for SQLite and $1 for PostgreSQL. Both backends run the same logical query.
This means deviation evidence is retained indefinitely, even after its originating run falls outside the retention window. To free that storage, delete the deviation record first.
For compliance or audit use-cases where you need longer raw event retention than you want to pay for in your primary database, consider reducing -retention-days and archiving the pruned events to cold storage (e.g. S3) using a separate pipeline before the orchestrator deletes them.

Build docs developers (and LLMs) love