Files
Gebos/docs/adr/0003-vendored-supabase-compose-subset.md
T
Lars Nolden 2ce68cb098
deploy / deploy (push) Successful in 5m57s
check / flake-check (push) Failing after 35m52s
Wire schema v1 into a manual Supabase migration and vendor the real stack
- Move db/schema.sql to supabase/migrations/ as the first supabase CLI
  migration (manual `db push` only, no automated runner); re-add the
  gebos_ingest role + grants there since init.sql never re-runs
- Add gebos-postgres-passwords oneshot on db-host: syncs role passwords
  from sops (LoadCredential, journal-safe), makes supabase_admin
  SUPERUSER and hands the auth schema to supabase_auth_admin to match
  the upstream supabase/postgres image; add pg_hba rule for 10.0.0.0/8
- Vendor the official docker-compose (studio/kong/auth/rest/meta only,
  external Postgres, loopback Studio with no Kong dashboard route) plus
  kong.yml (trimmed) and kong-entrypoint.sh (verbatim); tested: compose
  config, Kong config parse, migration applied on TimescaleDB pg17
- Document decisions as ADRs 0001-0004 (migrations, passwords, vendored
  stack, JWT API keys incl. verify/mint procedure)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 22:33:50 +02:00

3.4 KiB

ADR-0003: Vendor a trimmed Supabase compose stack against external Postgres

Date: 2026-07-08 Status: Accepted

Context

The Supabase stack on app-host was a placeholder skeleton. The official self-hosting bundle assumes its own bundled db container and ships nine+ services (realtime, storage, imgproxy, edge functions, supavisor pooler, Logflare analytics, …), most of which the UVI pilot does not use. Meanwhile our Postgres lives on a separate NixOS host (db-host) with TimescaleDB.

Kong deserves a note, since "why is it even there" came up: Kong is the single front door for api.gebos.online (Caddy → Kong on loopback :8000). It routes paths to internal services (/auth/v1 → GoTrue, /rest/v1 → PostgREST) and enforces the perimeter (key-auth + ACLs), so unauthenticated internet traffic never reaches the backends. Replacing it would mean re-implementing routing/auth/CORS in Caddy by hand instead of inheriting Supabase's tested config.

Decision

Vendor the upstream docker/docker-compose.yml (and kong.yml + kong-entrypoint.sh) into nix/supabase/, keeping only studio, kong, auth (GoTrue), rest (PostgREST), meta (postgres-meta), with these deviations — each also documented in the files' header comments:

  1. No db service. Everything connects to ${POSTGRES_HOST}:${POSTGRES_PORT} (db-host); depends_on: db dropped.
  2. No realtime / storage / imgproxy / functions / supavisor / analytics. Re-vendor from upstream when actually needed.
  3. Studio binds to loopback only, and Kong's catch-all dashboard route is removed. Upstream fronts Studio with Kong basic-auth on /; we expose no route to it at all. Access is ssh -L 3000:127.0.0.1:3000 — possession of the SSH key is the credential. supabase_dashboard_password is therefore currently unused. The public API surface is exactly /auth/v1, /rest/v1, /graphql/v1 and two .well-known endpoints.
  4. meta connects as supabase_admin, not postgres. Upstream's postgres role is its cluster superuser; on db-host postgres keeps a separate admin password, so supabase_admin (made SUPERUSER by ADR-0002) fills that role with the shared service password.
  5. kong-entrypoint.sh is vendored byte-identical. Kong's declarative YAML cannot read env vars, so the entrypoint substitutes $VARS (the API keys) into the config at container start; keeping it unmodified makes re-vendoring a plain copy.

Variable layering (the answer to "where does POSTGRES_HOST come from"):

  • non-secret deployment config (POSTGRES_HOST/PORT/DB, STUDIO_BIND) — systemd unit environment in gebos-supabase.nix, fed by module options;
  • secrets (POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY) — sops-rendered EnvironmentFile;
  • gebos constants (public URLs, org name) — hardcoded in the vendored compose.

Consequences

  • Deliberately small attack surface and dependency set; Studio reachable only through SSH.
  • Upstream updates are a re-copy plus re-applying the five listed deviations; all drift is concentrated in docker-compose.yml and kong.yml headers.
  • GoTrue has no SMTP configured yet — magic-link login cannot send email until the SMTP_* secrets are added (TODO marked in the compose file).
  • anon/authenticated still hold zero grants; PostgREST serves nothing until the RLS/grants migration lands (future ADR).