Files
hq/02-DECISIONS/0143-a-consumer-verifies-the-grant-it-is-given.md
T
jschoubben eba24a72af ADR 0144: anything on a machine may call anything on it, superseding 0143
Everything should be able to call what runs on the same machine, another machine's
service exposed to the private network, and another machine's service exposed
publicly. Three cases; the filter had two.

The first was expressed as the machines' own addresses on the private network. A
caller on the machine carries such an address; a caller in one of its containers
carries a bridge address and matched nothing — measured, same destination and same
machine: src 10.10.0.1 against src 172.17.0.8. The second case worked by accident,
because the tunnel rewrites a caller's address to the sending machine's. Two of
three working is why it read as correct.

0143 answered the wrong question. It proposed verifying each grant from the
consumer's own network position and went to length about which position, because
whether a caller sat in a container changed the answer — and that difference was the
bug. Observing a configuration error is not its remedy. Superseded, and nothing
replaces it; whether the mesh should check a grant is still open in issue 145 and
must stand on its own.

And a module is not a container: 61 of 72 happen to use one, 11 do not, and a rule
reasoning about containers describes most of the mesh rather than the mesh.
2026-09-29 13:32:01 +02:00

8.0 KiB

topic, status, date, deciders, reconstructed, extends, superseded-by
topic status date deciders reconstructed extends superseded-by
what runs on it superseded 2026-09-29 jochen false 02-DECISIONS/0010-delivery.md 02-DECISIONS/0144-anything-on-a-machine-may-call-anything-on-it.md

143. A consumer verifies the grant it is given

Context

A grant is what the mesh writes on a consumer's machine so it can reach a provider. The real one the forge receives for its database, as it arrives:

provision  postgres-database
at         <the provider's machine, by name>
port       the machine port the provider is published on
as         the role the provider created for this consumer

with the credential sealed in a separate file. Four facts and a password, and they are the whole mechanism by which anything in the mesh reaches anything else.

The mesh asserts that claim and never finds out whether it is true. Issue 145: converging a machine dropped the path from a container to a port on its own machine, and for eleven hours the mesh answered all doing what they were told, all heard from, every module current with its source while a web application logged, six thousand times:

connection to server at "<the machine>" (10.10.0.1), port 6852 failed: timeout expired

Every check the mesh makes passed, because every check it makes is about the relationship between the mesh and a machine: the declaration was applied, the digest matched, every container named was running. None of them asks whether a consumer can reach what it requires — though the mesh composed the grant and therefore knows the consumer, the machine, the address, the port and the credential.

And where the check runs decides whether it catches anything. The rule in force admitted the machines' own addresses on the private network. A dial from the machine to its own address carries exactly such a source address, so a check run by the host on its own behalf would have matched that rule and passed — while every container on the machine was refused. This is inference from the rule that was loaded, not a measurement: the fault was found and fixed before anyone thought to dial from the host. It is enough to decide the question, because a check whose position differs from the consumer's is testing something nobody asked about.

Considered Options

  1. The control plane dials each provision. Rejected, and it is the tempting one because the control plane holds every fact. It sits on the provider's machine for most provisions here and reaches the address by a path no consumer uses; in the measured outage it would have passed throughout.
  2. The host dials on the consumer's behalf, from the machine. Rejected for the reason above: the machine's network position is not the consumer's, and the one outage this exists to catch is exactly a difference between them.
  3. Ask the module. Rejected: a module is arbitrary software that the mesh does not write. Some could report on their provisions and most cannot, and a check that covers the modules that opted in tells nobody anything about the rest.
  4. Read the module's logs. Rejected: the failure was in a log the whole time, and reading a module's logs makes the mesh depend on the wording of software it does not control.
  5. The consumer verifies it, from its own network position. Adopted.

Decision

A consumer verifies each grant it is given, from its own network position. After a reconcile has applied a grant, the machine opens a connection to the address and port that grant names, from inside the consumer's own network namespace — the same position the consumer's software dials from, which is the only position that answers the question the grant asks.

It is a connection, not a conversation. Whether the port accepts a connection is what a grant claims; whether the credential is right, the role exists or the schema is current is the provider's to answer and the consumer's to discover. A check that spoke each provision's protocol would be a second implementation of every provision, and would fail for reasons that are not the mesh's.

One failure is not news. A provider restarting is ordinary, and so is a consumer between containers. A grant is reported unreachable only after it has failed on consecutive reconciles, and the count is what the machine reports rather than the last attempt — so a reader can tell "it was briefly away" from "it has never worked".

A grant that cannot be checked is said to be unchecked, never assumed good. A consumer that is not running has no network position to dial from; that is not a broken grant and must not read as one. It is also not a verified grant, and the two are different sentences.

What it costs to be wrong is the constraint on all of it. A check that reports a working provision broken trains a reader to ignore the report, which is worse than having none — the fault this repository keeps finding, one level up. So the threshold is consecutive failures, the check is the cheapest thing that answers the question, and an unknown is reported as unknown.

The mesh says it where it says everything else. A machine's report carries its unreachable grants, and status names them beside what is out of date — so "every module current with its source" stops being the whole of what the mesh will tell you about a machine whose modules cannot reach each other.

Consequences

  • The mesh can be wrong out loud. It has been able to assert a grant and not check it; now a grant that does not work is a thing the mesh says, and the eleven hours of issue 145 become minutes.
  • The host gains the ability to act from a container's network position, which it has not needed before. That is a real capability and the only one this needs.
  • A machine reports something that is not about the declaration. Everything it reports today is what it applied and what it holds; this is the first thing it says about whether what it applied works.
  • A provision with no port is not checked, because there is nothing to dial. Several are files and secrets, and saying "checked" about those would be the appearance of verification that this record exists to remove.
  • What got harder: a reconcile does more than apply. Every grant adds a connection attempt on a cadence, which is cheap individually and worth naming: a machine with many consumers dials once per grant per reconcile.

How it is checked

  • The outage is caught. A bed drops the path from a consumer's network position to a provider's port while leaving the machine's own path to it open — the exact shape of issue 145 — and the grant reads unreachable. This fails against the previous behaviour, where nothing reported anything, and against a check run from the machine, which passes while the consumer cannot reach it.
  • A restarting provider is not an outage. One failed reconcile reports nothing; the count rises and falls, and the grant reads reachable again without anybody acting.
  • A consumer that is not running reads unchecked, not broken, asserted separately from the unreachable case because they are different sentences.
  • A provision with no port is not claimed to be checked.
  • The report carries the count, not the last attempt, so "briefly away" and "never worked" are distinguishable by a reader who sees only the report.
  • status names an unreachable grant, asserted on the output, since a check nothing surfaces is the same as no check.

References

  • ADR 0010 — the declaration is owned resources; a grant is one of them
  • ADR 0009 — what a provision and a consumer are
  • issue 145 — the eleven hours
  • issue 136 — the same distance between a declaration and a machine, one level down