Skip to content

Infrastructure

Status: Operational reference.

Cross-references: DEPLOYMENT.md (general deployment), WORKER_DEPLOYMENT.md (worker-specific details), SECURITY.md (secret management).

  • Runtime: Cloudflare Workers (JavaScript / Container binding)
  • Database: Supabase PostgreSQL
  • CI/CD: Forgejo Actions
  • Deployment target: gondolier.dev (Workers Dev + custom domains)

All sensitive values are stored in OpenBao under the KV v2 path secret/data/gondolier/backend and are fetched keylessly via Forgejo Actions OIDC (JWT-based authentication to an OpenBao forgejo-actions auth role). Tokens are never committed, never printed to logs, and are masked with ::add-mask:: before being exported to the workflow environment.

Key Purpose Generation
CLOUDFLARE_API_TOKEN Wrangler authentication (Cloudflare API access) Admin-provided
CREDENTIAL_MASTER_KEY Envelope encryption master key for tenant forge tokens openssl rand -base64 32
CONTAINER_AUTH_SECRET Auth token for LeaseDO → ShuntContainer internal dispatch openssl rand -hex 32
SUPABASE_URL Supabase project URL (e.g. https://xxx.supabase.co) Admin-provided
SUPABASE_SERVICE_ROLE_KEY Supabase service role API key Admin-provided
STUB_JWT_SECRET JWT signing secret (HS256) — replace before production auth openssl rand -hex 32
STUB_CLIENT_ID OAuth client ID (stub) — replace before production auth openssl rand -hex 16

The deploy workflow enforces three required keys — it will fail if any are missing from OpenBao after the ensure step:

  • CLOUDFLARE_API_TOKEN
  • CREDENTIAL_MASTER_KEY
  • CONTAINER_AUTH_SECRET

The remaining keys (SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, STUB_JWT_SECRET, STUB_CLIENT_ID) are read from OpenBao and exported to the workflow environment but are not enforced as hard-required at the script level. The ensure step generates missing STUB_JWT_SECRET and STUB_CLIENT_ID automatically. If SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY are absent they must be provided by an admin (the ensure step refuses to generate them).

The deploy job authenticates to OpenBao using a Forgejo Actions OIDC token. The role name defaults to gondolier-ci and can be overridden via the bao_role workflow_dispatch input. The role must be configured in OpenBao with:

  • forgejo-actions auth method enabled
  • A role named gondolier-ci (or custom name) with bound_audiences set to openbao
  • read permission on secret/data/gondolier/backend
  • write permission on secret/data/gondolier/backend (for the self-heal step)

Secrets must be provisioned in OpenBao before the first deploy:

  1. Connect to the OpenBao UI or CLI.
  2. Navigate to (or create) the path secret/data/gondolier/backend.
  3. Create the KV v2 secret with all required keys and their values.
  4. Verify the keys are visible in the OpenBao secret version history.

The deploy workflow includes an ensure step that will auto-generate any missing optional keys on the first run. Required keys that are absent after the ensure step will cause the job to fail with a clear error listing the missing keys.

If you want wrangler to store secrets in the Cloudflare account (not just as workflow env vars), run once after provisioning the workflow env secrets:

Terminal window
wrangler secret put CREDENTIAL_MASTER_KEY
wrangler secret put CONTAINER_AUTH_SECRET
wrangler secret put SUPABASE_URL
wrangler secret put SUPABASE_SERVICE_ROLE_KEY
wrangler secret put STUB_JWT_SECRET
wrangler secret put STUB_CLIENT_ID

This is optional — the CI/CD deploy path uses workflow env vars, not account-level secrets.

Triggers: push to main, pull_request to main.

Jobs (in execution order):

  1. vetgo vet on all Go packages
  2. testgo test -race on unit tests
  3. buildgo build on all targets
  4. workers-checkwrangler deploy --dry-run (validates JS + Container binding)
  5. merge-queue-gate — re-runs vet + test + build on staging ref (shunt MQ gate)

All jobs run on the laputacloudco runner.

Triggers: push to main (post-merge, after shunt queues and merges), or manual workflow_dispatch.

Jobs:

  1. validate-go — same vet, test, build gates as CI
  2. validate-workerswrangler deploy --dry-run with Docker CLI
  3. deploy — OIDC login → fetch secrets from OpenBao secret/data/gondolier/backend → self-heal missing keys → wrangler login + wrangler deploy to production

The deploy job has an if guard: it runs only on push to main or when a manual dispatch targets main specifically. It does not run on pull_request events.

The deploy job includes:

  • enable-openid-connect: true (via permissions: id-token: write)
  • A secret-fetch step that authenticates to OpenBao keylessly using the Forgejo Actions OIDC JWT and the gondolier-ci role.
  • An ensure/self-heal step that generates missing optional keys and writes them back to OpenBao if the role has write permission.
  • Required-key validation: CLOUDFLARE_API_TOKEN, CREDENTIAL_MASTER_KEY, and CONTAINER_AUTH_SECRET must be present or the job fails.

No IaC tooling yet. Provisioning is manual:

  • Cloudflare Worker created via wrangler init / wrangler deploy
  • KV namespace created via wrangler kv:namespace create
  • Supabase project with tables created via migrations
  • DNS configured in Cloudflare dashboard

All Forgejo Actions workflows run on the laputacloudco runner, which is a DinD-capable Linux host. Docker CLI is installed at runtime (pinned version 27.5.1 with SHA256 verification) for the Workers Container binding.

The runner must also provide:

  • python3 — JSON parsing for fetch-valid-json.sh and secret scripts
  • openssl — key generation for CREDENTIAL_MASTER_KEY, CONTAINER_AUTH_SECRET, STUB_JWT_SECRET, STUB_CLIENT_ID
Terminal window
wrangler versions list
wrangler versions rollback <version>
Terminal window
wrangler tail

After deploy, check the Workers dashboard: https://dash.cloudflare.com → Workers → gondolier → Versions.

Or CLI:

Terminal window
wrangler versions list --limit=3
  1. Generate new secret value.
  2. Update the Forgejo Actions secret.
  3. Update the Cloudflare account secret (if using account secrets): wrangler secret put NEW_NAME
  4. Redeploy: merge a trivial commit or trigger workflow_dispatch.