- 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>
Supabase compose stack
Vendored from the official self-hosting bundle
(docker/docker-compose.yml in supabase/supabase), with deliberate changes:
-
No
dbservice. Every service readsPOSTGRES_HOST/POSTGRES_PORTfrom the environment and connects to the externaldb-host. The systemd unit (nix/modules/gebos-supabase.nix) injects these; secrets come from the sops-rendered env file (seegebos-secrets.nix). -
Only auth + rest + studio (+ kong, meta). Realtime, storage, imgproxy, edge functions, supavisor and the Logflare analytics stack are not vendored — re-add from upstream when actually needed.
-
Studio is bound to
127.0.0.1only, with no Kong dashboard route. Upstream protects Studio with Kong basic-auth on a catch-all/route; we drop that route entirely (kong.ymlhere), so there is no public route to Studio and the api.gebos.online surface is only/auth/v1,/rest/v1,/graphql/v1and the two.well-knownendpoints. To use Studio:ssh -L 3000:127.0.0.1:3000 deploy@app.gebos.online open http://localhost:3000Studio itself has no login in this topology — possession of the SSH key is the credential. (
supabase_dashboard_passwordin secrets.yaml is currently unused; it becomes relevant only if Studio ever gets fronted by basic auth again.) -
meta connects as
supabase_admin, notpostgres. In the upstream imagepostgres/supabase_adminis the superuser; on db-host thepostgressuperuser keeps its own password (postgres_admin_password), and thegebos-postgres-passwordsoneshot makessupabase_adminSUPERUSER with the sharedPOSTGRES_PASSWORDto match upstream semantics.
What's in here
docker-compose.yml— the stack itselfinit.sql— Supabase schemas, roles, and extensions Postgres needs before the compose services come up. Runs once at first initdb vianix/modules/gebos-postgres.nix. Application schema lives insupabase/migrations/(applied manually — seesupabase/README.md).kong.yml— Kong's declarative routing, trimmed to the services we runkong-entrypoint.sh— upstream helper, verbatim: substitutes$VARSinto kong.yml (Kong has no native env interpolation) and builds the request-transformer Lua expressions
Passwords
No service role has a password in any SQL file. The
gebos-postgres-passwords oneshot on db-host (see gebos-postgres.nix)
syncs them from sops on every boot/deploy:
| role | secret |
|---|---|
postgres |
postgres_admin_password |
supabase_admin, supabase_auth_admin, authenticator |
supabase_postgres_password (= POSTGRES_PASSWORD in the compose env) |
gebos_ingest |
ingester_postgres_password |
Updating the vendored compose
When upstream ships a new layout, re-vendor by copying the upstream files and
re-applying the deviations listed at the top of docker-compose.yml and
kong.yml (both carry the list in their header comments). Then commit,
deploy, and smoke-test Studio through the SSH tunnel.