Issue 774 (design 47): nothing is left with the controller — enrolment and rotation. ADRs 0587, 0589, 0583, 0588.
mesh-vault: enrolment-token (issue, sealed, drop; refuses an incomplete tunnel), node-account (sealed to the presented key), sign for a redeeming node only when the four keys' digest matches; rotate-node on a recorded hand act (one rotation at a time, the bus node last, acts kept in node-logins), node-account-ready only once the bus node loaded the list, confirm, abandon, rotation-done.
A wire table test decodes the vault's real answers into mesh-sdk's types.
mesh-cli: nox enrol open --key opens a sealed token locally.
Pins mesh-sdk at the group's head in mesh-vault and mesh-cli.
Delivery group: the four pull requests on branch feat/l3-enrol (mesh-sdk, mesh-catalog, mesh-delivery, mesh-host), one check, one Approve, merged in one pass in the order mesh-sdk → mesh-catalog → mesh-delivery → mesh-host. Rebased on main after the removal group merged (2026-11-02 01:35); every member pins mesh-sdk at dff0ce2abb21 (this group's head). Issuing and rotation stay off until the lease write (ADR 0561 §3, ADR 0587 §4).
Tested locally before the push (GOWORK=off, GOFLAGS=-mod=readonly, GOPROXY=off, GOSUMDB=off): go vet, go vet -tags integration and the untagged unit suite of every touched module pass. Integration tests that exercise the change, against an embedded bus: mesh-vault rotation integration test (whole vault, stand-in engine and reloading bus node): ok.
Reviewed-by: review-g2@shanks at c84dfb5e0b
Pushed-by: lead-47@shanks
Issue 774 (design 47): nothing is left with the controller — enrolment and rotation. ADRs 0587, 0589, 0583, 0588.
- mesh-vault: enrolment-token (issue, sealed, drop; refuses an incomplete tunnel), node-account (sealed to the presented key), sign for a redeeming node only when the four keys' digest matches; rotate-node on a recorded hand act (one rotation at a time, the bus node last, acts kept in node-logins), node-account-ready only once the bus node loaded the list, confirm, abandon, rotation-done.
- A wire table test decodes the vault's real answers into mesh-sdk's types.
- mesh-cli: `nox enrol open --key` opens a sealed token locally.
- Pins mesh-sdk at the group's head in mesh-vault and mesh-cli.
**Delivery group:** the four pull requests on branch `feat/l3-enrol` (mesh-sdk, mesh-catalog, mesh-delivery, mesh-host), one check, one Approve, merged in one pass in the order mesh-sdk → mesh-catalog → mesh-delivery → mesh-host. Rebased on main after the removal group merged (2026-11-02 01:35); every member pins mesh-sdk at dff0ce2abb21 (this group's head). Issuing and rotation stay off until the lease write (ADR 0561 §3, ADR 0587 §4).
**Tested locally before the push** (GOWORK=off, GOFLAGS=-mod=readonly, GOPROXY=off, GOSUMDB=off): `go vet`, `go vet -tags integration` and the untagged unit suite of every touched module pass. Integration tests that exercise the change, against an embedded bus: mesh-vault rotation integration test (whole vault, stand-in engine and reloading bus node): ok.
Reviewed-by: review-g2@shanks at c84dfb5e0bcdbe84621365198a257734cd2efe13
Pushed-by: lead-47@shanks
https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
The vault serves the verbs enrolment-token, node-account, rotate-node, node-account-confirm and abandon, keeps the state
node-logins, and says rotation-begun, node-account-ready and rotation-done (ADR 0587 §3, §5, §8; ADR 0583 §1-§7 and
insights; ADR 0561 §4, §5). Only mesh-enrolment asks the first two and sign for an enrolment; a rotation needs a hand act
read back from mesh-hand-acts by its id, runs one at a time with the bus node last, and is ready only when the loaded list
holds the new login and done when it lacks the old one. A node's login is handed to that node's engine alone, sealed to
the key it stated. The key of a node that leaves the placement is deleted. Accounts kept for an enrolment or a node are
marked and never retired as unwanted.
Subtasks 774/S43 to S50 in one commit: they share enrolment.go, noderotation.go, tools.go and the manifest, and could not
be cut apart without a commit that does not build. New tests: 3 unit functions (table-style flows) and the one
integration test of S50; mesh-vault stays at 134 unit and 2 integration tests, within the module budget of 500 and 50, so
none was merged or deleted. The integration test's player gained a mesh/ask answer.
(issue 774, 774/S43, 774/S44, 774/S45, 774/S46, 774/S47, 774/S48, 774/S49, 774/S50)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
The seat mesh-enrolment's tokens answers the token sealed to the operator key; the operator opens it at their own
terminal. The subcommand reads the sealed token on standard input and the private key from a file, prints the token text
a joining machine takes, asks nothing of the node-engine's socket, and says the token is not sealed to a wrong key
(ADR 0587 §6, §8). New: one table-style test function; mesh-cli had 6, so none was merged or deleted.
(issue 774, 774/S51)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
The vault wrote its own copies of the NodeLogin key and the token; the node-engine and the grants part read them through
mesh-sdk's codec (delivery.EncodeNodeLogin refuses a key without its version, a login that is not the node's, a stage
without a rotation). It now writes and reads through that codec and builds the token with delivery.Token, so the shapes
are one (ADR 0583 §1 insight, ADR 0587 §3). Built against the group's mesh-sdk with a go.work outside the worktree; go.mod
still pins the earlier mesh-sdk, for the lead to re-pin.
(issue 774, 774/S43, 774/S46, 774/S49)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Review round 1 found that redeem could never succeed: the vault's answers were not the shapes mesh-enrolment decodes.
- enrolment-token reads `sealed` (not `show`), so asking for the sealed token no longer mints a new one; it answers
delivery.EnrolmentTokenAnswer: at issue the bus address, its certificate fingerprint and the vault's signing key.
- Issue takes the machine's first tunnel (`tunnel`), keeps it beside the token and refuses a token whose Missing() is
not empty, so the sealed token is one a machine can use (ADR 0587 §3, insight 2026-11-02).
- node-account answers delivery.NodeAccountAnswer: the login, and the password sealed anonymously to the presented
raw X25519 key in base64, as the node-engine's SealingKey.Open reads it.
- A joining node's certificate is signed for the serving key presented at the redeem, only when the four presented keys
have the digest node-account recorded; the name comes from placement (ADR 0587 §6).
- rotate-node refuses a hand act that is not rotate-node of this node, keeps the act id in the node's node-logins key
(`acts`, beside mesh-sdk's fields) and refuses an id used before (ADR 0583 §2).
The enrolment test now calls the real tool handlers and decodes every answer into mesh-sdk's types, failing on an
empty field; it replaces the earlier enrolment test, so mesh-vault stays at 134 unit tests.
(issue 774, 774/S43, 774/S44, 774/S45, 774/S46, review round 1)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
mesh-sdk now carries the hand acts that warranted a node's rotations as NodeLogin.Acts (ADR 0583 §2), so the vault's
own `acts` field beside the codec is gone: a used act is refused from the key's Acts, and a key written without them
keeps the ones recorded. The seat's tunnel takes all five fields; the wire table refuses an issue missing the machine's
tunnel key or the range as well. Built against mesh-sdk 4224665; mesh-vault stays at 134 unit tests.
(issue 774, 774/S43, 774/S46, review round 1b)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Deliverynovox/mesh-catalog@c84dfb5e0bcd — merged since 2026-11-02T07:56:34Z
Source — source.main moved to novox/mesh-catalog@db104bdb08. Whether it reached the machines is mesh-delivery-view's, derived from source.main, the wants and the haves.
composed together: fail — the checker's verdict at the members' heads (3820753740cd): integration: mesh-mail-reader/cmd/mesh-mail-reader.TestAcceptanceOneMatteringMailSendsExactlyOneTellNamingIt
Transitions
2026-11-02 00:52 (new) → proposed (announced): novox/mesh-catalog#591's head announced
2026-11-02 00:52 proposed → ready (accepted): a verdict names this commit and the trunk's head as its trunk commit, the gate passed or warned, and the repository's own check did not fail; or the check gates no merge (check-gates-merge false)
2026-11-02 07:56 ready → merged (merged): on the trunk its modules follow: merged there
The commit's note under refs/notes/mesh-plan keeps every transition of this change up to its merge: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-catalog@c84dfb5e0bcd` — **merged** since 2026-11-02T07:56:34Z
**Source** — source.main moved to novox/mesh-catalog@db104bdb0838. Whether it reached the machines is `mesh-delivery-view`'s, derived from `source.main`, the wants and the haves.
**Group** `feat/l3-enrol`: novox/mesh-catalog@c84dfb5e0bcd, novox/mesh-delivery@63c8687166e6, novox/mesh-host@44db32c31bbe, novox/mesh-sdk@dff0ce2abb21
- composed together: fail — the checker's verdict at the members' heads (3820753740cd): integration: mesh-mail-reader/cmd/mesh-mail-reader.TestAcceptanceOneMatteringMailSendsExactlyOneTellNamingIt
**Transitions**
- 2026-11-02 00:52 (new) → proposed (announced): novox/mesh-catalog#591's head announced
- 2026-11-02 00:52 proposed → ready (accepted): a verdict names this commit and the trunk's head as its trunk commit, the gate passed or warned, and the repository's own check did not fail; or the check gates no merge (check-gates-merge false)
- 2026-11-02 07:56 ready → merged (merged): on the trunk its modules follow: merged there
The commit's note under `refs/notes/mesh-plan` keeps every transition of this change up to its merge: `git log --notes=mesh-plan`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Issue 774 (design 47): nothing is left with the controller — enrolment and rotation. ADRs 0587, 0589, 0583, 0588.
nox enrol open --keyopens a sealed token locally.Delivery group: the four pull requests on branch
feat/l3-enrol(mesh-sdk, mesh-catalog, mesh-delivery, mesh-host), one check, one Approve, merged in one pass in the order mesh-sdk → mesh-catalog → mesh-delivery → mesh-host. Rebased on main after the removal group merged (2026-11-02 01:35); every member pins mesh-sdk at dff0ce2abb21 (this group's head). Issuing and rotation stay off until the lease write (ADR 0561 §3, ADR 0587 §4).Tested locally before the push (GOWORK=off, GOFLAGS=-mod=readonly, GOPROXY=off, GOSUMDB=off):
go vet,go vet -tags integrationand the untagged unit suite of every touched module pass. Integration tests that exercise the change, against an embedded bus: mesh-vault rotation integration test (whole vault, stand-in engine and reloading bus node): ok.Reviewed-by: review-g2@shanks at
c84dfb5e0bPushed-by: lead-47@shanks
https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Delivery
novox/mesh-catalog@c84dfb5e0bcd— merged since 2026-11-02T07:56:34ZSource — source.main moved to novox/mesh-catalog@db104bdb08. Whether it reached the machines is
mesh-delivery-view's, derived fromsource.main, the wants and the haves.Group
feat/l3-enrol: novox/mesh-catalog@c84dfb5e0b, novox/mesh-delivery@63c8687166, novox/mesh-host@44db32c31b, novox/mesh-sdk@dff0ce2abbTransitions
The commit's note under
refs/notes/mesh-plankeeps every transition of this change up to its merge:git log --notes=mesh-plan.