From 187389b7d1b8d5e3f7b8966fa12f5c4cb3f654b4 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 20 Sep 2026 23:08:28 +0200 Subject: [PATCH] Hand off the secrets vault to mesh-catalog; open issues 069 and 070 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- 03-DESIGN/01-to-be/24-the-secrets-vault.md | 4 +- .../00-report.md | 2 +- .../00-report.md | 2 +- .../00-report.md | 57 +++++++++++++++++++ .../00-report.md | 54 ++++++++++++++++++ 5 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 04-ISSUES/069-one-secret-provision-yields-one-value/00-report.md create mode 100644 04-ISSUES/070-an-operator-cannot-deliver-a-pair-credential/00-report.md diff --git a/03-DESIGN/01-to-be/24-the-secrets-vault.md b/03-DESIGN/01-to-be/24-the-secrets-vault.md index 020ac8c..082fed3 100644 --- a/03-DESIGN/01-to-be/24-the-secrets-vault.md +++ b/03-DESIGN/01-to-be/24-the-secrets-vault.md @@ -1,7 +1,7 @@ --- layer: to-be -status: designed -code: [] +status: in-progress +code: [mesh-catalog] updated: 2026-09-20 decisions: - 02-DECISIONS/0085-a-secret-is-a-provision.md diff --git a/04-ISSUES/067-a-provision-cannot-name-which-provider-serves-it/00-report.md b/04-ISSUES/067-a-provision-cannot-name-which-provider-serves-it/00-report.md index 411feae..749c294 100644 --- a/04-ISSUES/067-a-provision-cannot-name-which-provider-serves-it/00-report.md +++ b/04-ISSUES/067-a-provision-cannot-name-which-provider-serves-it/00-report.md @@ -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 --- diff --git a/04-ISSUES/068-secrets-have-no-owning-module/00-report.md b/04-ISSUES/068-secrets-have-no-owning-module/00-report.md index 6c63b96..2e2ecbc 100644 --- a/04-ISSUES/068-secrets-have-no-owning-module/00-report.md +++ b/04-ISSUES/068-secrets-have-no-owning-module/00-report.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 --- diff --git a/04-ISSUES/069-one-secret-provision-yields-one-value/00-report.md b/04-ISSUES/069-one-secret-provision-yields-one-value/00-report.md new file mode 100644 index 0000000..047d445 --- /dev/null +++ b/04-ISSUES/069-one-secret-provision-yields-one-value/00-report.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)? diff --git a/04-ISSUES/070-an-operator-cannot-deliver-a-pair-credential/00-report.md b/04-ISSUES/070-an-operator-cannot-deliver-a-pair-credential/00-report.md new file mode 100644 index 0000000..5a2b7a2 --- /dev/null +++ b/04-ISSUES/070-an-operator-cannot-deliver-a-pair-credential/00-report.md @@ -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 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?