diff --git a/02-DECISIONS/0004-a-node-and-how-it-joins.md b/02-DECISIONS/0004-a-node-and-how-it-joins.md index 34ca3ca..a958a76 100644 --- a/02-DECISIONS/0004-a-node-and-how-it-joins.md +++ b/02-DECISIONS/0004-a-node-and-how-it-joins.md @@ -215,15 +215,26 @@ before the mesh has configured it, so it can resolve no mesh name. **Both are the same shape: a node needs a fact about the mesh before it has any trustworthy way to obtain one.** So that fact arrives by a path other than the mesh. -**The token carries four things**, and it is the only thing a joining node needs: +**The token carries five things**, and it is the only thing a joining node needs: | | | |---|---| +| **who it is** | the name the mesh calls this machine | | **where** | the broker's **address**, not a name — there is no resolution yet, and this is why none is needed | | **what it is connecting to** | the fingerprint of the broker's certificate | | **who it will believe** | the control plane's signing identity | | **the right to join** | a one-time secret, useless once used and useless after it expires | +*The first row was added 2026-08-30, from raising a mesh end to end for the first time.* It reads +like an oversight and is not: **the node cannot work its own name out.** The name is the mesh's, +chosen when the record was created, and the broker account the node authenticates as is named +after it — so it must be known *before* the mesh can tell the node anything. It is not a secret, +and whoever issues the token already has it. + +Without it, enrolment fails at the broker with an empty username and a message about credentials, +which points at everything except the cause. **A missing fact that surfaces as an authentication +error is worse than one that surfaces as a missing fact.** + **Carried out of band**, by the person adopting the machine. That is what breaks both circles: its authenticity comes from the channel it travelled, not from anything the node can check afterwards. **Trust on first use, with the first use moved out of band** — the difference between diff --git a/03-DESIGN/01-to-be/07-the-substrate.md b/03-DESIGN/01-to-be/07-the-substrate.md index 6f1c623..03bb12c 100644 --- a/03-DESIGN/01-to-be/07-the-substrate.md +++ b/03-DESIGN/01-to-be/07-the-substrate.md @@ -2,7 +2,7 @@ layer: to-be status: designed code: [] -updated: 2026-08-27 +updated: 2026-08-30 decisions: - 02-DECISIONS/0004-a-node-and-how-it-joins.md - 02-DECISIONS/0005-the-node-host.md @@ -200,3 +200,32 @@ host's vocabulary grows by one shape rather than by one resource type per substr [the node host](05-the-node-host.md), never proved. If it is false, the tier boundary moves. - **How the substrate is updated once a mesh exists.** Pinned by hand at bootstrap; afterwards the control plane could deliver it like anything else, and nothing says whether it does. + +## Raised, and observed + +*Written 2026-08-30, the first time a bare machine became a running mesh and something joined it.* + +**It works, and what that means precisely:** a machine with a container runtime and nothing else +applied the bundle its host carries and ended with a store, a database per context, those +contexts' schemas, a broker holding a certificate it generated itself, and the control plane +serving on top of them. Eleven resources, one command, no mesh to ask anything of. + +**Then it joined itself.** The same machine took a token, checked the broker against the +fingerprint pinned in it, generated three keypairs, and enrolled — which is +[ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)'s *the first node is a node whose +mesh is not up yet*, observed rather than argued. Its specialness lasted one command. + +**And a credential crossed.** With a second node recorded, the machine was declared the provider +of a database and pushed to over the broker. What arrived and what did not is the whole of the +[secrets argument](../../02-DECISIONS/0009-modules-and-the-graph.md), measured on a real machine: + +| | | +|---|---| +| the password, in plain text | **on the machine only**, one file, mode 0600 | +| in the declaration that crossed the broker | absent | +| in the control plane's database | absent | +| in what the node reported back | absent | + +**One fault, and it was in the joining.** The token did not say what the mesh calls the machine, +so enrolment needed a flag its own help said it did not — and failed at the broker with an empty +username. Recorded in ADR 0004 as the fifth thing a token carries. \ No newline at end of file