Files
hq/04-ISSUES/021-a-consumer-on-the-providers-machine-is-given-no-credential/00-report.md
T
jschoubben fc7717f6b1 021 closed — the record was left open after the fix landed
The code and its tests went in hours before the record was touched, so
an issue that read `located` had been fixed all along. That is the exact
failure the frontmatter exists to prevent: status is meant to be
answerable from the record rather than by reading the code.

Closed with the commit that did it, and cross-referenced to 022 and 023,
which came out of the same mistaken instinct — treating the machine as
a boundary, then as an identity, then finding a consumer had a password
and no name to present with it.
2026-09-01 15:04:36 +02:00

91 lines
4.0 KiB
Markdown

---
status: fixed
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`.*