Design connectivity in full: overlay, resolution, exposure, filtering, certificates

Written as one document because the five are one design. They share inputs,
they must agree, and every one of them today is computed in a different place
by a different module from a different copy of the same facts.

The through-line is that none of the five can be answered by a machine alone,
so all five are decided centrally and delivered as `file` resources. That costs
no new host vocabulary and removes both remaining direct database connections
from nodes -- wireguard and traefik are the only two, and both are connectivity.

Three decisions fall out, all proposed:

0050 -- reachability is declared, not inferred from an address. The RFC1918
regex is wrong for carrier-grade NAT (100.64/10 tests as public, so an endpoint
is written to an address nothing can reach), wrong for IPv6, and wrong for a
routable address behind a closed firewall. The lab needing TEST-NET-3 to
satisfy the regex is the same bug from the other side. Also kills hub election
by address prefix, which fails silently and makes renumbering an outage.

0051 -- the enrolment token carries where the mesh is and how to recognise it.
Closes two circles with one mechanism: verifying the mesh needed the CA, and
obtaining the CA meant trusting whoever handed it over; and a node had to reach
the mesh before it could resolve any mesh name. An address plus a fingerprint,
carried out of band, resolves both -- and closes the CA question 0049 deferred.

0052 -- a filter rule names its source. `scope:` is declared in five manifests,
is part of no rule type, and is referenced by no code, so those manifests
appear to restrict ports and restrict nothing. Removed rather than implemented;
the general fix is refusing unknown keys, which the host already does and
manifests do not.

Also corrects two claims in 0049 asserting wireguard was already handled.
Research 006 says both modules still reach upward; neither is.
This commit is contained in:
2026-08-27 00:36:49 +02:00
parent 8d9282d86b
commit 4e80820e2f
7 changed files with 557 additions and 9 deletions
+10 -4
View File
@@ -95,8 +95,9 @@ shapes built. The proxy becomes a `container` with `file` configuration, which t
knows how to apply and read back.
And it removes a database credential from every node, which is half of what
[ADR 0039](0039-the-link-is-the-security-boundary.md) is for. The same move was already made for
`wireguard`; this applies it to the other offender.
[ADR 0039](0039-the-link-is-the-security-boundary.md) is for.
[ADR 0037](0037-the-host-applies-it-does-not-decide.md) decided this in principle; **neither
offender has actually been changed**, and this applies it to one of the two.
### A node without a public address is routed through one that has
@@ -117,8 +118,10 @@ behind NAT* — as the case the lab exists to get right.
- **Traefik's upward dependency is removed, and the module mostly disappears.** It computed its
own configuration; now it is an image plus files somebody else derived. Of the 426 lines
research 006 counted, what survives is a declaration.
- **One of two remaining database credentials leaves the node.** `wireguard` was the other, and
it is already handled — so this closes the set that ADR 0039 identified.
- **One of the two direct database connections goes.** `wireguard` is the other and is *not*
yet handled — [ADR 0050](0050-reachability-is-a-property-of-the-address.md) is what handles it,
and only both together close the set ADR 0039 identified. Until then a node still holds the
credential, so this record alone changes the design and not the exposure.
- **Certificate issuance becomes a control-plane responsibility with a real constraint**: an
ACME challenge can only be answered at a publicly reachable address, so issuance happens
through a public node regardless of where the workload runs. The lab already runs its own
@@ -130,6 +133,9 @@ behind NAT* — as the case the lab exists to get right.
mesh, which needs something a joining node can verify before it trusts anything. **Internal
identity and public exposure are two certificate stories and this record only closes the
second.** Conflating them is what made the gap hard to see.
**Since closed** by [ADR 0051](0051-the-enrolment-token-carries-the-mesh.md): the joining node
verifies the control plane against a fingerprint carried in its enrolment token, so nothing
needs the CA before membership.
- **Nothing says how a route is revoked** when a module is unassigned. The grant model implies
it — removing a consumer drops what it was granted
([ADR 0045](0045-a-context-owns-its-store.md)) — but a stale public name pointing at nothing is