Store all production secrets encrypted in secrets.age (committed to the
repo) using age. Only one secret needs to be in CI: SECRETS_AGE_KEY.
When a secret changes locally, update secrets.env and re-run
scripts/secrets-encrypt.sh to commit a new secrets.age. CI picks up the
updated secrets automatically on the next push — no manual CI variable
updates required.
Changes:
- scripts/secrets-encrypt.sh: encrypt secrets.env → secrets.age
- scripts/secrets-decrypt.sh: decrypt secrets.age → GITHUB_ENV (CI) or
eval-safe export block (local)
- scripts/test_secrets.sh: encrypt/decrypt round-trip tests
- secrets.env.example: template documenting all production secret keys
- ci/main.go: add CheckSecrets function (runs test_secrets.sh via Dagger),
wire into Check(), update Graph(); add age to toolchain apt packages
- .forgejo/Dockerfile: add age package
- .forgejo/workflows/deploy.yml: replace per-secret CI references with a
single "Decrypt production secrets" step using SECRETS_AGE_KEY
- flake.nix: add age to dev shell
- Taskfile.yml: add check-secrets task, include in check-fast
- .gitignore: ignore plaintext secrets.env
- DAGGER.md: document Option 5 (encrypted secrets file) as active approach
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>