Files
home-network-k3s-impl/bootstrap/sops/README.md
T
jakman85andClaude Opus 4.8 84a2d762f0 Phase 1 (author-only): Flux + SOPS bootstrap + interface wiring (T-1.4)
bootstrap/sops (age-keygen refusing in-repo, .sops.yaml, install-age-key.sh
glue installing the sops-age Secret) + bootstrap/flux (bootstrap.sh: flux
install -> apply -k clusters/dev; decryption/url patches). PM wiring: place
.sops.yaml at repo root (SOPS walks to root); inline the 3 flux patches into
clusters/dev (avoids kustomize ../ load-restriction). clusters/dev builds with
decryption enabled; full CI gate PASS. No key material; nothing executed.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 01:21:01 -05:00

2.8 KiB

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).

AUTHOR-ONLY. Nothing here has been executed. No key or secret material exists in this output. No age-keygen, sops, or kubectl was run. The age keypair is generated later by the operator; .sops.yaml carries only a placeholder for the public recipient.

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.
.sops.yaml SOPS creation rules (encrypt only data/stringData → readable diffs). age: recipient is a clearly-marked REPLACE-ME placeholder filled at bootstrap. Effective home is the repo root (see below).
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.

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 (.gitignore guards age.key, *.key, *.dec.yaml).
  • Flux decrypts in-cluster using the age private key held in the sops-age Secret — the one documented out-of-band bootstrap action (docs/07, ADR-0007 Consequences).
  • decryption.provider: sops + secretRef: sops-age on the platform/apps Flux Kustomizations is wired by ../flux/patches/ (realising the commented TODOs in clusters/base).
  • No key material appears in scripts, docs, PRs, or logs (AGENTS.md §5).

Interface / handoff

  • .sops.yaml belongs at the REPOSITORY ROOT. SOPS discovers it by walking up from the file being encrypted, so it only governs platform/** and apps/** from root. It is authored here (in this worker's subtree); the PM/clusters owner installs it at the repo root (copy or symlink). Flagged as an escalation in the T-1.4 report.
  • Fill two placeholders at bootstrap: the age: recipient in .sops.yaml (from age-keygen.sh's output) — same value in both creation rules.
  • Order (see ../flux/README.md): age-keygen.sh → install sops-age Secret (age-key-secret.md) → ../flux/bootstrap.sh. The Flux entrypoint fails fast if the Secret is missing.