Files
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
..
2026-07-02 15:19:59 +02:00

Secrets

Managed with sops-nix + age.

Layout

  • .sops.yaml (repo root) — recipients and per-file creation rules.
  • nix/secrets/secrets.yaml.example — plaintext template (committed).
  • nix/secrets/secrets.yaml — sops-encrypted real values (committed once created).

Plaintext values never enter the Nix store or disk outside tmpfs. sops-nix decrypts on activation into /run/secrets/<name> with declared owner/mode, then renders gebos-env from those placeholders for systemd EnvironmentFile=.

First-time bootstrap

# 1. Generate your personal age key (keep this private, NEVER commit).
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
# → "Public key: age1abc..."  ← paste this into .sops.yaml as &dev_<you>

# 2. Get each host's age pubkey (one per host).
#    On each host as root:
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
# → "age1xyz..."  ← paste into .sops.yaml as &host_<name>

# 3. Edit .sops.yaml — replace every "age1TODO_..." with the real pubkeys above.

# 4. Create the real secrets file from the template, fill in real values, encrypt.
cp nix/secrets/secrets.yaml.example nix/secrets/secrets.yaml
$EDITOR nix/secrets/secrets.yaml
sops -e -i nix/secrets/secrets.yaml

# 5. Commit .sops.yaml + the encrypted secrets.yaml.
git add .sops.yaml nix/secrets/secrets.yaml
git commit -m "secrets: initial sops bootstrap"

Editing later

sops nix/secrets/secrets.yaml   # opens $EDITOR, re-encrypts on save

Rotation

  • Leaked valuesops nix/secrets/secrets.yaml, change value, redeploy.
  • New host → add its age pubkey to .sops.yaml, then sops updatekeys nix/secrets/secrets.yaml.
  • Compromised dev key → remove from .sops.yaml, sops updatekeys, redeploy.

Generating value material

Field How
supabase_postgres_password openssl rand -hex 32
supabase_jwt_secret openssl rand -hex 32 (must be ≥32 chars)
supabase_anon_key JWT signed with jwt_secret, role: anon, long expiry
supabase_service_role_key JWT signed with jwt_secret, role: service_role
supabase_dashboard_password openssl rand -hex 16 (Studio basic-auth, username gebos)
ingester_postgres_password openssl rand -hex 32
postgres_admin_password openssl rand -hex 32

Self-hosted Supabase docs have a one-liner for signing the two JWTs offline.

Local development

You do not need to bootstrap secrets for nix run .#dev. The dev process-compose stack and the ingester binary both default to local-only values (GEBOS_POSTGRES_URL, GEBOS_MQTT_BROKER, GEBOS_POSTGRES_PASSWORD, plus the JWT/anon-key pair the frontend needs). See nix/dev/process-compose.nix.