Consolidate: 65 decision records to 23
Every remaining cluster merged. Each was one design that had been split across
several records because it was worked out over days rather than at once.
the node host 8 -> 1 applies not decides, depends on nothing,
per operating system, root service, the
launcher, episodic, what a declaration is,
actions from the bundle only
a node and how it joins 4 -> 1 what a node is, joining, the link as
security boundary, the enrolment token
modules and the graph 7 -> 1 everything is a module, no domain modules,
three edges, provisioning, the core library
substrate and control 6 -> 1 the test, seven contexts, one control plane,
plane the authority is not a database, the named
products, the pinned bundle
connectivity 3 -> 1 a route is a grant, reachability declared,
filter rules
delivery 5 -> 1 reconciliation not a pipeline, artifacts,
the three silos, a failed step, the verdict
the lab 5 -> 1 (earlier)
how this repository 10 -> 1 (earlier)
works
Nothing was dropped. Each consolidated record carries the reasoning of the ones
it absorbs -- the measurements, the incidents, the alternatives rejected --
because that reasoning is the only reason to keep a record at all. What is gone
is the fragmentation: eight files to read to understand tier 0, when tier 0 is
one component.
The four superseded records went too. They existed to point at their
successors, and the successors now contain what they said.
The checker made this safe. Each merge left dangling links -- 38 files after
the host merge alone -- and it named every one. Nothing was found by reading,
and a manual pass would certainly have missed some, including references inside
AGENTS.md which every session loads.
This commit is contained in:
@@ -4,13 +4,13 @@ status: designed
|
||||
code: []
|
||||
updated: 2026-08-27
|
||||
decisions:
|
||||
- 02-DECISIONS/0037-the-host-applies-it-does-not-decide.md
|
||||
- 02-DECISIONS/0039-the-link-is-the-security-boundary.md
|
||||
- 02-DECISIONS/0049-a-route-is-a-grant.md
|
||||
- 02-DECISIONS/0050-reachability-is-a-property-of-the-address.md
|
||||
- 02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md
|
||||
- 02-DECISIONS/0052-a-filter-rule-names-its-source.md
|
||||
- 02-DECISIONS/0053-one-control-plane-and-no-failover.md
|
||||
- 02-DECISIONS/0037-the-node-host.md
|
||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
||||
- 02-DECISIONS/0049-connectivity.md
|
||||
- 02-DECISIONS/0049-connectivity.md
|
||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
||||
- 02-DECISIONS/0049-connectivity.md
|
||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
||||
---
|
||||
|
||||
# Connectivity
|
||||
@@ -31,7 +31,7 @@ node* — to each responsibility:
|
||||
|---|---|---|
|
||||
| **overlay** — who peers with whom, at what address | **every node**, and which of them can be dialled | control plane |
|
||||
| **resolution** — which name is which node | **every node** | control plane |
|
||||
| **exposure** — which public name reaches which container | **which node is publicly reachable** ([ADR 0049](../../02-DECISIONS/0049-a-route-is-a-grant.md)) | control plane |
|
||||
| **exposure** — which public name reaches which container | **which node is publicly reachable** ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)) | control plane |
|
||||
| **filtering** — which port is open, to whom | what is assigned here, and the overlay's shape | control plane decides, host applies |
|
||||
| **certificates** — who may present which name | which name belongs to which node | control plane |
|
||||
|
||||
@@ -53,7 +53,7 @@ It is also what removes the last two upward dependencies.
|
||||
[Research 006](../../01-RESEARCH/006-mesh-from-scratch/host-size.md) counted exactly two modules
|
||||
opening a direct connection to the control plane's database — `wireguard` and `traefik` — and
|
||||
they are the reason every node permanently holds a credential to it
|
||||
([ADR 0039](../../02-DECISIONS/0039-the-link-is-the-security-boundary.md)). Both are connectivity
|
||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). Both are connectivity
|
||||
modules. **Closing this context closes that set.**
|
||||
|
||||
## The order it comes up in
|
||||
@@ -77,7 +77,7 @@ never be established on a new node. The link stays on the underlay permanently
|
||||
outbound-only and carries its own identity, so it needs nothing the overlay provides.
|
||||
|
||||
**Nothing before step 3 can resolve a mesh name**, which is why the token carries an *address*
|
||||
([ADR 0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md)). Today this is
|
||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). Today this is
|
||||
patched with an `/etc/hosts` floor written underneath the resolver; under this design there is
|
||||
nothing to patch.
|
||||
|
||||
@@ -89,7 +89,7 @@ which of those it may dial, and which must dial it.
|
||||
**Inputs, all declared:**
|
||||
|
||||
- **reachability** — an endpoint, or none
|
||||
([ADR 0050](../../02-DECISIONS/0050-reachability-is-a-property-of-the-address.md)). Not
|
||||
([ADR 0049](../../02-DECISIONS/0049-connectivity.md)). Not
|
||||
inferred from the address shape, which is wrong for carrier-grade NAT, wrong for IPv6, and
|
||||
wrong for a routable address behind a closed firewall.
|
||||
- **site** — where the machine physically is, or nothing if it roams.
|
||||
@@ -98,7 +98,7 @@ which of those it may dial, and which must dial it.
|
||||
|
||||
**Keys.** Each node generates its own keypair. **The private key never leaves the machine**; the
|
||||
public key is published to the mesh. This is already true and it is already right — it is
|
||||
[ADR 0039](../../02-DECISIONS/0039-the-link-is-the-security-boundary.md)'s *a node holds its own
|
||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)'s *a node holds its own
|
||||
identity* applied to the overlay, and it means the control plane computes a graph it cannot
|
||||
itself impersonate.
|
||||
|
||||
@@ -141,17 +141,17 @@ expensively enough to be worth restating:
|
||||
name and overlay address.
|
||||
|
||||
**What goes away:** the `/etc/hosts` floor. It exists because a node had to reach the mesh
|
||||
database before its own DNS existed; with [ADR 0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md)
|
||||
database before its own DNS existed; with [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)
|
||||
nothing needs a name before the link, and a fallback nothing needs is a path nothing tests.
|
||||
|
||||
## 3 — Exposure
|
||||
|
||||
Settled by [ADR 0049](../../02-DECISIONS/0049-a-route-is-a-grant.md); summarised here because
|
||||
Settled by [ADR 0049](../../02-DECISIONS/0049-connectivity.md); summarised here because
|
||||
this is where it belongs.
|
||||
|
||||
**A route is a grant.** A module that must be reachable declares it needs one; the proxy provides
|
||||
it and hands back the public name. Ordinary
|
||||
[ADR 0044](../../02-DECISIONS/0044-a-module-declares-presence-instantiation-and-exclusion.md)
|
||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)
|
||||
vocabulary — the mirror of a database grant, where the consumer supplies a target and receives a
|
||||
name rather than supplying nothing and receiving credentials.
|
||||
|
||||
@@ -164,14 +164,14 @@ the case is a mesh-level fact, which is the fourth reason exposure is control-pl
|
||||
consequence of what runs on it and who must reach it, not an independent declaration to keep in
|
||||
step by hand.
|
||||
|
||||
**A rule names its source** ([ADR 0052](../../02-DECISIONS/0052-a-filter-rule-names-its-source.md)).
|
||||
**A rule names its source** ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)).
|
||||
A rule with no source is open, and must say so rather than appear to restrict something. `scope:`
|
||||
is removed rather than implemented: five manifests carry it today, it is referenced by no code,
|
||||
and it is the clearest instance in the repository of *an unenforced rule is indistinguishable
|
||||
from a wrong one, and costs more, because people believe it.*
|
||||
|
||||
**Unknown keys are refused** — the discipline the host's declaration parser already has
|
||||
([ADR 0043](../../02-DECISIONS/0043-a-declaration-is-an-ordered-list-of-owned-resources.md)), and
|
||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)), and
|
||||
the one manifests lack. `scope:` survived because nothing rejected it.
|
||||
|
||||
## 5 — Certificates
|
||||
@@ -197,7 +197,7 @@ worse than the lab problem that found it — every certificate experiment on a r
|
||||
production issuance quota, and a retry loop can exhaust it for a week.
|
||||
|
||||
**The mesh CA is not a bootstrap concern.** A joining node verifies the control plane against the
|
||||
fingerprint in its token ([ADR 0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md)),
|
||||
fingerprint in its token ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)),
|
||||
so nothing needs the CA before membership. It certifies internal names afterwards, and that is
|
||||
all it does.
|
||||
|
||||
@@ -208,7 +208,7 @@ The list is worth having in one place, because it is most of the argument:
|
||||
- **The last two direct database connections from nodes** — `wireguard` and `traefik`, the only
|
||||
two, both connectivity.
|
||||
- **Therefore the database credential on every node**, and the object-store credential beside it.
|
||||
[ADR 0039](../../02-DECISIONS/0039-the-link-is-the-security-boundary.md)'s central claim becomes
|
||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)'s central claim becomes
|
||||
true rather than aspirational.
|
||||
- **The `/etc/hosts` floor**, and the bootstrap circularity it patched.
|
||||
- **Hub election by address prefix**, and the silent no-hub failure when nobody knew the
|
||||
@@ -219,7 +219,7 @@ The list is worth having in one place, because it is most of the argument:
|
||||
## Open
|
||||
|
||||
- ~~**What happens when the hub is down.**~~ **Resolved** by
|
||||
[ADR 0053](../../02-DECISIONS/0053-one-control-plane-and-no-failover.md), together with `06`'s
|
||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md), together with `06`'s
|
||||
matching question — they were one question. Nothing takes over. WireGuard has no failover, the
|
||||
hub is declared rather than elected, and non-co-located paths stop while co-located direct peers
|
||||
and every already-assigned workload keep running. The recovery path is restore, and its deadline
|
||||
@@ -227,9 +227,9 @@ The list is worth having in one place, because it is most of the argument:
|
||||
- **Renumbering the overlay.** Made *possible* by declaring the hub rather than inferring it from
|
||||
an address, but no procedure exists, and a graph delivered node by node has an ordering problem
|
||||
while it is half-applied.
|
||||
- **Revoking a route** when a module is unassigned ([ADR 0049](../../02-DECISIONS/0049-a-route-is-a-grant.md)).
|
||||
- **Revoking a route** when a module is unassigned ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)).
|
||||
A stale public name pointing at nothing fails more visibly than a stale grant.
|
||||
- **IPv6.** [ADR 0050](../../02-DECISIONS/0050-reachability-is-a-property-of-the-address.md) makes
|
||||
- **IPv6.** [ADR 0049](../../02-DECISIONS/0049-connectivity.md) makes
|
||||
it expressible; nothing here says the overlay or the resolver handle it.
|
||||
- **Reporting declared-versus-observed.** ADR 0050 makes the disagreement detectable and does not
|
||||
say who looks or what they are told.
|
||||
|
||||
Reference in New Issue
Block a user