Operate
Self-hosting
Run the gateway, the control-plane API and the dashboard on your own infrastructure, with Postgres, real authentication and your own signing key.
When to use this
When more than one person or process needs the same AgentFox: a dashboard for the team, the gateway for agents in other languages, approvals decided by someone other than the developer. For one developer on a laptop, the local SQLite database from Install and configure is enough.
There is no licence check, no phone-home and no default egress. A fresh deployment runs with AGENTFOX_ALLOW_EGRESS=false and the offline echo provider, so it works end to end with no model and no key.
Three ways to run it
| Option | What runs | Use when |
|---|---|---|
| Python app | The gateway under uvicorn, your database | You already run Python services and want no containers. |
| Docker Compose | Postgres, gateway, dashboard, optional OPA | One machine, everything included. |
| Render | Postgres, gateway, dashboard from render.yaml | A managed host, from the repository's blueprint. |
Settings every deployment must change
| Setting | Why |
|---|---|
AGENTFOX_ENVIRONMENT=production | With auth_mode left at auto, this makes the API require tokens and refuse the development identity header. |
AGENTFOX_AUDIT_SIGNING_KEY | Signs the audit chain's checkpoints. The default is a published string, so the gateway refuses to start outside development without this. Keep it outside the application database; rotating it ends verification of checkpoints signed before. |
AGENTFOX_DATABASE_URL | Postgres for anything with more than one worker. SQLite is refused for a multi-worker server. |
AGENTFOX_SERVICE_AUTH_SECRET | Required outside development, even without GitHub sign-in: the default is published, and it authenticates the call that mints an owner token, so the gateway refuses to start on it. Identical on the gateway and the dashboard. |
AGENTFOX_TOKEN_ENCRYPTION_KEY | Only for connecting GitHub repositories. Without it that feature fails closed. |
Generate the two secrets:
python3 -c "import secrets; print(secrets.token_urlsafe(48))"
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Every setting is on Configuration.
The gateway as a Python app
Install with the Postgres extra
bash pip install "agentfox[postgres]" export AGENTFOX_DATABASE_URL="postgresql+psycopg://agentfox:PASSWORD@db.internal:5432/agentfox" export AGENTFOX_ENVIRONMENT=production export AGENTFOX_AUDIT_SIGNING_KEY="…your generated value…" export AGENTFOX_SERVICE_AUTH_SECRET="…another generated value…"Create the schema and load the catalog
bash agentfox initCreates and migrates the schema and loads the controls and packs. Run it once per database; it is idempotent.
Start it
bash uvicorn agentfox.apps.gateway.app:app --host 0.0.0.0 --port 8080Or
agentfox serve --host 0.0.0.0 --port 8080, which runs the same app. Check it:bash curl -s http://localhost:8080/api/healthOutput {"status":"ok","version":"0.3.1","governance_healthy":true,"degradation":{"healthy":true,"degraded_controls":{},"note":"healthy means every control ran, not that requests succeeded — a fail-open system reports success while checking nothing","fail_mode":"open",…}}/healthanswers the same.governance_healthyis about the controls, not the HTTP server: a degraded detector shows up there while requests still succeed.Create an operator and a token
The API under
/apirequires a token in production. A token is minted for an operator that already exists, and a database created byinithas none. Create the first one; no demo data is loaded:bash agentfox admin users create ops@example.com --role ownerOutput created ops@example.com · owner · org org_default Next: agentfox admin auth issue ops@example.combash agentfox admin auth issue ops@example.com --name dashboardOutput ╭─ Token issued — copy it now ─────────────────────────────────────────────────────────────────────╮ │ nom_api_… │ │ │ │ dashboard · ops@example.com · owner · org org_default │ │ expires 2027-10-05T15:06:06.434557+00:00 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ Only a hash is stored. There is no way to show this value again — issue a new token if it is lost. …Against a production gateway, the header is refused and the token works:
bash curl -s -o /dev/null -w "%{http_code}\n" -H "X-AgentFox-User: admin@example.com" http://localhost:8080/api/findings curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $AGENTFOX_TOKEN" http://localhost:8080/api/findingsOutput 401 200bash agentfox admin auth statusOutput ╭─ Authentication: enforced ───────────────────────────────────────────────────────────────────────╮ │ API tokens required. │ │ │ │ environment = production · auth_mode = auto │ │ The development identity header is refused. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯Sign in to the dashboard with the same token: on
/login, open "Self-hosted? Sign in with an API token". Signing out revokes it.The
/v1/guard/*routes and the model proxy read no operator credential; they identify the agent, and an agent key that does not verify is a 401. Keep the gateway on a private network.
To keep it running across reboots on Linux, the systemd guide runs the gateway as a user service, with schema upgrades as a separate one-shot unit.
Docker Compose
git clone https://github.com/architsharm/agentfox.git && cd agentfox
export AGENTFOX_SERVICE_AUTH_SECRET="$(openssl rand -hex 32)"
export AGENTFOX_AUDIT_SIGNING_KEY="$(openssl rand -hex 32)" # keep a copy
docker compose -f deploy/docker-compose.yml up -dBoth secrets are required: compose stops with an error naming the missing one, and the gateway would refuse to start on a published value anyway.
Four services: db (Postgres 16), gateway on port 8080, dashboard on port 3000, and opa on 8181, which is optional (AGENTFOX_POLICY_ENGINE stays native unless you change it). Compose pulls ghcr.io/architsharm/agentfox/gateway:latest and …/dashboard:latest. docker compose -f deploy/docker-compose.yml build builds from source instead; the gateway build downloads 1–2GB of permissive-licence detector weights into the image so the running container never fetches them.
What the compose file sets on the gateway, and what to change:
| Variable | Compose value | Note |
|---|---|---|
AGENTFOX_DATABASE_URL | postgresql+psycopg://agentfox:agentfox@db:5432/agentfox | Change the password here and on db. |
AGENTFOX_ENVIRONMENT | production | Tokens required (see below). |
AGENTFOX_ALLOW_EGRESS | "false" | Set true, plus a key, to use a hosted model. |
AGENTFOX_ENABLED_DETECTORS | the five defaults plus injection.classifier, safety.granite, injection.similarity | The image carries their weights. |
AGENTFOX_ENFORCEMENT_BUDGET_MS | "100" | Lower than the 300 default; with the classifiers on, long inputs can exceed it and fail open. |
AGENTFOX_AUDIT_SIGNING_KEY | from your shell (required) | Keep a copy outside the database. |
AGENTFOX_SERVICE_AUTH_SECRET | from your shell (required) | Also given to the dashboard. |
AGENTFOX_EVIDENCE_DIR | /var/agentfox/evidence | On the evidence volume. |
The gateway loads no demo data. Create the first operator and a token in the running container, then sign in on http://localhost:3000/login under "Self-hosted? Sign in with an API token":
docker compose -f deploy/docker-compose.yml exec gateway agentfox admin users create you@example.com --role owner --tokenGitHub sign-in is optional: create an OAuth app with the callback http://localhost:3000/api/auth/github/callback and export GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET before up. Demo data only if you want it: docker compose -f deploy/docker-compose.yml exec gateway agentfox admin seed.
The licence-gated Llama Guard tier
safety.restricted (Llama Guard 3-8B) carries a non-OSI licence. It is never in the published image and is off by default. To opt in, after your own licence review:
- accept Meta's licence for
meta-llama/Llama-Guard-3-8Bon Hugging Face; - build the image yourself with that account's token, which bakes the weights in:
export HF_TOKEN=hf_…thendocker compose -f deploy/docker-compose.yml build gateway(the token is a build secret and does not end up in an image layer); - add
"safety.restricted"toAGENTFOX_ENABLED_DETECTORS; - set
AGENTFOX_ACCEPT_RESTRICTED_MODEL_LICENSESto"1".
Without the token the build skips that tier and everything else still builds.
Render
render.yaml at the repository root is a blueprint for a complete installation: agentfox-db (Postgres, free plan), agentfox-gateway (Docker, starter plan, because the image installs the classifier extra), and agentfox-dashboard (free plan). In Render: New, Blueprint, pick your fork. It prompts for GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET. (deploy/render.yaml is a different file that deploys only a dashboard against a hosted API; it is not a self-host.)
Then set the GitHub OAuth app's callback to https://<your-dashboard-host>/api/auth/github/callback, exactly, including the scheme. The blueprint asks for AGENTFOX_AUDIT_SIGNING_KEY once, on creation: paste a random value and keep a copy. To change it later, keep the old value as AGENTFOX_AUDIT_SIGNING_KEY_PREVIOUS and run agentfox admin keys rotate (docs/deployment/key-rotation.md). The dashboard runbook, including Fly.io, is deploy/README-dashboard.md.
Upgrades
pip install --upgrade "agentfox[postgres]"
agentfox admin db upgrade
agentfox admin db current…
migrated b8d3f6a2c915 → b8d3f6a2c915
schema revision: b8d3f6a2c915Apply migrations before the new code serves traffic. The container's start command seeds but does not migrate an existing database, so for Compose and Render run agentfox admin db upgrade in the gateway container after pulling a new image. Take a database backup before any upgrade; admin db downgrade can drop columns.
After it is up
agentfox doctor
agentfox admin auth status
agentfox report verifyRun these with the deployment's environment. doctor should show ✓ authentication API tokens required; the identity header is refused. Point agents at it as described in Any language: the gateway, and Python agents with agentfox.auto() at the same database.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
401 on every /api call | Production mode refuses the header. Use a token from agentfox admin auth issue. |
unknown user when issuing a token | No operator exists yet. agentfox admin users create EMAIL --role owner, then issue the token. |
| GitHub sign-in returns 503 or fails at provisioning | GITHUB_CLIENT_* unset, or service_auth_secret differs between gateway and dashboard. Token sign-in on /login works without either. |
| The playground shows "Failed to fetch" | The browser origin is missing from AGENTFOX_PLAYGROUND_CORS_ORIGIN (comma-separated). |
Postgres: cannot use a string pattern on a bytes-like object | The database is SQL_ASCII. Create it as UTF-8. |
Limits
- One organisation per deployment, enforced at the database session. No SSO or OIDC yet.
- The fail-open budget and rate limits are per process, so N workers get N times the declared budget. Scale-out under load is untested.
- Background jobs run in process; there is no external queue.
More on Limits.