vps cluster docs

0002. SOPS + age, decrypted by ksops in ArgoCD’s repo-server

Context

Secrets have to live next to the manifests that use them, or a rebuild from git produces a cluster that cannot start. Before this decision, that meant ~35 plaintext Secret manifests and several .env files in git, including a Hetzner API token that could delete the server, a GitHub token with write access to two organisations, and a Cloudflare Origin CA private key. Deleting a file does not remove it from history, so the values are public to anyone who ever cloned the repo — and the cluster is the thing being protected.

A second requirement rules out several convenient designs: ArgoCD must be able to sync unattended, including after a node rebuild, with no human present to unlock anything.

Decision

SOPS with age recipients is the only secret mechanism, and ksops is how ArgoCD decrypts it.

Alternatives rejected

Consequences

Evidence

.sops.yaml (creation rules), k8s/platform/argocd/values.yaml (the repo-server ksops initContainer, pinned by digest), k8s/platform/argocd/secret/, scripts/check-secrets.sh, ansible/ansible.cfg (vars plugin), and docs/secrets.md.