Deploying zkde.fi
This page covers how the system is actually served — the reverse proxy, the process manager, the docs build pipeline, and the environment gates that control what's allowed to execute.
Runtime topology
flowchart LR U["Users"] --> RP["nginx<br/><i>:443, TLS</i>"] RP -->|"/"| FE["Next.js<br/><i>:3001</i>"] RP -->|"/api/"| BE["FastAPI<br/><i>:8003</i>"] RP -->|"/docs/"| DOCS["Static VitePress"] BE --> OBS["obsqra<br/><i>:8002</i>"] OBS --> MAD["Madara L3<br/><i>:9944</i>"]
Everything runs on a single server. nginx handles TLS and routes requests to the right service. There's no container orchestration — it's PM2 managing Node and Python processes.
| Component | Port | What it is |
|---|---|---|
| Next.js frontend | 3001 | App shell, SSR pages, static assets |
| FastAPI backend | 8003 | API, WebSocket, proof orchestration, receipt lifecycle |
| obsqra backend | 8002 | Identity infrastructure, proof aggregation, GATE services |
| Madara L3 | 9944 | Local appchain for fact registration and L3 settlement |
| VitePress docs | static | Built once, copied into frontend/public/docs/, served by nginx |
nginx routing
/ → localhost:3001 (Next.js)
/api/ → localhost:8003 (FastAPI)
/docs/ → frontend/public/docs/ (static, max-age=300)
/_next/ → .next/static/ (immutable cache)The full nginx config lives at nginx/zkdefi.conf. Key behaviors:
/docs/is served as static files with 5-minute cache headers/_next/static/is served with immutable cache headers (these are content-hashed)/api/is proxied with WebSocket upgrade support for the/ws/path- Everything else falls through to Next.js
Docs build pipeline
After editing any docs page, rebuild and sync:
./scripts/sync-docs.shThis builds VitePress from docs-site/ and copies the output into frontend/public/docs/. From there, nginx serves it as static content. You don't need to restart the frontend or backend — just run the script.
The build preserves existing chunks during rebuild to avoid 404s on cached asset URLs. If you see chunk 404s after a deploy, it means someone ran a clean build that removed old hashes. Hard refresh (Ctrl+Shift+R) fixes it on the client side.
Process management
pm2 start ecosystem.config.js # Start everything
pm2 status # Check what's running
pm2 restart zkdefi-frontend # Restart just the frontend
pm2 logs zkdefi-backend # Tail backend logsThe PM2 config at ecosystem.config.js defines the process list. The frontend and backend are separate PM2 processes with their own log files.
Environment gates
The system uses environment variables to control what's allowed to execute. The most important one:
EXECUTION_GATE_ALLOW_MAINNET_LIVE=falseWhen this is false (the default), the system will not submit live capital transactions. Every execution path checks this gate. It's the master switch between testnet mode and mainnet mode.
Other key variables:
| Variable | What it controls |
|---|---|
STARKNET_RPC_URL | Starknet node endpoint (currently Sepolia) |
MADARA_RPC_URL | Madara L3 node endpoint |
DATABASE_URL | Backend database connection |
EZKL_PATH | Path to the EZKL binary for proof generation |
Full list: docs/ENV.md in the repo.
References
docs/ENV.md— complete environment variable referencedocs/OPS_NGINX_ZKDEFI.md— detailed nginx configuration guidedocs/ARCHITECTURE.md— internal architecture documentationnginx/zkdefi.conf— the actual nginx config file