ADR 0084 (extends 0027) — a provision is served by a node-scoped provider the consumer selects, defaulting to co-location; a module may instead carry a private embedded instance that is not a provision. Design: 01-to-be/23-choosing-a-provider. ADR 0085 (extends 0031) — a secret is a provision and the vault is the module that provides it; a module's own local secret becomes an ordinary pair credential that rotates through the existing machinery, while the controller's provisioning-credential mint (0048) is unchanged. Design: 01-to-be/24-the-secrets-vault; doc 13 amended to cross-link the non-pair secret. Issues 067/068 marked resolved with amended-design set. ADR index regenerated; records and index checks pass. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
6.6 KiB
topic, status, date, deciders, reconstructed, extends
| topic | status | date | deciders | reconstructed | extends |
|---|---|---|---|---|---|
| what runs on it | accepted | 2026-09-20 | jochen | false | 02-DECISIONS/0031-the-control-plane-authenticates-nobody.md |
85. A secret is a provision, and the vault is the module that provides it
Context
ADR 0031 decided that identity runs on the mesh, not of it — a module other modules require, rather than a privileged part of the controller. ADR 0078 did the same for the store and the broker: the twelve-module floor has no specialty left in it. Secrets are the exception that survived. No module owns a secret.
Secret handling is smeared across three built-in parts of the runtime:
- the controller mints one credential per consumer↔provider pair and seals it to both node keys (ADR 0048);
- the mesh generates local secrets — "generated secrets are the mesh's, never authored"
(as-is,
06-configuration-and-secrets.md) — for a value a single module needs for its own use; - the synchroniser injects both into a node's generated files.
Nothing is the owner of "a secret" the way the store module is the owner of "a database", and the
cost is recorded rather than hypothetical. The as-is design names the weakness in its own words:
"Rotation is not a mesh operation… there is no mechanism that rotates one and informs everything
holding it." A rotate command has since been built and proven, but it reaches only the
provisioned pairs; a secret a module generates for its own fully-local use — the password of a
version-pinned embedded store (ADR 0084), an internal
token — is minted by the mesh and then has no operation that can remake it. Three species of
secret, and only the first has an owner:
| species | minted by | rotates? |
|---|---|---|
| a provisioned credential (a database login) | controller, sealed to nodes (0048) | yes — rotate, per pair |
| a module's own local secret | the mesh, as a generated value | no owner, no rotation |
| an operator-delivered secret (an external key) | a person, sealed in (secret accept) |
no rotation, no audit |
Considered Options
- Leave it a property of the controller. Rejected: it is the smear above — no owner, local secrets that cannot be rotated, operator secrets that cannot be audited — and it is exactly the specialty 0078 removed for the store and broker, kept here for no reason anyone recorded.
- A dedicated vault built into the foundation, not a module. Rejected: it reintroduces a privileged built-in, the thing 0031 and 0078 went out of their way to remove, and a mesh that wants none would still carry it.
- Fold all minting, the controller's provisioning credentials included, into the vault. Rejected: the controller must mint in order to deliver any provision — the vault's own credential among them — so making the vault mint the credential of its own delivery is the store/broker chicken-and-egg for no gain. The provisioned-pair credential already has an owner and a rotation (13); this record does not disturb it.
- A secret is a provision; the vault is an ordinary module that provides it. Adopted.
Decision
Secret-holding is a module, parallel to identity. A module that needs a secret for its own
use — a local service's password, an internal token, an external key it was handed — requires a
secret provision from a vault provider, exactly as it requires a database from the store. The
vault generates the value (or holds one it was given), and because the credential belongs to the
consumer↔vault pair it rotates, backs up and is audited through the same per-pair machinery
13 already defines. That is what
gives the second and third species the rotation and audit they lack. A module's own secret stops
being a generated value that nothing owns and becomes an ordinary provision with a provider.
The controller's minting of provisioning-pair credentials is unchanged (ADR 0048): it is how every provision, the vault's own included, is delivered. The vault does not mint the mesh's delivery credentials; it provides secrets to modules, and is itself provisioned the ordinary way.
The vault is node-scoped like every provider (ADR 0084): each node its own, selected the same way, so a module's own secret is held by the vault on the module's node. A mesh that wants no vault runs none — a module requiring no secret needs nothing, which is the same test 0031 applied to identity.
Consequences
The gap that opened this — a generated local secret with no rotation — closes without new machinery: rotating such a secret is the vault's provisioner remaking a pair credential, the operation 13 already specifies. Backup, audit and break-glass gain an owner — the vault module — and become things a design specifies rather than absences. The as-is sentence "rotation is not a mesh operation" is already false for provisioned pairs and, once the vault ships, for local secrets too; the as-is document is updated when it does, not before.
What got harder: a break-glass path — recovering a secret when the sealed delivery path is unavailable — must not reintroduce a key that one place holds, which is the property ADR 0048 was built to preserve; how the vault offers recovery without it is left to the design as an open question. And a module that today bakes a password into its own composition must instead require it from the vault — a migration taken module by module, not a flag day.
References
- ADR 0031 — identity is a module; this is the same move for secrets.
- ADR 0048 — the provisioning-credential path, left unchanged.
- ADR 0078 — the de-specialisation this completes.
- ADR 0084 — the node-scoping the vault obeys.
- issue 068 — the gap.
03-DESIGN/01-to-be/24-the-secrets-vault.md— the design. The source mesh'ssecret_locate/secret_backup/secret_verify/secret_breakglasssubsystem is the prior art it draws on.