Written 2026-09-28, answered the same week by mesh-controller bdf965d and c68d3a7, and never closed — so the mesh's own account said reach was declared nowhere while three readers were reading it: the filter, the proxy's names, and each authority's host policy. The resolution names them and what checks each. What is left is retiring the older per-port keys the block replaces, which is not a gap in what reach can say.
80 lines
5.0 KiB
Markdown
80 lines
5.0 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-28
|
|
located-in:
|
|
- mesh-controller internal/catalogue/manifest.go
|
|
- mesh-controller internal/catalogue/filtering.go
|
|
- mesh-controller internal/catalogue/declaration.go
|
|
- mesh-controller examples/route-proxy
|
|
- mesh-catalog (every routed module manifest)
|
|
fixed-by: mesh-controller bdf965d (a module names its endpoints) and c68d3a7 (an assignment configures an endpoint as one thing) — the filter, the proxy's names and both authorities now read one statement
|
|
amended-design: 03-DESIGN/01-to-be/08-connectivity.md
|
|
---
|
|
|
|
# 140 — An endpoint's reach is not declared, so three mechanisms each decide it separately
|
|
|
|
## What was observed
|
|
|
|
Preparing to converge the mesh's control-node — the last machine still running the firewall it
|
|
had before the mesh — the question came up for one module: the forge serves git over ssh, and that
|
|
port must stay reachable from outside the private network. Where is that said?
|
|
|
|
The manifest declares the port with a source of `mesh`, so the derived filter would close it to
|
|
everything but the private network. Looking for the place an assignment says otherwise, there are
|
|
two per-node settings keys: one that gives a module's declared port a machine port, and one that
|
|
overrides a declared port's source. The second has exactly one caller — the function that builds
|
|
the node's filter rules. Nothing else in the control plane reads it.
|
|
|
|
A module's routed endpoint is declared somewhere else entirely: a route contribution naming a label
|
|
and a port. It says nothing about reach. The proxy composes a **public** name and an **internal**
|
|
name for every route it is given, and obtains a certificate for each from a different authority.
|
|
Measured on that machine the same day: an identity provider's public name signed by the public
|
|
authority for 90 days, its internal name signed by the mesh's own intermediate for 24 hours and
|
|
renewed daily. Both names exist, and both certificates, because the proxy makes every name it can.
|
|
No assignment asked for either.
|
|
|
|
So the forge's ssh endpoint has a firewall source and nothing else — no name, no certificate, and no
|
|
way to say it should be public other than a key the filter alone reads. And the forge's web endpoint
|
|
has two names and two certificates that nobody requested.
|
|
|
|
## Why it matters beyond this instance
|
|
|
|
**Reach is stated twice, in two vocabularies, in two places that cannot disagree out loud.** A port
|
|
may be exposed to anywhere while the module contributes no public route; a public route may be served
|
|
for a module whose own listen is private. Nothing reconciles the pair or refuses it. Each mechanism
|
|
is separately defensible and the combination is unstated.
|
|
|
|
**The vocabulary belongs to the filter, not to reachability.** *Public, internal, or both* cannot be
|
|
expressed. A source of `anywhere` is one rule on one chain; it says nothing about which names should
|
|
exist or which authority should sign them. So "this endpoint must not be public" has no way to be
|
|
written, and is therefore enforced by nothing — while a public certificate for that very name is
|
|
obtained automatically.
|
|
|
|
**An endpoint is not a thing in the model.** A module has ports, and separately it has routes.
|
|
Nothing binds a port to a name to a certificate, which is why three mechanisms each decide reach on
|
|
their own and none of them is wrong. This is
|
|
[ADR 0045](../../02-DECISIONS/0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md)'s fault
|
|
one level up: that record closed "a declaration that reads as a restriction and restricts nothing"
|
|
for the packet filter. Here the declaration is absent altogether and the mechanisms guess.
|
|
|
|
**It blocks the certificate work.** The open question recorded for certificates — a name that must
|
|
not be public needs either DNS-01 or the internal authority only — cannot be answered while no
|
|
assignment states whether a name should be public. Neither can expiry reporting, revocation, or what
|
|
happens to a name when a machine leaves: all of them need to know which names were *meant*.
|
|
|
|
## Open questions
|
|
|
|
- Should an assignment name each of a module's endpoints, bind it to a node-level port, and state
|
|
whether it is reachable publicly, internally or both — with the filter, the proxy's names and the
|
|
certificate authority all derived from that one statement?
|
|
- What is an endpoint that is neither routed nor certified? Git over ssh is public reach with no name
|
|
and no certificate; the model has to hold that without inventing one.
|
|
- Are the two existing settings keys the same statement, half-built? If so, is this a new declaration
|
|
or the completion of theirs?
|
|
- Does an internal-only endpoint get a certificate at all, and from which authority — and does that
|
|
settle [issue 129](../129-nothing-makes-a-machine-trust-the-meshs-authority/00-report.md), where nothing
|
|
installs the mesh's own root?
|
|
- Does declaring reach per assignment also settle
|
|
[issue 139](../139-an-internal-route-name-resolves-to-the-consumers-node/00-report.md), where an
|
|
internal name resolves to the consumer's machine instead of the one serving the endpoint?
|