Multiple fixes: ADRs 0094–0097; issues 069, 049, 046 resolved; 064's image half decided #67

Merged
jschoubben merged 5 commits from multiple-fixes into main 2026-09-21 19:05:13 +00:00
5 changed files with 78 additions and 8 deletions
Showing only changes of commit acc9824949 - Show all commits
@@ -0,0 +1,63 @@
---
topic: the tiers
status: accepted
date: 2026-09-21
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0085-a-secret-is-a-provision.md
---
# 94. A module may hold several secrets from one provider, each a pair of its own
## Context
[ADR 0085](0085-a-secret-is-a-provision.md) makes a module's own secret a provision: the module
requires `secret` from the vault and reads the pair credential minted for that consumer↔vault
pair. A pair has one credential, a module requires a provision once, and so a module received
one value. Read against the catalogue, nine modules hold two or more secrets besides their
broker account; seven of them hold genuinely independent values with independent lifetimes — a
root certificate, its key and that key's password; an admin password beside an API token. None
derives from another, so "one value, derivation the module's business" answers nothing
([issue 069](../04-ISSUES/069-one-secret-provision-yields-one-value/00-report.md)).
## Considered Options
1. **One value per module; the module derives the rest.** Rejected: the values are independent.
2. **Require the provision several times.** Rejected: `requires` is a list of names, and a
requirement is matched by name everywhere.
3. **The `secrets` map names several files under local names, and each local name is a pair
credential of its own.** Adopted.
## Decision
A module's `secrets` entry for a requirement may be a path, as before, or an object of local
names to paths. Each local name is its own pair credential, keyed on it beside the provision,
the consumer node, the consumer module and the provider; its own file on the consumer, referred
to as `${secret:<local name>}`; its own holder at the provider, named the consumer's identity
with the local name after it; and rotated apart from the others. A local name may not be one of
the module's own secrets or something it requires, so what a placeholder means is never
ambiguous. The plain shape is unchanged, and every credential that exists is the one it was.
The holder's suffix is not a login any backend checks — a secret is not a login — so the
identity limit that binds a database role or an access key does not apply to it.
## Consequences
The ten modules that could not move onto the vault can. What got harder: `rotate secret` for a
consumer rotates every local name it holds from that provider; rotating one of several is a
finer command than the mesh has, and waits for a case that needs it.
## How it is checked
Manifest tests read both shapes, write them back, and refuse a colliding or unusable local
name. A resolver test asserts two local names are two needs, two files with two credentials,
and two holders at the provider. An inventory test asserts two local names are two rows, that
rotating one leaves the other, and that the provider is told both. The vault bed installs a
consumer that keeps two secrets and asserts two values delivered, two holders in the vault's
ledger, and both rotated by one command.
## References
- [issue 069](../04-ISSUES/069-one-secret-provision-yields-one-value/00-report.md)
- [ADR 0085](0085-a-secret-is-a-provision.md), [ADR 0049](0049-a-consumers-identity-fits-the-tightest-backend.md)
- [`03-DESIGN/01-to-be/24-the-secrets-vault.md`](../03-DESIGN/01-to-be/24-the-secrets-vault.md)
+1
View File
@@ -113,6 +113,7 @@ python3 00-META/checks/index.py fail if stale
- **0078** — [The store and the broker are ordinary modules](0078-the-store-and-broker-are-modules.md)
- **0079** — [The foundation seats are named after their servers](0079-the-foundation-seats-are-named-after-their-servers.md)
- **0092** — [An operator delivers a pair credential, and the mesh never replaces it](0092-an-operator-delivers-a-pair-credential.md)
- **0094** — [A module may hold several secrets from one provider, each a pair of its own](0094-a-module-may-hold-several-secrets-from-one-provider.md)
### What runs on them, and how it gets there
+7 -2
View File
@@ -4,6 +4,7 @@ status: implemented
code: [mesh-catalog, mesh-controller, mesh-host]
updated: 2026-09-21
decisions:
- 02-DECISIONS/0094-a-module-may-hold-several-secrets-from-one-provider.md
- 02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md
- 02-DECISIONS/0085-a-secret-is-a-provision.md
- 02-DECISIONS/0031-the-control-plane-authenticates-nobody.md
@@ -48,8 +49,12 @@ A module that needs a secret for its own use requires a `secret` provision, exac
a database from the store. The vault generates the value — or takes custody of one an operator
delivered, through `secret accept … --provider`, which seals it to both ends and records the pair
as accepted ([ADR 0092](../../02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md)) — and
the credential belongs to the consumer↔vault pair. An accepted pair is the one exception to what
follows: the mesh cannot make its replacement, so it is neither remade when a key changes nor
the credential belongs to the consumer↔vault pair. A module that needs several values names them
as local names under its `secrets` entry, and each is a pair of its own — keyed on the local
name, delivered as its own file, held at the vault as its own holder
([ADR 0094](../../02-DECISIONS/0094-a-module-may-hold-several-secrets-from-one-provider.md)).
*How it is checked:* manifest, resolver and inventory tests on two local names; the vault bed's
two-secret consumer. An accepted pair is the one exception to what follows: the mesh cannot make its replacement, so it is neither remade when a key changes nor
rotated; both are refused aloud, and accepting a new value is the rotation. *How it is checked:*
an inventory test accepts, reads back unchanged, and asserts the two refusals name the remedy. Because it is an ordinary pair
credential, **everything already built for pair credentials applies to it unchanged**: it rotates
@@ -1,9 +1,9 @@
---
status: located
status: resolved
opened: 2026-09-20
located-in: [mesh-controller internal/catalogue (requires/secrets), mesh-controller internal/inventory (secret key)]
fixed-by:
amended-design:
fixed-by: ADR 0094; mesh-controller feat/several-secrets (secrets: under local names, the pair keyed on the local name, migration 0027); proven by the vault bed
amended-design: 03-DESIGN/01-to-be/24-the-secrets-vault.md
---
# One `secret` provision yields one value, and a module may need several
@@ -18,6 +18,7 @@
is a manifest-vocabulary decision, and it moves the pair key: the credential must be keyed on
the local name, not the provision name, or the second pair overwrites the first.
**Located in:** the manifest's `requires`/`secrets` vocabulary (the catalogue parser) and the pair
credential's key (the controller's secret store). Not fixed here: the key change touches every
existing pair and belongs in a feature of its own, with a lab run against the vault bed.
**Located in:** the manifest's `secrets` vocabulary (the catalogue parser) and the pair
credential's key (the controller's secret store). Fixed as
[ADR 0094](../../02-DECISIONS/0094-a-module-may-hold-several-secrets-from-one-provider.md): the
key gains the local name, empty for every existing pair, so nothing that exists changed.