ADR-D28: Secrets management¶
- Status:
Accepted
- Date:
2026-05-05
- Decided:
2026-05-06 (user confirmation)
- Phase:
F-OPS
- Gate:
opens at F-OPS entry
Context¶
PostgreSQL credentials, MinIO keys, OIDC client secrets, optional external API tokens and SSH keys cannot live in plaintext in repositories. Multi-target deployment (cloud, HPC, airgap) requires a single mechanism that works across all of them.
Decision (recommended)¶
sops with age keys. Encrypted secrets.enc.yaml committed
in repos; CI decrypts with the age key stored in GitHub Secrets.
Local development uses a developer-specific age key checked into the
user’s keyring.
Consequences¶
Plaintext secrets never on disk persistente.
Per-environment file (
secrets.dev.enc.yaml,secrets.prod.enc.yaml).Rotation procedure documented.
Resolution¶
Accepted as recommended. sops + age confirmed by user
2026-05-06. Two reasons captured: (a) age keys are post-PGP ed25519,
short and revocation-chain-free; (b) sops is file-format agnostic so
the same workflow handles yaml/json/env. First migration target:
secrets.enc.yaml containing DB URL + AMQP URL + MinIO creds + GitHub
release token. Bootstrap script invokes sops -d before
manage.sh start. Per-environment files (secrets.dev.enc.yaml /
secrets.prod.enc.yaml).
Implementation (2026-05-06)¶
Scaffolding committed (does not yet hold any real secrets):
.sops.yamlat the repo root withcreation_rulesforsecrets.devandsecrets.prod. Recipient public keys are placeholders (AGE_RECIPIENT_DEV_PLACEHOLDER/AGE_RECIPIENT_PROD_PLACEHOLDER) until a maintainer generates an age keypair and pastes their public key in.secrets/secrets.dev.example.yamlplaintext template showing the schema (committed). Real values go insecrets/secrets.dev.yaml(gitignored), thensops --encrypt --in-placeproducessecrets/secrets.dev.enc.yamlwhich is what gets committed..gitignoreupdated to forbid plaintextsecrets.{dev,prod}.yamlwhile keeping the encrypted form and the example template tracked.Secrets management (sops + age) runbook covering install, key generation, daily editing, CI integration (
SOPS_AGE_KEYrepo secret), and recipient rotation.
Outstanding (requires the maintainer to act on a local machine):
Install
ageandsops(binaries to~/.local/bin; see the runbook).Run
age-keygen -o ~/.config/sops/age/keys.txtand copy the# public key:line into.sops.yaml.Copy
secrets/secrets.dev.example.yamltosecrets/secrets.dev.yaml, fill in real values, encrypt in place,git mvto.enc.yaml, commit.Add
SOPS_AGE_KEY(entirekeys.txtbody) to GitHub repo secrets so workflows can decrypt.