Port Igniter
Get Started

Architecture

How the agent, web, worker, beat, and redis services fit together, and why they share a SQLite database.

Reef Support Home

Reef is five services, with an optional AI-powered 6th: one privileged agent per monitored host, and a Django app (web, worker, beat) that shares a single image but runs a different role per container.

Reef architecture: the agent talks to web over HTTP with an API key; web enqueues jobs to redis for worker and beat, and reads/writes SQLite directly; worker and beat share the same SQLite database.

The services

  • web: gunicorn. Serves the dashboard (server-rendered shells + vanilla fetch) and every JSON API route. Enqueues scan jobs.
  • worker: celery -A app worker. Parses raw scan output into Finding rows and runs the notification rules.
  • beat: celery -A app beat. Every 60 seconds: turns due ScanSchedules into jobs (unless globally paused — see the toolbar play/pause button), expires abandoned jobs, and flags agents that stopped checking in. Hourly: it prunes old agent resource samples.
  • redis: the Celery broker and result backend.
  • agent: its own image. No database access, no application code. It authenticates with a key from DJANGO_API_KEYS, self-identifies by hostname, runs the scan tools against the host mounted read-only at /host, and only ever sends raw tool output back. All parsing is server-side, in app/scanning/parsers/.
  • ollama: an optional AI-powered LLM endpoint that integrates seamlessly within scan and finding report enabling you to easily “interpret” the results.

web, worker, and beat are the same image (portigniter-reef:YYYY-MM-DD) started with a different role. See Management Commands for the entrypoint table. They share the SQLite database file on a Docker volume; WAL mode plus a 30-second busy timeout keep concurrent readers and writers from tripping “database is locked”.

Why SQLite instead of Postgres

Reef is deliberately lean for v1 — one shared SQLite file over a Docker volume, with WAL mode for concurrent access, is enough for a single-box or small-fleet deployment and keeps the stack to five containers with no separate database service to run or back up. PostgreSQL support is on the Roadmap for when that stops being true.

Where the trust boundary sits

The agent is the only component that touches the host directly, and it’s intentionally kept dumb: it has no database credentials and no Django code, so compromising it doesn’t hand over anything beyond enabling one to “run the bundled scan tools and read /host”. Every interpretation of scan output, deciding what’s a finding, what severity it is, whether it’s a known-exploited CVE etc. happens on web/worker, not on the privileged agent.

Top