diff --git a/02-DECISIONS/0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md b/02-DECISIONS/0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md index 3cc43bf..3f69b72 100644 --- a/02-DECISIONS/0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md +++ b/02-DECISIONS/0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md @@ -1,6 +1,6 @@ --- topic: the mesh -status: proposed +status: accepted date: 2026-10-02 deciders: jochen reconstructed: false diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 556dcb3..ee87d97 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -179,7 +179,7 @@ python3 00-META/checks/index.py fail if stale - **0163** — [Taking a module over is a comparison: what it compares, what it refuses, and what it carries](0163-taking-a-module-over-is-a-comparison.md) - **0167** — [A membership carries what its module receives, and who the mesh is](0167-a-membership-carries-what-its-module-receives-and-who-the-mesh-is.md) - **0168** — [A converged machine is filtered by the mesh alone, and the host says what else refuses](0168-a-converged-machine-is-filtered-by-the-mesh-alone.md) -- **0169** — [A machine joins through the tunnel, and the bus is never public](0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md) *(proposed)* +- **0169** — [A machine joins through the tunnel, and the bus is never public](0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md) ### Its tiers, from the bottom up diff --git a/03-DESIGN/01-to-be/08-connectivity.md b/03-DESIGN/01-to-be/08-connectivity.md index 78830f5..21fb8a9 100644 --- a/03-DESIGN/01-to-be/08-connectivity.md +++ b/03-DESIGN/01-to-be/08-connectivity.md @@ -9,6 +9,7 @@ code: - mesh-host internal/apply (the service that reflects a rule set) updated: 2026-10-02 decisions: + - 02-DECISIONS/0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md - 02-DECISIONS/0168-a-converged-machine-is-filtered-by-the-mesh-alone.md - 02-DECISIONS/0167-a-membership-carries-what-its-module-receives-and-who-the-mesh-is.md - 02-DECISIONS/0148-the-meshs-names-are-resolved-not-copied-into-containers.md @@ -173,6 +174,28 @@ the broker's node must be dialable by every node, at a stable address, and so mu reachable; on one network it does not. A mesh whose nodes are all behind NAT cannot be raised, and a broker node whose address moves invalidates every token issued for it. +*2026-10-02.* **The order changes at step 1: the tunnel comes first, from the token** +([ADR 0169](../../02-DECISIONS/0169-a-machine-joins-through-the-tunnel-and-the-bus-is-never-public.md)). +The circularity above is real, and it is broken differently. The overlay is configured by the mesh, +except for the one peer a joining machine needs, and the token carries that peer. So the sequence +becomes: + +``` +0 the node has an underlay address the machine's own +1 the node makes its tunnel key before any token; it prints the public half +2 a token is issued for that key its address assigned, and the hub sent it as a peer +3 the tunnel comes up to the hub from the token alone: the hub's endpoint and key, its address +4 the node dials the bus OVER THE TUNNEL, at the bus's private address +5 it proves itself, and is proved to enrolment, checking the key is the one the token named +6 the rest of the overlay the whole peer set, delivered as files +7 names, filtering, routes as before +``` + +The link no longer stays on the underlay. The bus is reached over the tunnel by every machine, +including one that is joining, so it is never opened to the internet. The precondition becomes: **the +hub's tunnel must be dialable by every node, at a stable address.** That port answers nothing to a +key it does not know. + **Whether the link should later move onto the overlay, with the underlay as fallback, is [open](../../02-DECISIONS/0007-connectivity.md).** It is a decision rather than a derivation: the gain is which network carries bytes, not what an attacker can reach, since the link is already