Hand off the secrets vault to mesh-catalog; open issues 069 and 070
Design 24 flips to in-progress with mesh-catalog as its owner (playbook 04). Starting the build surfaced two gaps the decision did not settle: a module requiring `secret` receives exactly one value (069), and no command can accept an operator's value into a consumer↔vault pair (070). Both opened as issues rather than improvised around. Also fills fixed-by on 067 and 068, which the cycle check refused as resolved with no reference.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
---
|
||||
status: open
|
||||
opened: 2026-09-20
|
||||
located-in: [mesh-controller]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# An operator cannot deliver a pair credential, so the vault's third species has no entry
|
||||
|
||||
## Symptom, as observed
|
||||
|
||||
[ADR 0085](../../02-DECISIONS/0085-a-secret-is-a-provision.md) names three species of secret and
|
||||
gives the vault two: a module's own secret, which the mesh mints, and an **operator-delivered**
|
||||
secret — a credential for something outside the mesh, which only a person can supply. The design
|
||||
([24](../../03-DESIGN/01-to-be/24-the-secrets-vault.md)) says the vault *"takes custody of one an
|
||||
operator delivered"*.
|
||||
|
||||
Under 0085 a secret from the vault is a pair credential between the consumer and the vault. The
|
||||
controller has one command that takes a value from a person — `secret accept` — and it writes
|
||||
**only a module's own secret**: one node, one module, one name, sealed to that node's key alone.
|
||||
There is no command that accepts a value *into a pair*: sealed to the consumer's node **and** to
|
||||
the vault's node, recorded as `accepted` so a later push does not replace it with a minted one.
|
||||
|
||||
The primitive exists. The sealing package's `Accept` takes a value and two keys, and the licence
|
||||
adapters already use it to carry an operator's token to both ends of a licence. Nothing exposes it
|
||||
for an ordinary provision.
|
||||
|
||||
So today an operator-delivered secret can be held in only one of two wrong ways: as an own secret
|
||||
(un-audited, un-rotatable — the gap 0085 opened to close), or not at all.
|
||||
|
||||
## Why it matters beyond this instance
|
||||
|
||||
- **The vault is one third short of its decision** until this exists, and the third that is
|
||||
missing is the one with a real leak history — external keys are the secrets that end up in
|
||||
transcripts and env files.
|
||||
- **The `made`/`accepted` distinction is only recorded for own secrets.** A pair credential has no
|
||||
origin column, so even once a value can be accepted into a pair, `rotate` would mint over it —
|
||||
generating "32 random bytes where a working credential was", the exact fault `secret accept`
|
||||
was written to prevent for own secrets. Rotating an accepted pair credential must refuse, or
|
||||
must ask for the replacement value, and neither is designed.
|
||||
- **The consumer's login is derived, and an external service did not derive it.** An accepted
|
||||
external key has no `as` the far side knows; a pair credential assumes both ends agree on one.
|
||||
For the vault this is harmless (the vault creates nothing under that login), but the model
|
||||
leaks through in what the consumer is told.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Is this `secret accept <consumer-node> <module> secret --from …` growing a provider end, or a
|
||||
new verb on the pair?
|
||||
- Does an accepted pair credential refuse `rotate`, or does `rotate` become "accept a new value
|
||||
and deliver it" for that pair?
|
||||
- Should the origin (`made` / `accepted`) become a fact of every pair credential, so the vault's
|
||||
ledger can show which of its secrets a person supplied?
|
||||
Reference in New Issue
Block a user