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>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user