0026. Keep a single secret zero in the OS keychain; generate all other secrets¶
- Status: Accepted
- Date: 2026-09-26
Context¶
Every secret needs protection, and the protection itself needs a key: the chain ends in one initial secret, the secret zero. The platform already has several secrets (state encryption passphrase, object-store credentials, later tool admin passwords). None may be committed, and hand-managed secrets conflict with "everything is a provisioned state". Background: Secret zero.
Options¶
| Option | Pros | Cons |
|---|---|---|
| Every secret created by hand and stored in the keychain | Simple to understand | Manual work per secret; rotation is tedious and error-prone |
| One secret zero in the keychain; everything else generated by OpenTofu and kept in encrypted state | One manual secret; rotation is tofu apply -replace; no secrets in code |
State encryption becomes critical (it is, see 0025) |
| Secrets manager from day one | Enterprise pattern | Needs infrastructure that does not exist yet (Phase 2) |
Decision¶
- The state encryption passphrase is the only hand-managed secret. It lives in the OS keychain and is passed to OpenTofu by the task wrapper at run time.
- Other secrets (e.g. S3 credentials) are generated by OpenTofu (
random_password), stored in encrypted state and passed between stacks by the task wrapper, never written to disk in plain text. - One passphrase serves all stacks locally; separate keys per environment are a known
simplification, reversible via OpenTofu key rotation (
fallback).
Consequences¶
- Losing the passphrase makes all state unreadable: it must be backed up in a password manager.
- Phase 2 moves secret zero and generated secrets to OpenBao; the chain stays the same.
- Keychain support depends on the host OS; the task wrapper also accepts the passphrase from an environment variable so other hosts and CI can supply it.