The vault mints a joining node's token, account and certificate, and rotates a node's login over two; nox enrol open (issue 774) #591

Merged
mesh-tools merged 7 commits from feat/l3-enrol into main 2026-11-02 07:56:29 +00:00
Member

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

https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL

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
mesh-tools added 7 commits 2026-11-02 00:52:38 +00:00
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
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Pin mesh-sdk at dff0ce2, the enrolment group's rebased head, so every member builds the same contract (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@db104bdb0838
mesh/delivery-group group feat/l3-enrol merged: every member is merged
mesh/check failed (2 tests): integration: mesh-mail-reader/cmd/mesh-mail-reader.TestAcceptanceOneMatteringMailSendsExactlyOneTellNamingIt: timed out w…
c84dfb5e0b
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Author
Member

Delivery novox/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.

Group feat/l3-enrol: novox/mesh-catalog@c84dfb5e0b, novox/mesh-delivery@63c8687166, novox/mesh-host@44db32c31b, novox/mesh-sdk@dff0ce2abb

  • 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`.
mesh-tools merged commit db104bdb08 into main 2026-11-02 07:56:29 +00:00
mesh-tools deleted branch feat/l3-enrol 2026-11-02 07:56:31 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#591