--- status: resolved opened: 2026-09-01 located-in: [mesh-control] fixed-by: mesh-control df62bb5 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. ## Fixed A requirement answered on this machine is still a requirement. Resolution now records a need for it, so a credential is made, the provider is told who asked, and the consumer's file is written — the same as if the two were on different machines. The reasoning that made it a gap is now written where it was assumed: the machine is not a trust boundary once both ends are containers, and treating it as one gave the commonest arrangement of all — a service and its database on one node — the weakest handling. Two later issues came out of the same mistaken instinct and are worth reading together: [`022`](../022-one-credential-per-node-per-provision-not-per-module/00-report.md), where the machine was treated as an *identity* rather than a boundary, and [`023`](../023-a-consumer-cannot-build-a-connection-string/00-report.md), where the consumer was given a password and never told the name to present with it. *Closed 2026-09-01. The fix landed the same day and this record was left open by oversight — the code and the tests were in place for hours while the record still said `located`.*