From a73014dcd50d6b5b7cdd5c9657fad51bb281573f Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 30 Aug 2026 02:37:37 +0200 Subject: [PATCH] A bare machine became a mesh, and something joined it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First end-to-end raise. A machine with a container runtime applied the bundle its host carries and ended with a store, databases, schemas, a broker holding a certificate it generated itself, and the control plane serving. Then it took a token, checked the broker against the pinned fingerprint, generated three keypairs and enrolled — the first node being a node whose mesh is not up yet, observed rather than argued. And a credential crossed. Declared the provider of a database for a second node and pushed to over the broker, the machine ended with the password in one file at mode 0600, and that password appears nowhere in the declaration that crossed the broker, nowhere in the control plane's database, and nowhere in what the node reported back. That is the whole secrets argument, measured. One fault, 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. It is the fifth thing a token carries now — the node cannot work its own name out, because the broker account it authenticates as is named after it and exists before the mesh has told it anything. --- 02-DECISIONS/0004-a-node-and-how-it-joins.md | 13 +++++++- 03-DESIGN/01-to-be/07-the-substrate.md | 31 +++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) 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