Hybrid & Federated Deployments

Split-infrastructure (frontend ↔ backend on different platforms) plus federated cross-organisation deployments via the GPS protocol.

This section is intended for: Technical Team, Management. Unauthorised access is restricted.

Audit status: MixedFrontend/backend split: Partial. Federation (GPS/COGN): Implemented. Note: gps/onboard is currently unauthenticated — tracked as a separate task.

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/onboard is 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:

  1. Deploy backend on customer infra (self-hosted.md or docker.md).
  2. Add the Vercel domain to CORS_ALLOWED_ORIGINS on the backend (src/app.ts rejects any other origin with 403 in production).
  3. Set NEXT_PUBLIC_API_URL on Vercel to the customer backend's public URL. The frontend normalises it to end with /api/v1 (frontend/src/services/api.ts).
  4. Set PUBLIC_URL on 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:

  1. Deploy backend exactly as ../deployment-guide.md describes (Railway + Vercel section can be skipped — only the backend is needed).
  2. Add the customer's UI domain to CORS_ALLOWED_ORIGINS on Railway.
  3. Build a Next.js image inside the customer infra with NEXT_PUBLIC_API_URL=https://api.govula.com baked 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 identifier
  • FEDERATION_ORG_NAME — human-readable name
  • FEDERATION_PUBLIC_KEY / FEDERATION_PRIVATE_KEY — the signing keypair
  • (optional) FEDERATION_DISABLED=1 to 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 UIA
Govula backend, customer UIB
Multi-org governance interchangeC (on top of A or B)
No outbound internet at allD

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

Canonical source: docs/deployment/hybrid.md

This page mirrors the markdown deployment hub on disk. The full markdown source includes additional code blocks, command examples, and embedded reference tables.

Hub index: /docs/deployment

You are here · Deploy · step 9
Scalingnext step

Next in Deploy: Scaling.

What should I do next?

Activation Flowprimary

continues in "deploy"

Ranked using IA v1 graph + intent map + glossary density (deterministic; no AI inference).