|
|
|
@@ -0,0 +1,112 @@
|
|
|
|
|
---
|
|
|
|
|
topic: the mesh
|
|
|
|
|
status: accepted
|
|
|
|
|
date: 2026-10-01
|
|
|
|
|
deciders: jochen
|
|
|
|
|
reconstructed: false
|
|
|
|
|
extends: 02-DECISIONS/0048-a-provider-creates-the-credential-the-mesh-minted.md
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# 158. A provider with one credential shares it with every consumer, and the vault remakes it for all of them at once
|
|
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
|
|
|
|
[ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md) gave every consumer of a
|
|
|
|
|
provision its own credential: the mesh mints one per pair, the provider's own code creates the
|
|
|
|
|
login, and rotating one consumer's touches nothing else. That is right for a database, a broker, an
|
|
|
|
|
object store — software that can hold many logins.
|
|
|
|
|
|
|
|
|
|
The media software on the home server cannot. A download client has one web password; an indexer
|
|
|
|
|
has one API key; each of the library managers has one key in its configuration; the media server
|
|
|
|
|
holds one token issued elsewhere. There is no login per consumer to create, so
|
|
|
|
|
[ADR 0113](0113-the-vault-makes-every-secret.md)'s only remaining form applied: the value is
|
|
|
|
|
*accepted*. On 2026-10-01 the home server held forty-seven accepted own secrets and twelve accepted
|
|
|
|
|
pair credentials, every one rotatable only by a person changing the software by hand and accepting
|
|
|
|
|
the new value, and one pair credential sat *made* and wrong because nobody could accept the real one.
|
|
|
|
|
The operator asked for every password in the vault and rotatable, and for a library manager's
|
|
|
|
|
definition to receive the download client's credential and address through provisioning like
|
|
|
|
|
anything else (filed as the forge's issue 243 on this repository).
|
|
|
|
|
|
|
|
|
|
The address half already works: the library manager requires the download client's API provision,
|
|
|
|
|
the provider serves scheme, port and user name, and the binding carries them. Only the credential
|
|
|
|
|
half had no form.
|
|
|
|
|
|
|
|
|
|
## Considered Options
|
|
|
|
|
|
|
|
|
|
1. **Keep accepting.** Honest about what the software can do and what the mesh cannot, and it is
|
|
|
|
|
the state the home server was in: nothing rotates, a consumer added later needs a person, and an
|
|
|
|
|
unknown predecessor password stays unknown for ever.
|
|
|
|
|
2. **Put a login per consumer in front of the software.** A proxy that holds the one credential and
|
|
|
|
|
issues many. A second service per provider, with its own credential to keep, to make the mesh's
|
|
|
|
|
model fit software that does not share it.
|
|
|
|
|
3. **Let the provider say its one credential is the credential.** An offer names which of the
|
|
|
|
|
provider's own secrets *is* what every consumer receives. The vault keeps one record, sealed to
|
|
|
|
|
the provider's machine, every current consumer's machine and the operator, and because it stores
|
|
|
|
|
no plaintext it cannot seal an existing value to a later consumer — so it **remakes the value
|
|
|
|
|
for all of them at once** whenever the set of consumers changes or a rotation is asked. The
|
|
|
|
|
provider takes it the way an own secret is taken ([ADR 0114](0114-a-shared-credential-rotates-over-two-credentials.md),
|
|
|
|
|
issue 180); consumers read it at start.
|
|
|
|
|
|
|
|
|
|
## Decision
|
|
|
|
|
|
|
|
|
|
**Option 3.** A provider whose software holds one credential shares that credential, and the mesh
|
|
|
|
|
owns its whole lifecycle.
|
|
|
|
|
|
|
|
|
|
- **The offer says so.** `{"name": "download-client-api", "credential": {"own": "password"}}` on a
|
|
|
|
|
provider's `provides` entry names one of its own secrets as the credential of that provision. The
|
|
|
|
|
named own secret must say how it is taken (`taken: at-start` or `taken: applied`); an offer
|
|
|
|
|
naming an undeclared or untaken secret is refused at parse.
|
|
|
|
|
- **One record, many seals.** The vault keeps one value per (provider assignment, provision). It is
|
|
|
|
|
sealed to the provider's machine, to each consumer's machine that currently binds the provision,
|
|
|
|
|
and to the operator. Every consumer's binding file carries the provider's one user name and the
|
|
|
|
|
secret file carries the shared value; the shape a consumer reads is the pair credential's, so a
|
|
|
|
|
consumer's definition does not know whether its credential is shared.
|
|
|
|
|
- **Remade for all, together.** When a consumer binds or unbinds, or `secret rotate` is asked on the
|
|
|
|
|
provider's own secret, the vault makes a new value and seals it to every current holder in one
|
|
|
|
|
act, and the mesh sends every holding machine. The provider restarts on the new value or applies
|
|
|
|
|
it at start; each consumer restarts on it. There is no window between two credentials, because
|
|
|
|
|
there is one credential; there is the restart, stated as the cost below.
|
|
|
|
|
- **An accepted shared value is sealed to everyone the moment it is accepted.** `secret accept` on
|
|
|
|
|
the provider's own secret is the one moment the mesh holds the plaintext, and it seals copies for
|
|
|
|
|
every current consumer then. It is not remade afterwards ([ADR 0113](0113-the-vault-makes-every-secret.md)):
|
|
|
|
|
a consumer that binds later is refused until the value is accepted again, in words that say so.
|
|
|
|
|
- **A value the software issues itself stays accepted.** A token the media server obtains from its
|
|
|
|
|
vendor cannot be set by the mesh; its provision keeps the accepted form until a module can deliver a
|
|
|
|
|
value it did not mint to the vault, which this record does not build.
|
|
|
|
|
- **Nothing changes for software that holds many logins.** ADR 0048's form stays the default; this
|
|
|
|
|
is the form for an offer that says it has one credential.
|
|
|
|
|
|
|
|
|
|
## Consequences
|
|
|
|
|
|
|
|
|
|
- The media stack's six providers stop needing a person per consumer. A library manager binding
|
|
|
|
|
the download client gets a working credential the mesh made, and an unknown predecessor password
|
|
|
|
|
is replaced by one the mesh knows, recoverable with the operator's key.
|
|
|
|
|
- **Adding or removing a consumer restarts every consumer of that provision and the provider.**
|
|
|
|
|
That is the price of one credential, and it is paid when a definition binds, not at an hour of
|
|
|
|
|
nobody's choosing. It is stated in the plan's words when it happens.
|
|
|
|
|
- Rotation of a shared credential is [ADR 0114](0114-a-shared-credential-rotates-over-two-credentials.md)'s
|
|
|
|
|
single-party form across several machines: in place, all holders sent together. The staged form
|
|
|
|
|
for a backend that takes its credential once is still not built, and a provider whose own secret
|
|
|
|
|
says `applied` refuses rotation by name until it is.
|
|
|
|
|
- The vault can name who holds a shared value — the copies are the record — so *who has this* stays
|
|
|
|
|
a query, as design 13 requires.
|
|
|
|
|
- The accepted count on the home server becomes a list that shrinks, provider by provider, as each
|
|
|
|
|
one's start applies the file.
|
|
|
|
|
|
|
|
|
|
## How this is checked
|
|
|
|
|
|
|
|
|
|
| Rule | Checked by |
|
|
|
|
|
|---|---|
|
|
|
|
|
| An offer may name one of its own secrets as its credential; an undeclared or untaken secret is refused at parse | manifest tests |
|
|
|
|
|
| A consumer of a shared provision receives the provider's value as its pair credential, under the provider's one user name | resolver and declaration tests |
|
|
|
|
|
| The record is sealed to the provider, every current consumer and the operator; a consumer binding or unbinding remakes it for all | inventory tests against a raised store |
|
|
|
|
|
| Rotating the provider's own secret remakes every holder's copy, and an accepted value is sealed to current consumers once and not remade | inventory tests |
|
|
|
|
|
| Live: a library manager on the home server binds the download client with a value the mesh made, the client takes it at start, and a rotation through the console reaches both | done by hand after the media catalogue's providers apply the file at start |
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
- [ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md) — extended: the per-consumer form stays the default; this is the form for one credential
|
|
|
|
|
- [ADR 0113](0113-the-vault-makes-every-secret.md), [ADR 0114](0114-a-shared-credential-rotates-over-two-credentials.md) — the accepted form and the single-party rotation this rests on
|
|
|
|
|
- [Issue 180](../04-ISSUES/180-a-modules-own-secret-cannot-be-rotated/00-report.md) — the `taken` word and the rotation this reuses
|
|
|
|
|
- [Design 24 — The secrets vault](../03-DESIGN/01-to-be/24-the-secrets-vault.md), [Design 13 — Credentials and their rotation](../03-DESIGN/01-to-be/13-credentials-and-their-rotation.md)
|
|
|
|
|
- The forge's issue 243 on this repository, where the operator's ask and the home server's count were recorded
|