Skip to content
VerifAIer
Home / Docs / Operations / Deployment & upgrade
Operations · Deployment

Deployment & upgrade

How to deploy the backend to production, where the static surfaces live, what the host must provide, and how to upgrade safely.

Production deployment guide

Run the backend container behind a TLS-terminating reverse proxy. The app is stateless except for its SQLite database (and the in-memory registry), so scaling is "more replicas behind a load balancer", with the datastore caveat below.

Dockerfile · docker-compose.yml · api/routes.py · auth/config.py
flowchart TB
  subgraph EDGE["Edge (your responsibility)"]
    LB["Load balancer / reverse proxy
TLS · rate limit · timeouts · WAF"] end subgraph BACKEND["Backend host(s)"] A1["uvicorn worker(s)
vailidator.api.routes:app :8000"] DB[("SQLite volume (DB_PATH)
or Postgres via DATABASE_URL")] end subgraph STATIC["Static surfaces (separate)"] V["Docs / marketing site → Vercel (demo/)"] NG["Public Proof Verifier → nginx / Cloudflare Pages / GitHub Pages"] end USERS["Clients · extension · SDK · dashboards"] --> LB --> A1 --> DB USERS --> STATIC
Production shape, self-hosted backend behind your edge; static surfaces hosted separately. Grounded in Dockerfile + vercel.json + deployment/.

Required production settings (all via env):

env

VERIFAIER_ENV=production
APP_ENV=production
SESSION_SECRET=<64-hex random>        # python -c "import secrets; print(secrets.token_hex(32))"
AUTH_COOKIE_SECURE=true               # HTTPS only
AUTH_COOKIE_SAMESITE=lax              # or strict
CORS_ORIGINS=["https://app.example.com"]   # restrict from the default ["*"]
DB_PATH=/app/data/vailidator.db       # on a persistent volume
# providers: leave mock, or enable+key+live per ops-config#providers
Hard startup gate. With AUTH_COOKIE_SECURE=true, the app refuses to start if SESSION_SECRET is unset/ephemeral (V-F3). Set the secret before enabling secure cookies. Missing secrets otherwise print [SECURITY] warnings to stderr at startup.
  • Proxy owns the edge: TLS, HSTS, general rate limiting, per-request timeouts and WAF live at your reverse proxy, the app sets response security headers but does not terminate TLS or throttle general traffic.
  • Workers: run multiple uvicorn workers/replicas behind the LB for throughput. Keep one SQLite writer per node, or move to Postgres for shared multi-node state.
  • No managed control plane: there is no VerifAIer-hosted SaaS. You own the deployment lifecycle.

Vercel / static site deployment

The docs + marketing site (demo/) is pure static HTML and deploys to any static host. vercel.json is configured for Vercel:

vercel.json · deployment/verifier_nginx · deployment/verifier_cloudflare_pages · deployment/verifier_github_pages

config

# vercel.json (real): serve demo/ as static
outputDirectory: "demo"
cleanUrls: true, trailingSlash: false
headers: Cache-Control per path + X-Content-Type-Options / X-Frame-Options /
         Referrer-Policy / Permissions-Policy on /(.*)
SurfaceHostBackend?
Docs / marketing site (demo/)Vercel (vercel.json) or any static hostno, static only
Public Proof Verifier (single-file)nginx / Cloudflare Pages / GitHub Pages (deployment/)no, zero backend
Operator & Control Center dashboardsStreamlit containers (Docker)calls the API via API_URL
The FastAPI backend does not run on Vercel. Vercel (and the deployment/ nginx/Cloudflare/GitHub-Pages configs) host static surfaces only. The API must run as a container/uvicorn process on your own infrastructure. The deployment/ nginx and Cloudflare configs specifically serve the zero-backend Public Proof Verifier with a strict CSP.

Backend hosting requirements

RequirementDetail
RuntimePython 3.12, the version CI runs (or the provided python:3.12-slim container). 3.13 and 3.14 are exercised by hand; below 3.12 is refused by requires-python. See Install.
Dependenciesfastapi · uvicorn · anthropic · pydantic · pydantic-settings · jsonschema (see config)
Persistent storagea writable volume for the SQLite file at DB_PATH (or a Postgres instance via DATABASE_URL)
Network egressnone required: outbound only if you enable a live provider (or optional blockchain anchoring)
Ports8000 (API); 8501/8502 if running the dashboards
Process1+ uvicorn workers; front with a reverse proxy for TLS + throttling
HealthGET /health for readiness/liveness
Privilegesruns non-root (appuser); no special capabilities

Upgrade guide

Upgrades are additive by design, the API is versioned under /api/v1 and response bodies only grow (new optional fields). Procedure:

bash

# 1. back up the database first (see ops-resilience#backup)
cp "$DB_PATH" "$DB_PATH.$(date +%Y%m%d).bak"          # or snapshot the volume

# 2a. containerized: pull/rebuild and recreate
git pull && docker compose build && docker compose up -d

# 2b. local install: reinstall the package
git pull && pip install -e ".[dev]"

# 3. smoke test the new version
curl -s http://localhost:8000/health     # confirm it responds
  • Schema migrations: database schema/migrations live under src/vailidator/db/migrations and db/schema; apply per your migration runner before serving traffic.
  • Rollback: keep the previous image tag and the pre-upgrade DB backup; roll back by redeploying the old tag and restoring the file.
  • In-memory state: the trust registry is in-memory and is rebuilt from observations after a restart, expect it to be empty immediately post-upgrade until agents are re-observed.

Run the smoke tests and the release checklist after every upgrade.