# Secrets Managed with [sops-nix](https://github.com/Mic92/sops-nix) + [age](https://github.com/FiloSottile/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/` with declared owner/mode, then renders `gebos-env` from those placeholders for systemd `EnvironmentFile=`. ## First-time bootstrap ```sh # 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_ # 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_ # 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 ```sh sops nix/secrets/secrets.yaml # opens $EDITOR, re-encrypts on save ``` ## Rotation - **Leaked value** → `sops 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`.