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.

FANGS is a delta detector, not a classifier. It watches npm packages, runs each new release inside a Docker sandbox, and flags behavior that differs from the package’s rolling baseline. The system is built from two long-running processes — fangs-orchestrator and fangs-runner — plus a CLI (fangs) and an optional web dashboard. Communication between the orchestrator and runner happens over HTTP (opt-in mTLS for production); storage defaults to SQLite with opt-in PostgreSQL.

System diagram

Components

fangs-orchestrator

Hosts the HTTP API, watcher, differ, and notifier. Owns the storage backend. Receives event batches from runners and triggers analysis when a run completes.

fangs-runner

Loads the eBPF sensor at startup, polls for jobs, spawns Docker sandboxes, streams captured events back to the orchestrator. Requires CAP_BPF and the Docker socket.

fangs CLI

Operator interface: add packages, submit one-off scans, review deviations, manage the allowlist, promote baselines. Talks to the orchestrator’s HTTP API.

eBPF sensor

Seven tracepoints, two kprobes, and one libssl uprobe attached at runner startup. No agent inside the container — kernel-level observation only.

End-to-end scan flow

1

Watcher polls npm registry

The watcher runs inside fangs-orchestrator and polls registry.npmjs.org every 5 minutes (configurable via --watcher-interval). For each watched package it calls the registry’s metadata endpoint and compares dist-tags.latest against the last-seen version stored in the database.
2

New release → sandbox_scan job queued

When a new version is detected the orchestrator records a releases row (package name, version, tarball SHA-256, npm integrity, published timestamp) and enqueues a sandbox_scan job. The sandbox spec targets node:20-slim and runs npm install <pkg>@<version> followed by a 2-second grace period.
3

Runner polls for work

The runner long-polls GET /v1/runners/{id}/jobs. A 204 No Content response means the queue is empty; a 200 body carries a Job struct with the RunID, SandboxSpec, and WatchedPaths list.
4

Runner creates cgroup, registers sensor, starts container

Before calling docker start, the runner creates a parent cgroupv2 cgroup and calls Sensor.AddCgroup with the cgroup’s inode ID and the job’s WatchedPaths. Registering before the container starts closes the race window — events from the container’s very first syscall are captured. The eBPF probes are already attached globally; only processes whose cgroup is in CGMAP produce events.
5

Sensor captures events, runner streams to orchestrator

While the container runs, the sensor emits typed events (file access, exec, net connect, DNS query, TLS SNI) into a 64 MiB kernel ringbuf. The runner reads the ringbuf, batches events into EventBatch JSON objects, and streams them to POST /v1/runs/{run_id}/events. Each batch carries a monotonically increasing seq number; the orchestrator deduplicates on (run_id, seq).
6

Orchestrator debounces, then runs Differ.AnalyzeRun

Each event-batch POST triggers a 2-second debounce timer. When the timer fires (no new batches for 2 s), the orchestrator calls Differ.AnalyzeRun(ctx, runID). The differ is also called once more when the runner posts the final ScanResult.
7

Differ extracts fingerprints, compares to baseline

AnalyzeRun loads the run’s events from the events table, applies the operator allowlist filter, and calls ExtractFingerprintsWith to produce a deduplicated set of (category, normalized_value) pairs. It then loads the package’s baseline_fingerprints. Any pair not present in the baseline becomes a Deviation row.
8

Zero deviations → auto-promote; deviations → hold for review

If the run produces zero deviations, it is automatically marked is_baseline=true and its fingerprints are merged into baseline_fingerprints (D38 auto-promotion). If there are deviations the run lands in fangs pending and waits for a human decision.
9

Notifier fires webhooks

When deviations are written, the notifier dispatches webhooks to any configured targets (Slack, Discord, generic SIEM webhook). Targets are added with fangs notifier add.
10

Operator reviews

The operator reviews flagged runs with fangs pending or through the web dashboard at http://127.0.0.1:8443/ui/. They can promote a run to baseline (fangs baseline promote <run-id>) or suppress recurring noise (fangs allow add).

Process startup order

The runner must start after the orchestrator is reachable. On first start it POST /v1/runners/register, receives a RegistrationAck containing job_poll_interval and heartbeat_interval, then begins polling. The orchestrator restart auto-expires stale runner registrations after approximately 3× the heartbeat interval.
# Terminal A — orchestrator (control plane)
./bin/fangs-orchestrator

# Terminal B — runner (requires CAP_BPF + Docker socket)
sudo ./bin/fangs-runner

# Terminal C — operator console
./bin/fangs package add axios       # watch axios; seeds first baseline
./bin/fangs pending                 # see runs awaiting review
./bin/fangs deviation list          # all deviations across packages

Wire protocol summary

All orchestrator ↔ runner traffic is JSON over HTTPS. The protocol version is negotiated at registration (proto_version: 1). Key endpoints:
MethodPathPurpose
POST/v1/runners/registerRunner introduces itself at startup
GET/v1/runners/{id}/jobsRunner long-polls for the next job
POST/v1/runs/{run_id}/eventsRunner streams EventBatch objects
POST/v1/runs/{run_id}/resultRunner posts final ScanResult
POST/v1/scansOperator submits an on-demand scan
GET/v1/healthLiveness probe
Enable mTLS before deploying beyond a single workstation. Without it, anyone on the network can register a runner and receive jobs containing attacker-controlled package names. See the TLS-mTLS wiki page for the full certificate lifecycle.

Storage

The orchestrator owns all persistent state. SQLite is the default; PostgreSQL is an opt-in via --db-url. Core tables: watched_packages, releases, runs, events, baseline_fingerprints, deviations, allow_entries, runners, notifiers. Prometheus metrics are exposed at /metrics and the dashboard at /ui/.

Build docs developers (and LLMs) love