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:
@@ -2,7 +2,7 @@
|
||||
status: resolved
|
||||
opened: 2026-09-20
|
||||
located-in: []
|
||||
fixed-by:
|
||||
fixed-by: graduated — hq fc4ab37 (ADR 0084, to-be design 23)
|
||||
amended-design: 03-DESIGN/01-to-be/23-choosing-a-provider.md
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
status: resolved
|
||||
opened: 2026-09-20
|
||||
located-in: []
|
||||
fixed-by:
|
||||
fixed-by: graduated — hq fc4ab37 (ADR 0085, to-be design 24)
|
||||
amended-design: 03-DESIGN/01-to-be/24-the-secrets-vault.md
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
status: open
|
||||
opened: 2026-09-20
|
||||
located-in: []
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# One `secret` provision yields one value, and a module may need several
|
||||
|
||||
## Symptom, as observed
|
||||
|
||||
[ADR 0085](../../02-DECISIONS/0085-a-secret-is-a-provision.md) makes a module's own secret a
|
||||
provision: the module requires `secret` from a vault and reads the pair credential the mesh
|
||||
minted for that consumer↔vault pair. A pair has **exactly one** credential — that is the property
|
||||
[13](../../03-DESIGN/01-to-be/13-credentials-and-their-rotation.md) is built on, and the reason
|
||||
rotation touches one holder and nothing else.
|
||||
|
||||
A module may only require a provision name once, so a module that requires `secret` receives
|
||||
**one value**. Counted across the catalogue at the time of writing, a module's own secrets other
|
||||
than its broker account number as follows:
|
||||
|
||||
| own secrets | modules |
|
||||
|---|---|
|
||||
| one | 22 |
|
||||
| two | 6 (an internal token and an admin password, a user and a password for an outbound mail relay, …) |
|
||||
| three or more | 4 (a source, an admin and a relay password; a root certificate, its key and that key's password; …) |
|
||||
|
||||
Ten modules cannot be migrated onto the vault as designed, because the design gives them one
|
||||
value where they hold two or more, and nothing in the record says what they should do instead.
|
||||
|
||||
## Why it matters beyond this instance
|
||||
|
||||
- **The migration in ADR 0085 is "module by module, not a flag day"** — but for a third of the
|
||||
modules with own secrets there is no target to migrate to, and the gap is silent: such a
|
||||
module keeps `own-secrets` for the rest and looks migrated.
|
||||
- **Two candidate answers pull in different directions and neither is recorded.** A module could
|
||||
derive several keys from its one value (a key-derivation step inside the module, which puts
|
||||
cryptography into every module that needs two secrets), or the mesh could let a consumer
|
||||
require several *named* secrets from one vault (which is a pair with several credentials, the
|
||||
thing 13 deliberately does not have, or several pairs between the same two modules, which the
|
||||
identity derivation in [ADR 0049](../../02-DECISIONS/0049-a-consumers-identity-fits-the-tightest-backend.md)
|
||||
cannot express).
|
||||
- **A secret with structure is not one value either.** A certificate authority's root
|
||||
certificate, key and key password are three things with one lifecycle; a value the controller
|
||||
mints at random is none of them. The vault's third species — an operator-delivered secret — is
|
||||
the nearer fit, and it has its own gap ([070](../070-an-operator-cannot-deliver-a-pair-credential/00-report.md)).
|
||||
|
||||
## Open questions
|
||||
|
||||
- Is "one value per module" a rule to keep, with derivation the module's business, or does a
|
||||
consumer name several secrets and the vault serve each as its own pair?
|
||||
- If the latter: what is the login of the second pair between the same consumer and the same
|
||||
vault, given that a login is derived from the (node, module) pair and is what the secret is
|
||||
keyed on?
|
||||
- Which modules genuinely need several independent secrets, and which hold two names for one
|
||||
thing (an admin password and a token that is only ever set from it)?
|
||||
@@ -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