From d7d6f2eda0d6318d69d723f28f10477e814864e5 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 27 Sep 2026 23:47:45 +0200 Subject: [PATCH] Design 28: the move needs a credential and a membership for machines already enrolled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first live attempt at 5.2 found the half nobody had built. With the seat handed over on record and the new bus's module assigned beside the old one, the push was refused: not one user has a credential for the new bus. The check is right. A credential is minted only at enrolment, at `module issue` and for a person; nothing mints one for a machine already enrolled or for the control plane itself, and on the host the membership is written once at enrolment and never rewritten. So "move each machine" had no mechanism under it on either side. Written into 5.2 as the mechanism to build before anything moves: the control plane mints what is missing and delivers each plaintext where its owner reads it — a machine's as a sealed membership in its declaration, a module's as its broker secret, its own as its module secret — and the host saves a delivered membership and re-dials on it through the reconnect path it already has. 5.3 is ticked as built; 5.4's catalogue half is done and its live half waits on 5.2. --- 03-DESIGN/01-to-be/28-building-the-bus.md | 39 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/03-DESIGN/01-to-be/28-building-the-bus.md b/03-DESIGN/01-to-be/28-building-the-bus.md index 6b7b4e3..f3c692f 100644 --- a/03-DESIGN/01-to-be/28-building-the-bus.md +++ b/03-DESIGN/01-to-be/28-building-the-bus.md @@ -683,12 +683,43 @@ healthy while reacting to nothing. > crash-looped for two hours and nothing could be deployed until it was repaired by hand. > An earlier version of this note said the old broker stays as an ordinary provider of > `amqp` ([ADR 0127](../../02-DECISIONS/0127-amqp-is-a-provision-not-the-bus.md)); that is withdrawn by [ADR 0131](../../02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md) — see 5.4. -- [ ] 5.3 **the seat changes hands as one act.** A command takes a seat and the assignment taking it + + **What the first live attempt found, 2026-09-27.** With the seat handed over on record and the + new bus's module registered and assigned beside the old one, `push` refused the control node: + *not one user has a credential for the new bus*. The check is right — a bus whose user list is + empty refuses every connection in the mesh — and it exposed the half of this task nobody had + built. A credential is minted at three moments only: a machine's at enrolment, a module's at + `module issue`, a person's at `operator`. **Nothing mints one for a machine already enrolled, or + for the control plane itself.** And on the host, the membership — bus address, fingerprint, + password, transport — is written once, at enrolment, and nothing ever rewrites it. So "move + each machine and confirm it reports" had no mechanism under it on either side. + + The mechanism, to build before anything moves: + - **the control plane mints what is missing** — every user the records derive with no hash — + and delivers each plaintext where its owner reads it: a machine's inside its declaration, as a + sealed *membership* for the new bus (address, fingerprint, password, transport); a module's as + its broker secret, the path `module issue` already uses; the control plane's own as its module + secret, so it reads it the way any module does; + - **the host saves a delivered membership and re-dials on it** — the same file enrolment wrote, + the same reconnect path a lost connection takes, so a machine moved this way is a machine + that came back, and nothing new has to be right for it to work; + - **the switch is then two acts in one push**: `MESH_BUS_NATS` on the control plane, and + `seat mesh-broker --to /` — the seat never empty, every machine + already holding a credential that works on the other side. + + Until the first bullet exists the check keeps refusing, and it should: a machine moved without + a credential cannot come back, and afterwards there is no bus to tell it anything over. +- [x] 5.3 **the seat changes hands as one act.** A command takes a seat and the assignment taking it over, and the seat is never empty in between — the emptiness is the outage of 2026-09-27, when the control plane, which finds its own bus through this seat, lost the address and looped. - Today only `seat rename` exists. This is what 5.2 uses to move `mesh-broker` from the old - broker's assignment to the new one's, and it is built first ([ADR 0131](../../02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md)). -- [ ] 5.4 **the old broker and everything that named AMQP leave the mesh** ([ADR 0131](../../02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md), + **Built 2026-09-27** (mesh-controller `seat_holding`, migration 0039; design 26 says how it is + checked). Its first live use recorded the standing holder — which the row moving under it had + made unable to satisfy what the seat delivers, so the first handover on a mesh that predates the + record writes down who holds without re-judging them. This is what 5.2 uses to move + `mesh-broker` from the old broker's assignment to the new one's ([ADR 0131](../../02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md)). +- [~] 5.4 **the old broker and everything that named AMQP leave the mesh** — the catalogue half done + 2026-09-27 (three modules removed; registration refuses the word; the seat's row delivers + `mesh-bus`, migration 0040); the live half — unassigning the old broker — waits on 5.2 ([ADR 0131](../../02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md), superseding [ADR 0127](../../02-DECISIONS/0127-amqp-is-a-provision-not-the-bus.md)): the two modules that required `amqp` are removed, the broker's module is unassigned and removed, registration refuses a manifest that provides or requires `amqp`, and a whole-catalogue check asserts none does. Not