Files
hq/04-ISSUES/021-a-consumer-on-the-providers-machine-is-given-no-credential/00-report.md
T
jschoubben 36d9b38a0d An issue is open, diagnosing, located, resolved or wontfix — nothing else
The playbook, the README and the status skill knew five statuses; the cycle check
knew a sixth, 'fixed', and not 'wontfix'. Eleven issues sat in the sixth for weeks
with their fixes shipped, one step short of closed. They are resolved; the check
refuses the word from now on and accepts the one the playbook allows.
2026-09-21 17:38:17 +02:00

4.0 KiB

status, opened, located-in, fixed-by, amended-design
status opened located-in fixed-by amended-design
resolved 2026-09-01
mesh-control
mesh-control df62bb5

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:

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 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, where the machine was treated as an identity rather than a boundary, and 023, 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.