Secrets
The vault is running. SOPS is still the path deploys use.Both statements are true as of 2026-07-26, and the distinction matters more than either
one on its own.
Intended model
OpenBao is the runtime source of truth for secrets. SOPS + age is the bootstrap and disaster-recovery backup. Deploy is vault-first with SOPS fallback, so a sealed or absent vault degrades to the encrypted file rather than failing.What is actually true today
OpenBao was reinitialized on 2026-07-26 after being absent since the June 14 rebuild. It is up, healthy, initialized and unsealed; auto-unseal is proven to survive a container restart, including through thehill90-vault-unseal systemd unit that runs at boot.
But the vault holds nothing. vault.sh setup and vault.sh seed have not been run,
so there are no policies, no AppRoles and no KV data. Every deploy therefore still reads
its secrets from SOPS — which is not an inference, it is what the green vault deploy
logged:
KV path map
The paths the intended model uses, once seeded:What SOPS holds regardless
Three categories have no vault equivalent and live in SOPS by design:Admin access is token-only
There is no single sign-on. OIDC through Keycloak was removed when the Keycloak stack was retired alongside the shelved application — thehill90-vault client was its only
remaining consumer, and carrying a full identity provider for one operator was not worth
the surface. This was a deliberate trade. Do not re-add vault.sh setup-oidc without
first re-introducing an identity provider.
Services authenticate by AppRole, never by root token.
Auto-unseal
OpenBao starts sealed after every container restart. Three mechanisms handle it:- Deploy-time —
deploy.sh vaultcallsvault.sh auto-unsealafter compose up. - Boot-time — the
hill90-vault-unsealsystemd oneshot runs afterdocker.service, unsealing within roughly 60 seconds of a reboot. - Manual fallback —
vault.sh unseal.
/opt/hill90/secrets/openbao-unseal.key, mode
0600, owned by deploy.
Schema validation
platform/vault/secrets-schema.yaml is the canonical mapping between vault KV paths,
SOPS keys and compose ${VAR} references. scripts/checks/check_secrets_schema.py runs
in CI on every pull request and fails on drift in either direction — a compose reference
with no schema entry, a schema key missing from the SOPS example, and three other checks.
The open question
Whether the vault earns its place at all is an open decision. The recorded recommendation is that SOPS stays the operative store until the vault has a concrete consumer, and that OpenBao’s file storage backend — deprecated, blocking any move past 2.6.x — sets a deadline on deciding rather than a reason to act immediately. The image tag is pinned, so nothing can break by surprise.Source documents
docs/architecture/secrets-model.md— the full model and current-state notedocs/runbooks/secrets-workflow.md— day-to-day secrets operationsdocs/runbooks/vault-unseal.md— auto-unseal operations and the root-token ordering trapdocs/runbooks/secrets-schema-validation.md— what the validator checksdocs/reference/secrets.md— age key locations and handling