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
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
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
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 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