ADR 0589 §6: an address key gains the issue's ask and time, written when the ask is raised. The key is written at the
reservation, before the ask, so both are empty then and both set from the ask; one without the other is refused, as
is no node. EncodeEnrolmentAddress takes node, ask and issued. EnrolmentIssuedID builds the enrolment-issued event's
id, enrolment-issued.<node>.<issue time in Unix nanoseconds>, as the holder names it today. Tested as rows of
TestNodeRecord; no new test function (1438 unit tests).
(issue 774, review round 1b)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
The operator's decision of 2026-11-02 01:05: the tunnel key is given at issue. Enrolment gains overlay-key, required at
every stage and refused unless it is 32 bytes in base64. Tested as rows of TestNodeRecord; no new test function (1438
unit tests).
(issue 774, review round 1b)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
The operator's decision of 2026-11-02 01:05: issue requires overlay_key, the machine's tunnel public key; the vault's
enrolment-token tunnel declares all five fields a token needs (hub_key, hub_endpoint, address, key, range). An
add-node is proposed at the yes with the tunnel key alone; the identity keys come whole at redeem or not at all. A new
remove-node, mesh-enrolment's alone on the same warrant and the proposal's one change, takes a joining node running
nothing off placement when its enrolment ends unconfirmed (RemovedNodes names the key the holder deletes; LayChanges
lays nothing for it and refuses a node that runs modules or holds seats). NodeLogin gains Acts, the hand acts that
warranted its rotations. Tested as rows of TestPlacement, TestNodeRecord and the two seat tests; no new test function
(1438 unit tests).
(issue 774, review round 1b)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Lead's decision on milestone point 7 (a progressive insight of ADR 0587 §3): mesh-enrolment builds the joining
node's first tunnel at issue and passes it to the vault's enrolment-token as `tunnel`, an object of hub_key,
hub_endpoint and address, each required; the vault puts it in the token (delivery.TokenTunnel). Tested as a row of
TestTheVaultsSeatPromisesItsIntakeVerbs; no new test function (1438 unit tests).
(issue 774, review round 1)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
Blocker 6: enrolment-expired carries the enrolment-issued event's message id (IssuedID), since a bundle's publish
answers no stream sequence and ADR 0587 §2 asks only that the event be named; it gains a codec refusing no event named.
Blocker 7: NodeHealth gains Fallback (node, the login fallen back from, why, at), from which the watchman raises
node-account-fallback (ADR 0583 §6); a fall back naming another node is refused. A token missing its signer now says
the vault's signing key, not the retired word. Tested as rows of TestNodeRecord and the control records test; no new
test function (1438 unit tests).
(issue 774, review round 1)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0587 §7: at redeem mesh-enrolment proposes one change, the node with its decided fields (the approved address,
never the hub) and the public halves of its keys, carrying the issue's warrant; mesh-placement checks the warrant
names this node and address and that the caller is mesh-enrolment, and writes the key with no ask of its own
(changing ADR 0567 §2 and §5 for this one change by this one caller). The proposal codec refuses an add-node beside
other changes, without a warrant, or whose warrant names another node or address, and a warrant on any other
proposal; AddNodeRefusal adds the caller's check for the assigner; LayChanges adds the node and refuses one placed
already. Tested as rows of TestPlacement's proposal subtest, no new test function.
(issue 774, 774/S42)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0587 §1-§3 (changing ADR 0561 §5) make mesh-enrolment the only caller of the vault's enrolment-token and
node-account and a caller of sign, and §2 its bucket enrolments read by it and by the grants part as a named reader.
The holder calls the vault through its manifest's invokes, in its own name, as any caller does; while the vault serves
either enrolment verb every other principal is denied both, however wide its grant (a runtime's or an agent's `*`),
as ControllerOnly denies the desk's prompt. The writers table gains the open enrolments (mesh-enrolment) and the
node-logins (the vault), each one writer; NamedReaders names the grants part for enrolments. Golden lists unchanged.
Tested as a subtest of TestTheCopiedComposerComposesAsTheControllers, no new test function.
(issue 774, 774/S41)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0587 §4: the grants part composes enrol.<node> granted only to call mesh-enrolment.redeem and to hear its own
inbox; §6: from the lease write the controller's enrolment subject is not served. The enrolment principal now calls
redeem in its own name while the seat's holder serves it, and keeps mesh.control.enrol only until Records says the
lease is handed away (a field the grants part sets and the controller's composer never does). A user left able to
enrol nowhere is refused by name rather than composed empty. Golden lists unchanged. Tested as a subtest of
TestTheCopiedComposerComposesAsTheControllers, no new test function.
(issue 774, 774/S40)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0583 §1: a node's account rotates between node.<n> and node.<n>--b, each with the node's permissions and its
inbox _INBOX.node.<n>. Records gains NodeLogins (each node's key of node-logins; the context file's NodeRotating,
renamed because a node that rotated and rotates no more logs in as its second login alone), and Users composes one
node user per login: the one in use and, while it rotates, the other. Each login calls in its own name, which is how
the vault tells the login that confirms (§3). While the vault serves rotate-node, each node reads its own node's key
of node-logins alone and may call node-account-confirm in its own name (§1 insight); the grants part is the state's
one named reader. With no rotation every list is the controller's golden one, unchanged. Tested as a subtest of
TestTheCopiedComposerComposesAsTheControllers, no new test function.
(issue 774, 774/S39)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0583 §1 (insight 2026-11-01) keeps which login a node uses in the vault's state node-logins, one key per node:
the login in use, the one rotating and the stage (begun, ready, confirmed), with the list revision that held both.
§2, §3 and §5 give the vault's seat rotate-node (with the recorded hand act's id), node-account-confirm (the caller
is the proof) and abandon, optional until the vault serves them; the events rotation-begun, node-account-ready,
rotation-done and the engine's finding node-account-fallback are named beside them. The codec refuses a stage without
a rotation and a rotation to anything but the node's other login; an empty rotating reads as none. Tested as rows of
TestNodeRecord and TestTheVaultsSeatPromisesItsIntakeVerbs, no new test function.
(issue 774, 774/S38)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0587 §3 and §5 (changing ADR 0561 §5) have the vault make the token and its hash (enrolment-token), answer it
sealed to the operator key for tokens to relay (sealed, §8), drop both at every end (drop), and mint, answer again
and retire a joining node's account (node-account), with mesh-enrolment their only caller; sign names mesh-enrolment
for a joining node's certificate (with the serving key it presented, since the node has no record yet) and for a
redeem answer. Both verbs optional until the vault that runs serves them. Their answers' shapes are in go/delivery.
Tested as rows of TestTheVaultsSeatPromisesItsIntakeVerbs, no new test function.
(issue 774, 774/S37)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
From the lease write the vault makes the token (ADR 0587 §3), mesh-cli opens and prints it, and the node-engine
parses it: three repositories, so the type leaves mesh-host's internal/identity (which keeps an alias) for
go/delivery, field names unchanged from the controller's token issue, the tunnel kept (the lead's settlement 7).
Encode is added, refusing a token with a part missing. The test reads a token the controller's own code made and
encodes it back to the same line. Paid by merging TestGateBuckets into TestKeysOfPlainNamesAreUnchanged.
(issue 774, 774/S36)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0587 §2 gives the seat mesh-enrolment the bucket enrolments with keys node.<node> (an Enrolment: node, address,
why, caller, ask, stage, issued, expires, keys digest) and address.<address> (the node's name alone), no secret in
either. The token's life rules (an hour by default, at most a day, §4), the event enrolment-expired's content (§2),
and redeem's call, proof bytes, keys digest and signed answer (§6) live here too, because the holder in
mesh-delivery and the engine in mesh-host both read them. The proof binds the node and its keys, not the token's
secret, which the holder never holds (§1). Tested as rows of TestNodeRecord, no new test function.
(issue 774, 774/S35)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
ADR 0587 §1 makes joining a machine a seat held once by the module mesh-enrolment, the vault's only caller for its
enrolment verbs. The seat is in the mesh's own set, its verbs not optional (the seat is new with its holder), and
its events the five that carry no secret. The test is a block in TestHoldingAMeshSeatRequiresServingItsVerbs, no new
test function.
(issue 774, 774/S34)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL