Order the records the way the system is learned

Jochen asked whether the order made sense. It did not -- it followed when
things happened to be decided, which after consolidation is fictional anyway
since record 5 alone folds decisions taken across a week.

Concretely wrong before: the domain statement sat at 8, after five engineering
rules; the constitution was scattered across 5, 12 and 17; the tiers landed at
15, 16, 21 and 22 with process records in between.

Now it walks: what the mesh is (1-3), its tiers from the bottom up (4-8), what
runs on them and how it gets there (9-10), how it is built (11-16), how it is
checked (17-18), how we work (19-23).

Two things made this safe rather than free. It is a permutation, not a
compaction, so the renames go through temporary names -- otherwise two files
want one slot and one is lost. And the reference rewrite is a single
simultaneous pass, because almost every number moved into a slot another number
was vacating; replacing one at a time would have cascaded and pointed things at
the wrong record while still resolving.

Verified: 284 [ADR NNNN](path) links across the repository, all with matching
text and target.

The ordering principle is now stated in 19 rather than left implicit -- the
repository already said "the numbering is the flow" about its folders, and
there was no reason for the records to be the exception.
This commit is contained in:
2026-08-28 23:30:42 +02:00
parent e1febe8e0f
commit 333356cff3
85 changed files with 471 additions and 465 deletions
+27 -27
View File
@@ -4,17 +4,17 @@ status: in-progress
code: [mesh-host]
updated: 2026-08-27
decisions:
- 02-DECISIONS/0011-how-this-repository-works.md
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
- 02-DECISIONS/0016-the-node-host.md
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
- 02-DECISIONS/0023-delivery.md
- 02-DECISIONS/0016-the-node-host.md
- 02-DECISIONS/0016-the-node-host.md
- 02-DECISIONS/0016-the-node-host.md
- 02-DECISIONS/0016-the-node-host.md
- 02-DECISIONS/0016-the-node-host.md
- 02-DECISIONS/0019-how-this-repository-works.md
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
- 02-DECISIONS/0005-the-node-host.md
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
- 02-DECISIONS/0010-delivery.md
- 02-DECISIONS/0005-the-node-host.md
- 02-DECISIONS/0005-the-node-host.md
- 02-DECISIONS/0005-the-node-host.md
- 02-DECISIONS/0005-the-node-host.md
- 02-DECISIONS/0005-the-node-host.md
---
# The node host
@@ -25,11 +25,11 @@ Tier 0. The one thing ever installed by hand, and the only thing that changes a
A **statically linked binary that requires nothing to be present** — copy it onto a machine and
run it, and that is the whole installation
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). Written in Go, because the
([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)). Written in Go, because the
job is system-level and because the host shares no code with any other tier.
A single binary with one job: **apply declared state on this machine**
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). Overlay
([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)). Overlay
membership, packet filtering, packages, services, containers and filesystems are not six
concerns it carries; they are six instances of the one.
@@ -61,7 +61,7 @@ returns it.
Three properties, each following a recorded decision:
**A failed step fails the apply.** Not "logs and continues"
([ADR 0023](../../02-DECISIONS/0023-delivery.md)). A partial apply that
([ADR 0010](../../02-DECISIONS/0010-delivery.md)). A partial apply that
reports success is the mesh's most expensive shape.
**Each applier reads back.** Setting a value is not evidence the value took. The firewall is
@@ -69,7 +69,7 @@ asked whether the rule loaded; conntrack is asked what timeout it holds. This is
§5 as a component requirement rather than a review habit.
**What was applied is recorded after it works, never before**
([ADR 0014](../../02-DECISIONS/0014-a-picture-is-read-from-what-runs.md)). A failed apply leaves
([ADR 0018](../../02-DECISIONS/0018-a-picture-is-read-from-what-runs.md)). A failed apply leaves
the machine in whatever state it reached, and nothing must claim otherwise.
### store
@@ -78,17 +78,17 @@ Local, and **authoritative while disconnected**. Not a cache of the control plan
of what this node has applied and what it currently holds.
This is structural rather than convenient: if disconnection is an ordinary situation rather
than an exception ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)), the
than an exception ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)), the
store is what makes it ordinary. A laptop shut for a week comes back and reconciles; it does
not come back and ask what it is.
### link
The node's one connection to the control plane, and its security boundary
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)).
It is the broker connection that already exists
([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)) — outbound,
([ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md)) — outbound,
node-initiated, per-node addressed — carrying **per-node identity instead of a shared
credential**. The node owns no password. It owns an identity, and that identity is what it
presents.
@@ -108,7 +108,7 @@ architecture, a network position.
capability is real when it is present, running and working, and the difference is the whole
point of detecting it.
The profile is what makes [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)
The profile is what makes [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)
work: a node is a node, and what varies between them is here rather than in the definition.
### inventory
@@ -133,7 +133,7 @@ is the component; that one is what happens to it.
## Where a declaration comes from
One behaviour, two sources
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)):
([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)):
| Situation | Source |
|---|---|
@@ -150,7 +150,7 @@ the mesh, and the full peer set arrives derived.
## What a declaration is
Settled by [ADR 0016](../../02-DECISIONS/0016-the-node-host.md).
Settled by [ADR 0005](../../02-DECISIONS/0005-the-node-host.md).
**JSON**, because the host has no dependencies to spend and the standard library carries no
YAML. **An ordered list of typed resources**, each with a stable identity — the order is stated
@@ -187,8 +187,8 @@ Raising the substrate needs six shapes in the host's vocabulary, and **all six a
| `directory`, `file` | **built** | no machine dependency at all |
| `service` | **built** | running/stopped **and** enabled/disabled at boot — a unit started but not enabled stops being true at the next reboot |
| `package` | **built** | present, never upgraded, and **never uninstalled** — the host cannot know what else needs it, so dropping one is *forgotten*, not *removed* |
| `container` | **built** | pinned by digest ([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)); identified by a label carrying a digest of the declaration that made it, because a runtime normalises what it is given and that is indistinguishable from drift |
| `action` | **built** | bundle-only ([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)); verify is mandatory and is the idempotency check as well as the read-back |
| `container` | **built** | pinned by digest ([ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md)); identified by a label carrying a digest of the declaration that made it, because a runtime normalises what it is given and that is indistinguishable from drift |
| `action` | **built** | bundle-only ([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)); verify is mandatory and is the idempotency check as well as the read-back |
**The parser enforces the boundary rather than the caller remembering it.** `Parse` refuses an
action and is what the link uses; `ParseTrusted` permits one and is what the bundle uses. The
@@ -205,7 +205,7 @@ until it is done the substrate bootstrap has no end-to-end test.
**3 — link and store.** The node connects, receives declarations, and holds what it applied.
**4 — enrolment.** The one genuinely new mechanism in
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md); everything else there
[ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md); everything else there
is configuration of what already runs.
Stage 1 is deliberately the smallest useful thing. The lab currently raises **empty machines**
@@ -216,7 +216,7 @@ that, and every later stage is tested by a lab that already works.
**The lab is the harness.** A scenario places a host on a machine and asserts what it did —
against a real hypervisor, with the boundary never mocked
([ADR 0013](../../02-DECISIONS/0013-a-test-defends-a-decision.md)).
([ADR 0017](../../02-DECISIONS/0017-a-test-defends-a-decision.md)).
Each decision above owes a test:
@@ -239,7 +239,7 @@ Each decision above owes a test:
package, a unit, a container and a dataset *are*. Nothing has measured that surface, and it is
the residue of the question [`host-size.md`](../../01-RESEARCH/006-mesh-from-scratch/host-size.md)
answered.
- **Rescue.** [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) suggests it is
- **Rescue.** [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md) suggests it is
a node whose local state is discarded so the mesh re-derives it, and does not decide it.
- **What may expire.** An identity needing refresh to stay valid would make a laptop fail for
being a laptop ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
being a laptop ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)).