bootstrap/sops — SOPS + age key material & decryption wiring (Phase 1, T-1.4)
Generates the age keypair, declares SOPS creation rules, and documents how the age private key is installed into the cluster so Flux can decrypt SOPS at reconcile. Spec: ADR-0007 (SOPS+age), docs/07 · Requirement: R-SEC-01 (secrets encrypted at rest in git, decrypted only in-cluster).
The SOPS creation rules live in the single canonical repo-root
.sops.yaml. The operator generates the age keypair viaage-keygen.sh, pastes the public recipient into that root file, and installs the private key in-cluster as thesops-ageSecret. No private key material ever enters git. (CREDENTIALS.mdis the end-to-end provisioning checklist.)
Files
| File | Purpose |
|---|---|
age-keygen.sh |
Operator-run script: generates the age keypair to a path outside the repo, prints only the public recipient. Refuses to write inside the repo or clobber an existing key. |
age-key-secret.md |
Runbook + kubectl create secret template installing the age private key as the sops-age Secret in flux-system (the sanctioned out-of-band step). Rotation + DR custody included. |
CREDENTIALS.md |
End-to-end operator checklist: SOPS setup + the full secret inventory (per component/phase). |
secret-templates/ |
*.yaml.example scaffolds (correct name/namespace/keys, placeholder values) to copy → fill → sops -e. |
repo-root .sops.yaml |
The single canonical SOPS creation rules (encrypt only data/stringData → readable diffs). At the repo root, NOT here — a copy in this dir would shadow it for files under bootstrap/**. |
How this satisfies R-SEC-01 / ADR-0007
- Secrets are committed encrypted (
sops --encrypt) using the age public recipient in.sops.yaml; plaintext never enters git (.gitignoreguardsage.key,*.key,*.dec.yaml). - Flux decrypts in-cluster using the age private key held in the
sops-ageSecret — the one documented out-of-band bootstrap action (docs/07, ADR-0007 Consequences). decryption.provider: sops+secretRef: sops-ageon theplatform/appsFlux Kustomizations is wired by../flux/patches/(realising the commented TODOs inclusters/base).- No key material appears in scripts, docs, PRs, or logs (AGENTS.md §5).
Interface / handoff
.sops.yamlis a SINGLE file at the REPOSITORY ROOT. SOPS discovers it by walking up from the file being encrypted, so a root file governsplatform/**,apps/**,catalog/**, andsecrets/**. Do not keep a second copy in this directory — it would be discovered first for files underbootstrap/**and shadow the root config (that duplicate was removed after it caused exactly that).- Fill the recipient at bootstrap: paste
age-keygen.sh's public recipient into the root.sops.yaml— the same value in both creation rules. - Order (see
../flux/README.md):age-keygen.sh→ installsops-ageSecret (age-key-secret.md) →../flux/bootstrap.sh. The Flux entrypoint fails fast if the Secret is missing.