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.
72 lines
2.9 KiB
Markdown
72 lines
2.9 KiB
Markdown
---
|
|
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.
|