Split-infrastructure deployments where the frontend and backend live on different platforms, plus federated cross-organisation deployments via Govula's GPS protocol.
Status:
- Frontend ↔ backend split: Partial — works because the frontend talks to a single base URL (
NEXT_PUBLIC_API_URL); no infra-specific glue is required. - Federated cross-org (GPS / COGN): Implemented — see
src/services/federationService.ts,src/routes/gps.ts. Note:POST /api/v1/gps/onboardis currently unauthenticated by bug (audit finding F-A1,../audits/phase1-readiness-audit.md§3); a follow-up task tracks lockdown.
Pattern A — Frontend on Vercel + Backend on customer infra
┌──────────────────┐ CORS-allowed HTTPS ┌────────────────────┐
│ Vercel │ ─────────────────────────► │ Customer VPC │
│ Next.js build │ │ Express + Postgres│
│ NEXT_PUBLIC_ │ │ CORS_ALLOWED_ │
│ API_URL=https://│ │ ORIGINS includes │
│ api.customer. │ │ vercel domain │
│ example │ └────────────────────┘
└──────────────────┘
Use when: customer requires data residency / control of the backend, but accepts a Vercel-hosted UI.
Steps:
- Deploy backend on customer infra (
self-hosted.mdordocker.md). - Add the Vercel domain to
CORS_ALLOWED_ORIGINSon the backend (src/app.tsrejects any other origin with 403 in production). - Set
NEXT_PUBLIC_API_URLon Vercel to the customer backend's public URL. The frontend normalises it to end with/api/v1(frontend/src/services/api.ts). - Set
PUBLIC_URLon the backend to the Vercel domain so outbound replay links resolve.
Pattern B — Frontend on customer infra + Backend on Railway
┌────────────────────┐ CORS-allowed HTTPS ┌──────────────────┐
│ Customer infra │ ─────────────────────────► │ Railway │
│ Next.js (docker) │ │ Express service │
│ NEXT_PUBLIC_API_ │ │ Neon Postgres │
│ URL=https:// │ └──────────────────┘
│ api.govula.com │
└────────────────────┘
Use when: customer accepts the official Govula API as the system of record but wants the UI inside their domain (white-label / corporate intranet).
Steps:
- Deploy backend exactly as
../deployment-guide.mddescribes (Railway + Vercel section can be skipped — only the backend is needed). - Add the customer's UI domain to
CORS_ALLOWED_ORIGINSon Railway. - Build a Next.js image inside the customer infra with
NEXT_PUBLIC_API_URL=https://api.govula.combaked in at build time.
Pattern C — Federated cross-organisation (GPS / COGN)
This is not a deployment-topology choice; it's an additional capability layered on any of the above. Multiple Govula instances exchange signed governance signals, policies, and verification envelopes.
┌─ Govula instance @ Org A ──┐ ┌─ Govula instance @ Org B ──┐
│ src/routes/gps.ts │ │ src/routes/gps.ts │
│ Ed25519 keypair (env) │ ↔ │ Ed25519 keypair (env) │
│ POST /gps/onboard (peer) │ │ POST /gps/onboard (peer) │
│ POST /gps/signals │ │ POST /gps/signals │
│ POST /gps/verify (public) │ │ POST /gps/verify (public) │
└─────────────────────────────┘ └─────────────────────────────┘
Wire-protocol details — see src/services/federationCryptoService.ts for the Ed25519 envelope format and src/migrations/032_federation.sql for the persisted-state shape.
Required env on each instance:
FEDERATION_NODE_ID— globally-unique identifierFEDERATION_ORG_NAME— human-readable nameFEDERATION_PUBLIC_KEY/FEDERATION_PRIVATE_KEY— the signing keypair- (optional)
FEDERATION_DISABLED=1to opt out
Pattern D — Air-gapped + observability-degraded
┌─────────────────────────────────────────────┐
│ Customer VPC (no outbound internet) │
│ │
│ Backend (docker) │
│ Frontend (docker) │
│ Postgres (docker or managed) │
│ │
│ SENTRY_DSN unset ──► Sentry skipped │
│ RAILWAY_API_TOKEN unset ─► adapter degraded│
│ RESEND_API_KEY unset ──► transport opt-out
└─────────────────────────────────────────────┘
Govula degrades gracefully: every observability adapter and the email transport check their token at boot; missing tokens emit a structured warn (src/config/validateEnv.ts) but never block boot. See observability-setup.md for the per-adapter degradation table.
Decision summary
| You need… | Pattern |
|---|---|
| Customer backend, our hosted UI | A |
| Govula backend, customer UI | B |
| Multi-org governance interchange | C (on top of A or B) |
| No outbound internet at all | D |
Rollback
Each side rolls back independently — see ../rollback-plan.md. Federation rollback is bidirectional: peers continue to operate; signal queues drain on reconnect.
Where to read more
../deployment-guide.md— official Railway+Vercel canonicalself-hosted.md/docker.md— customer-infra optionssecrets-management.md— federation keypair handling- In-app:
/docs/deployment/hybrid