Issue 124: a value the mesh's own rule produced reached neither end as a statement. The object store's provisioner derived each consumer's bucket in its own code; all three consumers transcribed the rule into their own definitions, one of them wrong, and each of the three also named the machine it happens to run on. A served value may now name the consumer the mesh is serving. Design 27 amended; issue 124 resolved.
90 lines
5.7 KiB
Markdown
90 lines
5.7 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-26
|
|
located-in: [mesh-controller internal/catalogue, mesh-sdk src/provisioner, mesh-catalog modules/minio]
|
|
fixed-by: 02-DECISIONS/0188-a-provider-declares-what-it-derives-for-each-consumer.md
|
|
amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md
|
|
---
|
|
|
|
# 124 — A consumer cannot be told a value its provider derived for it, so it transcribes one
|
|
|
|
## What was observed
|
|
|
|
The object-store interface gives each consumer a bucket. The provider **derives** which one: it
|
|
takes the login the mesh minted for that consumer and normalises it into a bucket name, then creates,
|
|
checks and removes exactly that. Its own comment states the reasoning — derived from the login, so
|
|
teardown recomputes it with nothing to persist — and it never reads the bucket a consumer's
|
|
definition named.
|
|
|
|
The consumer still has to tell its own software which bucket to use. It has no way to be told, so
|
|
all three consumers wrote the answer down by hand. Two transcribed it correctly. The third named the
|
|
predecessor's bucket, while the key the mesh scopes for it admits only the derived one: it would have
|
|
authenticated successfully and been refused on every object, which reads like a credential fault and
|
|
is not one. That was found by reading, not by running, and it was filed as part of the fix.
|
|
|
|
## Why the value cannot arrive
|
|
|
|
A definition may interpolate five things: a secret, a seat, a port, a machine fact, and a **bound**
|
|
value — what a provider said a consumer must know. Everything about the arrangement is already
|
|
delivered that way: the endpoint, the port, the region, the login itself.
|
|
|
|
The bound values come from what the provider `serves`, and **`serves` is a literal block in the
|
|
provider's definition**: the same values for every consumer. A value derived *per consumer* has no
|
|
way into it. Nothing else can carry it either — a provisioner's contract takes the provision and
|
|
returns nothing, so what it derived stays inside it.
|
|
|
|
So the mesh has a value it computed, a consumer that needs exactly that value, and no channel
|
|
between them. The gap is not that the answer is unknown; it is that the answer cannot be passed.
|
|
|
|
## Why it matters beyond this instance
|
|
|
|
Every interface where the provider names the resource has this shape. A database provisioner that
|
|
prefixed database names, a queue provider that scoped vhosts, an object store that derives buckets —
|
|
each would force the consumer to reproduce the provider's rule in its own definition. That is a copy
|
|
of somebody else's logic, kept in agreement by hand, which is the failure
|
|
[issue 119](../119-a-module-definition-decides-where-its-files-live/00-report.md) records for paths
|
|
and this one records for names.
|
|
|
|
It also shifts a rule out of the place that can enforce it. The provider knows its own naming rule
|
|
and can refuse a bad one; a transcription in a consumer's definition is a string, and no check
|
|
compares it to what the provider will actually create. The one wrong instance was well-formed.
|
|
|
|
## Open questions
|
|
|
|
- Should a provider be able to return consumer-visible values from provisioning — the natural
|
|
channel, since the provider is what derived them, but it means a grant carries data the provider
|
|
wrote rather than only data the mesh minted.
|
|
- Or should `serves` be able to say a value is derived from the consumer's identity, with the
|
|
mesh performing the derivation — which keeps the provider declarative, and requires the mesh to
|
|
know normalisation rules belonging to somebody else's protocol. It already knows one: the identity
|
|
limit is 20 characters *because* of what an object store's access key accepts.
|
|
- Either way: should a consumer that names a resource its provider will not use be **refused** rather
|
|
than ignored? The contributed bucket was read by nothing, and looked authoritative for months.
|
|
- What would have caught the wrong instance? A test that resolves a consumer's grant and compares the
|
|
bucket in its own configuration against the one the provider would create is a check that could
|
|
exist today, for any interface, without the mechanism above.
|
|
|
|
## Answered, 2026-10-02 — [ADR 0188](../../02-DECISIONS/0188-a-provider-declares-what-it-derives-for-each-consumer.md)
|
|
|
|
The channel is the provider's own `serves` block, which may now name the consumer the mesh is
|
|
serving: `${consumer:as}` and `${consumer:as:dns}`. The mesh fills it once, where it knows who the
|
|
consumer is, and delivers the one filled value to both ends — the consumer's binding and its
|
|
`${bound:…}` substitutions, and the provider's contributions entry, so a provisioner is told the
|
|
name rather than deriving it. Each open question above, answered:
|
|
|
|
- **Should a provider return values from provisioning?** No. It would make a grant carry data the
|
|
provider wrote, make a consumer's declaration wait on its provider's reconcile loop, and put the
|
|
rule where nothing can refuse it. The reasoning is in the record.
|
|
- **Or should `serves` say a value is derived?** Yes, and the mesh performs the derivation — but it
|
|
learns no protocol doing it. The only fact is the identity the mesh itself minted, in one of two
|
|
alphabets it already knows.
|
|
- **Should a consumer that names the resource be refused?** Yes. A consumer's file that already
|
|
contains the value the mesh is about to derive for it is refused at resolution, naming the
|
|
placeholder to write instead. That is the check this report asked for, and it is exact rather than
|
|
heuristic: a derived value carries the identity minted for this consumer on this machine, which
|
|
nothing else would spell out.
|
|
|
|
minio's `bucketFor` is gone; its manifest serves `"bucket": "${consumer:as:dns}"`. The three
|
|
consumers' hand-written bucket names are gone with it — each of them also named the machine the
|
|
module happens to run on, which is the second thing wrong with a transcription.
|