What a module must be able to say, measured against 127 that exist

Every manifest in the system being replaced was read and every key
counted, then set against what the new one can express. Three findings
worth more than the table.

**The most-used key was already covered and I expected a gap.**
Depending on another module — 65 manifests, the commonest thing any of
them says — is a requirement naming a module, which already means that
module rather than anything providing the name.

**The largest real gap is tool servers: 56 modules, over half.** A
module can already run one; what is missing is anything saying it offers
tools. That is plausibly a provision rather than new vocabulary, which
would need nothing added — not yet decided, and recorded as undecided.

**The gap most worth closing is health, at seven modules.** The mesh
knows a container is running, which is not whether it answers, and this
project has paid for that distinction twice. An action with a verify is
exactly the right shape and may not arrive over the link, so a module
cannot declare one.

Two things are missing deliberately and say so: stage hooks, because the
link may not carry an action and a module needing setup ships a program;
and flavours, retired in favour of claims.

Config merging is missing and should stay missing. A mechanism that
understands TOML gets asked for YAML, then INI, which is how the thing
being replaced became unholdable.

Also records what the survey found that is not about coverage: manifests
that had stopped matching what was actually brokered, one fact derived
in two places giving two answers, and a live listing returning
credentials in plaintext.
This commit is contained in:
2026-09-01 02:20:34 +02:00
parent 1ee62392b9
commit 36d342f176
3 changed files with 192 additions and 0 deletions
@@ -0,0 +1,71 @@
---
status: located
opened: 2026-09-01
located-in: [mesh-control]
fixed-by:
amended-design:
---
# 021 — A consumer on the provider's machine is given no credential
## Symptom
A module that requires something answered **on the same machine** resolves cleanly and is given
**no credential at all**. Two modules, zero needs:
```
postgres provides postgres-database, grants /var/lib/postgres/grants
keycloak requires postgres-database, secrets /var/lib/keycloak/database.env
→ modules: 2, needs: 0
```
Nothing is refused and nothing is reported. The consumer's `secrets:` path is simply never
written, and whatever reads it fails later, somewhere else.
## Where it comes from
The world a node resolves against is **every other node**:
```go
for _, n := range nodes {
if n.Name == exclude { continue }
```
So a provider on the same machine is never a `Provider` in `world.Offered`, never becomes a
`Needed`, and the credential loop — which walks `resolved.Needs` — has nothing to walk. Every step
is individually reasonable and the sum is a silent gap.
## Why it was not noticed
**Everything proven so far was cross-machine.** The lab's provisioner scenarios put the consumer on
one node and the provider on another, which is the interesting case for a *mesh* and the rare case
in practice. The first module to want a database on its own machine was the first real one.
The postgres provisioner even records the assumption in passing — *"Node is empty for a module on
this machine, which is asking for something local and is not this provisioner's business"* — which
reads as a deliberate exclusion of local consumers.
## Why the assumption is wrong
It holds for a process on the machine reaching a unix socket, where the operating system can vouch
for who is calling. **It does not hold for containers**, which is how nearly everything runs here: a
module's containers reach a provider's containers over TCP on a shared network, and the database
asks for a password exactly as it would from another machine.
**The machine is not a trust boundary once both sides are containers.** Treating it as one gives
the most common arrangement — a service and its database on one node — the weakest handling.
## What it is not
Not the same as [`020`](../020-a-certificate-is-issued-and-never-collected/00-report.md) or a
provisioner defect. The provisioner never sees these consumers because the mesh never records
them as consumers.
## What a fix has to keep
- **A local consumer still appears in the provider's grants**, so its provisioner creates the role
or bucket or client, exactly as for a remote one.
- **The credential is still sealed**, to the one node that is both ends. The mesh holding a
readable secret for local consumers would be a hole opened for convenience.
- **Refusing must stay refusing.** A requirement nothing answers is still refused; this is about a
requirement that *was* answered.