Tier 2 exists, and the token was missing a quarter of itself

mesh-control is built as far as it can honestly go: one context of seven,
inventory, with its schema and the command that applies it. The repos map and
the control plane design say so, and point at ADR 0024 for what it took.

Separately, and more importantly: this repository described the enrolment token
as carrying three things when ADR 0004 says four. The missing one is the
control plane's signing identity -- the reason a node does not have to trust
the broker it dials.

Without it the control plane's authority is transitive through the broker, and
0004 spells out what that costs: a compromised broker could forge declarations,
and since the host applies whatever the link delivers, that is the whole
machine. The record has the argument in full; the design doc had dropped the
conclusion.

Found by reading the two together while deciding what the control plane must
store, which is roughly the only way it would have been found -- both documents
are internally consistent and only disagree with each other.
This commit is contained in:
2026-08-29 02:49:58 +02:00
parent 84f4425fd6
commit 82a3065f82
3 changed files with 24 additions and 8 deletions
+2 -2
View File
@@ -21,7 +21,7 @@ and a forge address is an operational detail (see [`README`](../README.md)).
## What the mesh becomes ## What the mesh becomes
[ADR 0019](../02-DECISIONS/0019-how-this-repository-works.md) records the repositories the [ADR 0019](../02-DECISIONS/0019-how-this-repository-works.md) records the repositories the
monorepo decomposes into. **`mesh-lab` and `mesh-host` exist so far** — the lab is built first monorepo decomposes into. **`mesh-lab`, `mesh-host` and `mesh-control` exist so far** — the lab is built first
([ADR 0016](../02-DECISIONS/0016-the-lab.md)); the rest are the ([ADR 0016](../02-DECISIONS/0016-the-lab.md)); the rest are the
target, not the present. target, not the present.
@@ -29,7 +29,7 @@ target, not the present.
|---|---|---| |---|---|---|
| `mesh-host` | 0 | **exists.** The node host — one statically linked binary, requiring nothing present ([ADR 0005](../02-DECISIONS/0005-the-node-host.md)) | | `mesh-host` | 0 | **exists.** The node host — one statically linked binary, requiring nothing present ([ADR 0005](../02-DECISIONS/0005-the-node-host.md)) |
| `mesh-substrate` | 1 | the four pinned services, as declarations | | `mesh-substrate` | 1 | the four pinned services, as declarations |
| `mesh-control` | 2 | the control plane and its contexts | | `mesh-control` | 2 | **exists.** The control plane and its contexts — one of seven built ([ADR 0024](../02-DECISIONS/0024-running-the-control-plane.md)) |
| `mesh-surfaces` | 3 | tools, web, cli | | `mesh-surfaces` | 3 | tools, web, cli |
| `mesh-sdk` | — | contracts shared across tiers | | `mesh-sdk` | — | contracts shared across tiers |
| `mesh-lab` | — | **exists.** The lab — scenario lifecycle, networking, placement. Ships to nobody; runs on a workstation. | | `mesh-lab` | — | **exists.** The lab — scenario lifecycle, networking, placement. Ships to nobody; runs on a workstation. |
+12 -4
View File
@@ -1,14 +1,15 @@
--- ---
layer: to-be layer: to-be
status: designed status: designed
code: [] code:
updated: 2026-08-27 - mesh-control
updated: 2026-08-29
decisions: decisions:
- 02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md
- 02-DECISIONS/0005-the-node-host.md - 02-DECISIONS/0005-the-node-host.md
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md - 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
- 02-DECISIONS/0008-a-context-owns-its-store.md
- 02-DECISIONS/0019-how-this-repository-works.md - 02-DECISIONS/0019-how-this-repository-works.md
- 02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
--- ---
# The control plane # The control plane
@@ -69,6 +70,13 @@ and [research 006](../../01-RESEARCH/006-mesh-from-scratch/skeleton.md) leaves *
unresolved — putting it in the substrate risks recreating the circularity the tier design just unresolved — putting it in the substrate risks recreating the circularity the tier design just
removed. Listing it here would settle by naming what has not been settled by arguing. removed. Listing it here would settle by naming what has not been settled by arguing.
**One of the seven is built.** `inventory` owns a database of that name and holds the node records;
the rest do not exist. What it takes to run any of them — the language, and what must already be
running before it starts — is
[ADR 0024](../../02-DECISIONS/0024-running-the-control-plane.md), which also records where the
build stopped and why: at **identity**, because what a node presents to prove who it is is not
decided anywhere, and a migration is the most expensive place in this system to guess.
**These are contexts, not services.** They are separate in the sense that matters — each owns **These are contexts, not services.** They are separate in the sense that matters — each owns
its own store, and they integrate through the record rather than by reading one another its own store, and they integrate through the record rather than by reading one another
([`how-we-build`](../../00-META/how-we-build.md) §4). They are not separate deployables, and ([`how-we-build`](../../00-META/how-we-build.md) §4). They are not separate deployables, and
+10 -2
View File
@@ -134,9 +134,17 @@ to link to and nothing to apply. It answers `profile`, `inventory` and `version`
nox-mesh-host enrol --token <one-time token> nox-mesh-host enrol --token <one-time token>
``` ```
The token carries three things and is carried by a person The token carries **four** things and is carried by a person
([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)): the broker's ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)): the broker's
address, the fingerprint to expect, and the right to join once. address, the fingerprint to expect, **the control plane's signing identity**, and the right to
join once.
**The fourth is the one this document listed three of.** A node connects to the broker and takes
instruction from the control plane behind it, and those are two different identities. Pinning only
the broker would make the control plane's authority *transitive* — a compromised broker could then
forge declarations, which, since the host applies whatever the link delivers, is the whole machine.
So the transport is verified once at connect, and **each declaration is verified by its signature,
every time**.
What happens, in order: What happens, in order: