diff --git a/02-DECISIONS/0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md b/02-DECISIONS/0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md index a520bb3..199f947 100644 --- a/02-DECISIONS/0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md +++ b/02-DECISIONS/0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md @@ -99,6 +99,31 @@ composed, so it is not certified. binding. The per-node source override becomes its reach, widened from the filter alone to the names and the certificate as well. +## Progressive insight — 2026-09-29, from building it + +**Reach does not mean the same thing to the filter for an endpoint the proxy serves.** The decision +above says `internal` means "the filter opens the machine port to the private network" and `public` +means "the filter opens it to anywhere". For a routed endpoint the second half is wrong, and +[ADR 0045](0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md) already said so before this +record was written: *a public service is exposed through the proxy, not by opening its own port* — it +listens `from: mesh`, only the proxy reaches it, and it is exposed by name. + +Found by trying to express one real module, not by review. Its routed name must be public, because +browsers post to it; its machine-side port must not be, because that port serves the dashboard in +cleartext. Under one value driving both, saying "public" would have reopened a port an operator had +just closed. Measured the same evening: that module's routed name answered from the internet over TLS +while its machine-side port was refused from the same place. The port is not the path. + +So the reach of a **routed** endpoint asks for names, and its port keeps what the manifest said. The +reach of an **unrouted** endpoint — git over ssh, a mail port, the bus — governs the port, because +there is no name and the port is the only way in. That is the same split this record already draws in +*an endpoint that is not routed is reached but never named*; what it got wrong was carrying the filter +across it. + +This corrects a fact, not the decision: one statement per endpoint, three things derived from it and +none of them deciding on its own, all stand. The table in the decision should be read with the filter +column applying to an unrouted endpoint. + ## Consequences - **A manifest gains endpoint names, and a route contribution names an endpoint instead of a port.**