023 fixed — the mesh says who a consumer is, and what it is bound to

Both halves had one cause: the mesh knew something and did not say it.

Who a consumer is now comes from one derivation, sent to the provider in
its grant and to the consumer in its binding, so the two agree by
construction. The provisioners use the name they are given and refuse to
invent one, because a name of their own would create a login the
consumer could never guess while everything reported success.

Bound values reach the file that needs them through the symmetric twin
of the sealed placeholder — simpler, because they are not secret, so the
control plane fills them in and the host gains nothing.

The lab run meant to prove this failed in a way that looked like the fix
being wrong: rotation could not authenticate against a real database.
The cause was the suite rebuilding the control plane's image and not the
provisioner's, so an image built that minute ran against a provisioner
built the day before. That is 005's family and is recorded with the
issue, because the misleading part is worth more than the fix.
This commit is contained in:
2026-09-01 03:05:43 +02:00
parent 4adc656b54
commit f583502fc9
2 changed files with 43 additions and 10 deletions
+9 -8
View File
@@ -119,15 +119,16 @@ the consumer's side did not refuse, it just gave two of the three no credential.
written to date had one consumer per node, which is the natural shape of a small test and not the
shape of a machine.
**A consumer still cannot build a connection string**
([`023`](../../04-ISSUES/023-a-consumer-cannot-build-a-connection-string/00-report.md), open). It
is given its own password in whatever shape its configuration needs, and the host, port and user
name are still out of reach: the user name is invented by the provisioner and recorded nowhere,
and the bound values sit in a JSON document that an application reading `KEY=value` cannot use.
**A consumer could not build a connection string**
([`023`](../../04-ISSUES/023-a-consumer-cannot-build-a-connection-string/00-report.md), fixed). It
had its password in the right shape and the host, port and user name were out of reach: the user
name was invented by the provisioner and recorded nowhere, and the bound values sat in a JSON
document that an application reading `KEY=value` cannot use.
The asymmetry is worth stating, because it is backwards. **The secret is the hard case** — the
mesh must not be able to read it — and the secret is the part that now arrives. The host and port
are ordinary facts held in the clear, and they are the ones stuck.
The asymmetry was backwards, which is what made it worth stating. **The secret is the hard case** —
the mesh must not be able to read it — and the secret was the part that already arrived. The host
and port are ordinary facts held in the clear, and they were the ones stuck. Both halves came from
the same thing: the mesh knew something and did not say it.
## What the survey found that is not about coverage
@@ -1,8 +1,8 @@
---
status: located
status: fixed
opened: 2026-09-01
located-in: [mesh-control]
fixed-by:
fixed-by: mesh-control 122680b
amended-design:
---
@@ -77,3 +77,35 @@ was fixed.
Keycloak, Gitea, Mailu and MinIO all have manifests that parse and resolve, and none of them can
start. This is what stands between the module set and a running one.
## Fixed
Both halves had the same cause: **the mesh knew something and did not say it.**
**Who a consumer is, said once.** `ConsumerIdentity(node, module)` is one derivation, sent to the
provider in its grant and to the consumer in its binding — so the two agree by construction rather
than by two conventions that happened to match on the day they were written. The provisioners now
use the name they are given and **refuse to invent one** if the mesh says nothing: falling back to
a name of their own would create a login the consumer could never guess, and everything would
report success. They also refuse a name that does not carry the mesh's prefix, because that prefix
is how withdrawal finds what it made.
**Bound values reach the file that needs them.** `${bound:provision:key}` is the symmetric twin of
the sealed placeholder and simpler: these values are not secret, so the control plane fills them
in before sending, and the host gains no field and learns no format. `at`, `as` and `from` are
true of any provision; every other key comes from what the provider said it *serves*, so the
control plane still learns nothing about what a `postgres-database` is.
Keycloak and Gitea now produce complete connection strings — asserted from the manifests on disk,
checking that every part is filled, that no placeholder survives as a value, and that the password
is still a hole only the host can close.
### Also found, and separate
The lab run that was meant to prove this failed in a way that looked like the fix being wrong: a
rotation test could not authenticate against a real database. The cause was that the suite
rebuilt the control plane's image and not the provisioner's, so a run with an image built that
minute used a provisioner built the day before. Fixed in `mesh-lab`; it is
[`005`](../005-pipeline-test-harness-unbuildable/00-report.md)'s family — a rebuild covering
most of what a run uses is worse than one covering none, because the run that follows it is
believed.