The design pass. Everything a module needs is a requirement: a name, a contract, and one of four kinds of provider — a module, the node's host, the mesh, the operator. Installing a module resolves every requirement or refuses, naming everything missing at once. It retires six mechanisms that grew separately: provisions through bindings, settings, assigned ports, machine facts, minted secrets and literals in the definition. ADR 0113, proposed: a provider makes what it provides, and the mesh carries it back sealed to the consumer's node. It is the return path ADR 0048 left "to a separate decision", now needed three ways: data provisions with nothing to answer with, contracts needing a value the controller cannot make, and a vault that generates nothing. Who a consumer is stays the mesh's (ADR 0049). Genesis is the one exception. On acceptance it supersedes 0048 and amends 0085. ADR 0112 is revised from three sources to that single concept. ADR 0110 is amended for two points raised in review. The vault gets the mesh-vault seat (issue 106). A seat's holder outranks co-location for a provision it delivers. Writing that down exposed an inconsistency: mesh-store delivering postgres-database would have sent every database consumer to the control-node, against to-be 23's node-local stores. So a seat delivers a provision only where the mesh has one answer for everyone — artifact store, npm registry, git, vault — and mesh-store and mesh-broker deliver nothing. 23 and 26 follow. 'Control plane' becomes 'controller' in the records written today.
7.8 KiB
topic, status, date, deciders, reconstructed
| topic | status | date | deciders | reconstructed |
|---|---|---|---|---|
| what runs on it | proposed | 2026-09-25 | jochen | false |
113. A provider makes what it provides, and the mesh carries it back to the consumer
Context
ADR 0048 decided that a provider is handed the credential and makes none: the controller mints one per consumer and provider pair, seals it to both nodes, and the provider creates the login under it. It fixed a real fault. The provider harness of the time generated its own password and sealed it with a symmetric key nobody held, so a consumer could never receive what the provider made. Controller-minting worked because it needed no way back from provider to consumer.
It left that way back undecided, deliberately. 0048 says so: "delivering provider-generated data back to a consumer is a return path the mesh does not have and this decision does not build — a separate shape, left to a separate decision." Since then, the missing return path has come up repeatedly:
- Data provisions have nothing to answer with. The analytics provider assigns a site id the consumer needs. The DNS provider registers a name the consumer should be told. Both have no path back, and say so in their code.
- Some contracts need a value the controller cannot make. The broker needs its admin password in a hashed form the mesh's plain secret delivery cannot produce, so a module-specific bootstrap step was written to derive it. A provider making the value to its own contract would not need one.
- The vault is a ledger. A module's own secret is "a
secretprovision the controller mints and the vault records" (ADR 0085, as amended). The one module whose job is secrets generates none, and cannot apply a policy (length, form, lifetime) because it never makes one. Every other provider creates what it provides. The vault is the exception.
ADR 0112 proposes that everything a module needs is a requirement answered by a provider against a contract. Under that, "the controller mints this one kind of answer on the provider's behalf" is a special case the model would carry forever.
Considered Options
1. Keep 0048: the controller mints credentials, and data provisions stay without a way back. Rejected. The vault stays a ledger, contracts needing a derived form keep needing bespoke steps, and a data provision stays unable to answer at all.
2. The provider makes the value and hands it to the consumer itself. Rejected. This is the fault 0048 fixed. The provider cannot seal to the consumer's node, the two may be on different machines, and a key both ends hold is the distribution problem one level down.
3. The provider makes the value and gives it to the mesh, and the mesh carries it to the consumer. Chosen. The provider answers over its own scoped account. The controller, which already seals to every node, seals each secret field to the consumer's node and delivers it the way it delivers everything else.
Decision
A provider makes what it provides. Given a consumer, it creates the resource and answers with the fields its contract names: a password, an access key, a site id, a registered name, a hashed admin secret. The vault generates the secrets it provides, to their contract, and rotates them.
The mesh carries the answer back. The provider hands its answer to the controller over its own scoped broker account. The controller seals every field the contract marks secret to the consumer's node, and delivers the answer as the consumer's resolved values. A provider never reaches a consumer directly. Plaintext exists on the provider's machine, as it does today, and on the consumer's, and nowhere between.
Who a consumer is stays the mesh's. The login a consumer presents is the mesh's derivation, which both ends agree on by construction (ADR 0049, issue 023). A provider makes what a consumer is given, never what it is called.
Rotation is the provider's act. Asked to rotate, a provider makes a new value and answers again, and the mesh redelivers it. An operator-delivered value (ADR 0092) is still never replaced by the mesh: its provider is the operator.
Genesis is the one exception. The foundation's own credentials and the vault's own access exist before any provider can answer. Genesis mints those itself, seals them to the operator key as today, and hands them to their holders. Nothing else is minted by the controller.
What this changes in earlier records
On acceptance, each of these is superseded or amended by this record, not edited:
- ADR 0048 is superseded: a provider no longer receives a minted credential. Its refusal of provider-held symmetric keys stands, and is why option 2 is rejected here.
- ADR 0085 is amended: the vault generates a module's own secret rather than recording one the controller minted. "The vault stores no plaintext, ever" stands. It makes a value, hands it to the mesh and keeps only what it keeps today.
Consequences
- A consumer waits for its provider. Its requirement is not resolved until the provider has answered, so resolution gains a state, waiting on a provider, that is shown rather than silent. Today a consumer can receive a credential before the resource behind it exists. After this it cannot.
- The provider harness in the SDK changes: an adapter's create answers with its contract's fields instead of returning nothing, and every provider in the catalogue moves to it. This is one migration per provider, the cost 0048 named for changing the contract, and it is paid once.
- The controller gains the return path: receiving an answer on a provider's account, sealing its secret fields, and delivering them. Data provisions gain the same path, so the analytics and DNS providers can finally answer.
- Bespoke derivation steps, like the broker's admin-hash bootstrap, become the provider's own answer and can be removed.
- What got harder: a provider that is down cannot hand out credentials, where today the controller could mint one in its absence. That is honest, because a credential for a resource that does not exist yet was never usable, but it moves a failure from later and silent to earlier and visible.
How it is checked
| Rule | Checked by |
|---|---|
| A provider's answer reaches the consumer sealed to its node | A controller test delivering a provider's answer: each secret field is sealed to the consumer's node key and to nothing else. |
| A consumer's identity is still the mesh's | A resolution test: the login a consumer presents is the mesh's derivation, whatever the provider answers. |
| A consumer waits for its provider | A resolution test with no answer yet: the requirement shows as waiting on a provider, and nothing is delivered. |
| The controller mints nothing outside genesis | A controller test: outside genesis, no code path mints a credential. The minting function is reachable only from genesis. |
| The vault generates and rotates | A vault test: a requested secret is generated to its contract, and a rotation answers with a new value. |