--- topic: the mesh status: accepted date: 2026-10-02 deciders: jochen reconstructed: false extends: 02-DECISIONS/0048-a-provider-creates-the-credential-the-mesh-minted.md --- # 188. A provider declares what it derives for each consumer, and the mesh tells both ends ## Context An arrangement between a consumer and a provider is delivered entirely by the mesh. Where the provider is, which port it answers on, what name the consumer must present, where its password is — each arrives as a fact the consumer reads from its binding, or as `${bound:…}` filled into a file before the declaration leaves the control plane. The provider invents none of it and hands none of it back ([ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md)). One kind of value escapes that. Where the **provider names the resource** — a bucket, a database, a vhost — the name is derived from the consumer, per consumer, and the mesh has no way to carry it. `serves` is a literal block in the provider's definition: the same values for every consumer. A provisioner's contract takes a provision and returns nothing. So a value the mesh's own rule produced reaches neither end as a statement; it is recomputed at one end and transcribed at the other. The object store is the instance ([issue 124](../04-ISSUES/124-a-consumer-cannot-be-told-what-its-provider-derived/00-report.md)). Its provisioner normalises the login the mesh minted into a bucket name and creates, checks and removes exactly that; the rule lives in twenty lines of the module's own TypeScript. Its three consumers each write the answer into their own definition by hand. Two transcribed it correctly; one named a predecessor's bucket, and would have authenticated successfully and been refused on every object, which reads like a credential fault and is not one. Even corrected, the transcriptions are wrong in a second way. Each is `mesh--`, so each **names the machine the module happens to run on today** — a definition stating a fact about one installation, which [ADR 0155](0155-a-definition-names-no-installation-and-how-that-is-checked.md) forbids and whose check does not catch because the name is not a domain. Move any of the three to another machine and its configuration points at a bucket its key cannot open. The shape is not the object store's. A database provisioner that prefixed names, a queue provider that scoped vhosts, any provider that derives a resource from who is asking: each forces the consumer to reproduce somebody else's rule and keep it in agreement by hand. ## Decision **1. A served value may name the consumer the mesh is serving.** A `serves` block, which is literal today, may interpolate the mesh's own statement of who the consumer is: - `${consumer:as}` — the identity the mesh minted for this consumer, exactly as the login it is told to present ([ADR 0049](0049-a-consumers-identity-fits-the-tightest-backend.md)); - `${consumer:as:dns}` — the same identity written as a DNS label. Nothing else. **The mesh learns no protocol here; it spells its own name in an alphabet it already knows.** The identity is the mesh's, minted by the mesh, already capped at twenty characters because of what an S3 access key accepts; `dns` is that same name with its separator written `-` instead of `_`, which is the whole of the difference between the mesh's identifier alphabet and the one buckets, vhosts and hostnames use. A provider that needs a prefix or a suffix writes it around the placeholder, because a served value is a string. The rejected alternative is **the provider returning values from provisioning** — the natural channel, since the provider is what derived them. It is rejected for three reasons, in order of weight. It inverts the delivery the mesh is built on: a grant would carry data the provider wrote rather than only data the mesh minted, and [ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md) removed exactly that second path once already. It makes a consumer's declaration incomplete until its provider's reconcile loop has run, so a consumer could not be composed before a provider answered — a bootstrap order the mesh does not have and does not want. And it puts the rule where nothing can check it: a value that arrives from a running process cannot be refused at resolution, only discovered wrong later, which is the failure this record exists to end. **2. The mesh resolves it once, per consumer, and tells both ends from the one resolution.** At the moment a consumer's declaration is composed, the mesh knows exactly who the consumer is. There, and only there, the placeholders are filled. The result reaches: - the **consumer**, as the served facts in its binding file and as `${bound::}` in any file it writes — unchanged mechanisms, carrying one more key; - the **provider**, as `serves` on that consumer's entry in its contributions file, so the provisioner is *told* the name rather than recomputing it. **The provider stops deriving in code and starts declaring.** One statement, filled once, delivered to both ends: the two cannot disagree, because there is no second computation to disagree with. **3. A served value stays settled before it is per-consumer.** Settings still compose into `serves` ([ADR 0174](0174-a-node-varies-a-module-through-settings-and-kept-regions-never-an-edit.md)), and the consumer placeholders are filled after that, so an operator may set a prefix and the mesh still derives the rest. A `${consumer:…}` naming a fact or an alphabet the mesh does not have is refused when the definition is parsed, with what it may say. **4. A consumer may no longer name the resource its provider derives.** With the value delivered, a literal in a consumer's definition is not merely redundant — it is the one thing that can disagree with what the provider will actually create. The three object-store consumers lose their hand-written bucket names in this change. ## Consequences - One more thing a definition may say, and one less thing a module may be wrong about. The vocabulary grows by a placeholder; the catalogue loses three literals that named this installation's control node. - A provider's naming rule becomes readable in its definition instead of in its source. `minio`'s `bucketFor` goes; the manifest says `"bucket": "${consumer:as:dns}"` and the provisioner uses what it is given. - A provider that already serves consumers keeps serving them: the derived value equals what the code derived, so no bucket, database or login changes name. This is a change of **who says it**, not of **what is said**. - The mesh now holds a rule in another system's alphabet — one rule, `dns`, stated once. A second alphabet is a decision, not an addition: the cost of each is that the mesh must be right about somebody else's naming, and that cost is only worth paying where the mesh already mints the name. ## How this is checked - A served value naming an unknown fact or alphabet is refused at parse, with the list of what it may say — tested on both halves of the message. - Resolving a consumer whose provider derives a value puts that value in the consumer's binding file, in its `${bound:…}` substitutions, and in the provider's contributions entry for that consumer — one test asserting the three agree, because agreeing is the whole point. - Two consumers of one provider on one machine get two different derived values, and neither gets the other's. - A catalogue-wide test refuses a consumer definition that writes a literal where its provider derives: the provider's `serves` names the key, so the catalogue can say which definitions transcribe one. - `dns` is checked against the identity the mesh actually mints, not against an invented string: the test derives an identity with `ConsumerIdentity` and asserts the label it becomes. ## References - [issue 124 — a consumer cannot be told a value its provider derived for it](../04-ISSUES/124-a-consumer-cannot-be-told-what-its-provider-derived/00-report.md) - [ADR 0048 — a provider creates the credential the mesh minted, and seals nothing](0048-a-provider-creates-the-credential-the-mesh-minted.md) - [ADR 0049 — a consumer's identity fits the tightest backend](0049-a-consumers-identity-fits-the-tightest-backend.md) - [ADR 0174 — a node varies a module through settings and kept regions, never through an edit](0174-a-node-varies-a-module-through-settings-and-kept-regions-never-an-edit.md) - [ADR 0155 — a definition names no installation, and how that is checked](0155-a-definition-names-no-installation-and-how-that-is-checked.md) - [design 27 — a module requires, the mesh resolves](../03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md)