ADR: Secrets Storage Strategy for TheBurrow
Summary
Use Ansible Vault + 1Password as the authoritative secrets store for TheBurrow, with structured lifecycle tooling to enforce rotation, audit trail, and governance. Manual vault edits are a governance violation once tooling is complete.
Status: Accepted Date: 2026-05-30 Implementation: mistborn-ops/ansible#602
Context
No authoritative secrets store exists in TheBurrow. Credentials are
stored in group_vars/all/vault.yml (renamed from all.vault.yml
via mistborn-ops/ansible#564) with no rotation discipline, no audit
trail, and no structured break-glass access pattern. One compromised
vault password exposes every secret in the lab.
Credentials requiring a home:
- NetBox API token
- GitHub API token
- Pi-hole API tokens
- Step-CA provisioner password
- Service account passwords
- SNMP community strings
Options Evaluated
| Criterion | Ansible Vault + 1Password | NetBox Secrets plugin | HashiCorp Vault | Bitwarden Secrets Manager |
|---|---|---|---|---|
| Ansible-native retrieval | ✅ Native | ⚠️ Via API | ✅ hashi_vault lookup | ✅ Via plugin |
| No cleartext at rest | ✅ AES-256 | ✅ Encrypted in DB | ✅ Encrypted | ✅ Encrypted |
| Audit trail | ❌ None (status quo) | ⚠️ NetBox changelog only | ✅ Full audit log | ✅ Access log |
| Break-glass without Ansible | ✅ 1Password stores secrets & vault pass | ❌ Requires NetBox up | ❌ Requires Vault up | ✅ Web UI / CLI |
| Home-lab operational complexity | ✅ Zero new services | ⚠️ NetBox dependency | ❌ High — leases, tokens, separate service | ⚠️ External SaaS dependency |
| Rotation support | ❌ Manual (status quo) | ⚠️ Manual via UI | ✅ Dynamic secrets | ✅ Rotation policies |
Decision
Ansible Vault + 1Password with structured lifecycle tooling (mistborn-ops/ansible#602).
HashiCorp Vault is operationally too heavy for a solo-operated home lab and introduces a hard dependency — if Vault is down, automation is down. NetBox Secrets creates a circular dependency — NetBox must be up to run the playbooks that manage NetBox. Bitwarden Secrets Manager introduces an external SaaS dependency that breaks automation when unreachable.
Ansible Vault is already the runtime store and is working. The real gaps are lack of rotation discipline, no audit trail, and no structured lifecycle management. These are closed by tooling (#602), not by replacing the store.
Architecture
Runtime path
Ansible Vault (group_vars/all/vault.yml) is the authoritative runtime
secret store. All automation reads secrets from vault at run time. No
secret values appear in plain group_vars, issues, PRs, or commit
messages.
Human-readable record and break-glass
1Password holds all secret values in human-readable form. It is the break-glass path when Ansible is unavailable. The vault password itself is stored in 1Password.
Lifecycle management
All secret lifecycle events — add, rotate, delete — are performed via the tooling introduced in mistborn-ops/ansible#602. Manual vault edits are a governance violation once #602 is complete, following the same hardline rule as NetBox UI edits.
Every lifecycle event generates a GitHub issue documenting:
- What secret was affected (by name/role, never by value)
- Why the change was made
- When it occurred
- Who initiated it
Rotation schedule
Rotation cadence enforced by tooling. Minimum triggers:
- Personnel change
- Suspected compromise
- Annual review
Vault structure
Within the single monolithic vault file (group_vars/all/vault.yml),
secrets are organized and namespaced by service domain. This limits
blast radius during any future migration to a more granular structure.
Consequences
Positive
- Zero new services to operate or monitor
- Break-glass always works — 1Password has no Ansible dependency
- Audit trail via GitHub issues is lightweight and durable
- Fits existing governance workflow (issue → branch → PR → merge)
Negative
- No dynamic secret generation — rotation is scheduled, not automatic
- Audit trail is process-dependent until tooling enforces it
- No fine-grained access control — vault password is all-or-nothing
Risks mitigated
- Circular NetBox dependency eliminated
- No new service failure modes introduced
- Break-glass path remains operational independent of lab state
Alternatives Considered
HashiCorp Vault
Rejected — requires a separate service with its own HA, token leases, and renewal automation. If Vault is unavailable, all Ansible automation that reads secrets fails. Operational complexity is disproportionate for a solo-operated home lab.
NetBox Secrets plugin
Rejected — creates a circular dependency. NetBox must be running to retrieve secrets needed to manage NetBox. Breaks the ability to recover NetBox from scratch.
Bitwarden Secrets Manager
Rejected — introduces an external SaaS dependency. If Bitwarden is unreachable, automation breaks. Adds a billing relationship for infrastructure that should be self-contained.
Related
- mistborn-ops/ansible#544 — original decision issue
- mistborn-ops/ansible#564 — vault rename to standard auto-loading path
- mistborn-ops/ansible#602 — lifecycle management tooling implementation