Commit Graph
711 Commits
Author SHA1 Message Date
jschoubben 604f1528d6 Builder owns the step runner; a build's imports reach its done key and log
buildrun leaves mesh-sdk for the builder's own Go module (ADR 0593 §1); the
checker, assigner, indexer and watchman require it by a replace to the sibling
directory. Issue 1930's bound was already on mesh-sdk's main and moves with it.
The builder copies the imports a build module answers to the done key and the
log's end entry, where the indexer keeps them (§2; D7).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 18:29:56 +01:00
jschoubben 2aa43ec249 Bridge reads no mesh package from a go.sum
Only the indexer computes what a fingerprint takes (ADR 0593 §2): the merged and
pull messages carry no Packages, and the kept inputs no longer hold them (D6).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 18:26:53 +01:00
jschoubben fba98ee80f Indexer computes fingerprints alone, over the packages a module imports
The fingerprint, named tree and package versions move from mesh-sdk into the
indexer's internal/fingerprint (ADR 0593 §2). The indexer publishes each reached
mesh Go module's package digests from the forge's listing in its new packages
state, keeps the imports a done build reports, and writes the import form only
when an input differs from the build that reported them (§4), so the form change
costs no rebuild and a package merge moves only its importers (§3). Candidates
take the same form against the group's member heads (D1-D4).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 18:25:57 +01:00
mesh-tools 511e4b7414 Merge pull request 'The grants part and the indexer without the controller; consumers far behind raise a condition (issue 1126 group B)' (#138) from feat/1126-removal into main 2026-11-02 13:41:16 +00:00
jschoubben d26781b403 Place a group's catalogue head beside its other members' tests (issue 1126)
mesh/delivery merged: source.main moved to novox/mesh-delivery@4dd7dcfc1411
mesh/check being carried to d26781b403df on 4dd7dcfc1411 | from be88162cde31
A group's check judges each member against the group's heads (ADR 0482 §2,
ADR 0573 §4), but the checker placed mesh-catalog at its trunk beside every
other member even when the group held a catalogue pull request, so
mesh-delivery's judge tests in group feat/1126-removal judged the new judge
against catalogue main and failed. The catalogue now follows the rule of
every other sibling: the member's head and ref in a group, the trunk alone.
A catalogue change's own trunk copy (ADR 0529 §2) is placed separately and
is unchanged.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 13:12:54 +01:00
jschoubben e2cb398186 Pin mesh-sdk to the group head of issue 1126
mesh/delivery merged: source.main moved to novox/mesh-delivery@511e4b741497
mesh/delivery-group group feat/1126-removal merged: every member is merged
mesh/check checking e2cb3981866e on 4dd7dcfc1411 | novox/mesh-delivery#138 in group feat/1126-removal
Every member builds against mesh-sdk abf5eac, the head of feat/1126-removal, so the group is checked as one contract.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 12:27:33 +01:00
jschoubben 6a4d88265d Say mesh-grants.consumer-behind again only when the consumers behind change, not their counts
The counts change every pass, so a consumer that stays behind was said to the operator again at each pass. The
condition names the consumers alone; how far each is stays in the last pass.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 12:23:23 +01:00
jschoubben 655692c7ba Pin mesh-sdk to the group head of issue 1126
Every member builds against mesh-sdk f569924, the head of feat/1126-removal, so the group is checked as one contract.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 12:20:14 +01:00
jschoubben 8f9766e46c Let the grants part say an account consumer far behind as mesh-grants.consumer-behind
The self-check's bus-consumer probe judged consumers the controller defined, and with the controller ended it judges none
(issue 1126). The grants part owns the account consumers, so it says when one stays more than 1000 messages behind its
stream's head at two passes running, the probe's bound and confirmation, and clears it once none is.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 12:03:29 +01:00
jschoubben 83c1b3ce38 Pin mesh-sdk to the group head of issue 1126
Every member builds against mesh-sdk 729a86b, the head of feat/1126-removal, so the group is checked as one contract.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:56:55 +01:00
jschoubben 000a24d261 Match the judge's copy of mesh-wants' upgrade to mesh-sdk (issue 1126)
mesh-sdk now says what upgrade's compare answers without the controller:
each node's own composition and the assigner's findings. The judge keeps
a copy of the seat, which must read the same.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:49:17 +01:00
jschoubben 3228d911cb Let a module move between repositories by the merges that move it (issue 1126, route-proxy)
route-proxy leaves the controller's repository, which is archived, and nothing registered a move since the controller
went: the indexer left a module named by another repository's merge as it was, forever. Now that merge waits, and the
holding repository's merge that drops the module hands it over in place, so its key never goes missing between the two;
merged the other way round, the key is deleted and written new. A trunk that still holds the module keeps it (ADR 0420
clause 7, ADR 0501 clause 3).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:48:34 +01:00
jschoubben 7e358b0f1d Recognise the control node by the bus module, not the controller (issue 1126)
The watchman held a node a control node, urgent after half an hour
silent, while its have named mesh-controller; the controller ended by
its unassignment (ADR 0590), so no node was one any more and the bus
node's silence stayed a warning. The control node is now the node whose
have holds the bus module, nats, which the watchman already hears by
that name.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:47:53 +01:00
jschoubben 8d4c13427a Build against mesh-sdk without the controller (issue 1126)
mesh-sdk's group B member renames the grant to Grant, drops the
controller principal, the signer and the lease fields from the user
list key, and the keeper's and config's take-over verbs. The grants
part, the assigner and the view use the new names; the judge's copies of
the health and config seats follow; the tests' fixtures lose the
controller's user, and the refused principal is one removing what the
closed set does not name, the controller's mirror being gone.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:46:12 +01:00
jschoubben 2049b1a370 Say the assigner's conditions as findings alone, the keeper being the one source (issue 1126, B8)
ADR 0590 clause 3 ends the choice of a condition source by the keeper's
take-over key, which mesh-sdk no longer defines. The assigner watched
that key and said every condition straight to the router while it named
the controller. It now writes each as a finding the keeper carries, and
says one straight only where it has no findings bucket at all.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:46:12 +01:00
jschoubben 43d4f511c5 Drop the lease write's findings, the controller's artifacts-gone and the issuing gate (issue 1126, B9)
ADR 0590 supersedes the lease write: mesh-grants says no lease event any
more, so the watchman's lease-altered and removal-failed findings and
their row had nothing to hear. The ended controller's consumers are
removed as hand acts, so their deletion is no consumer lost; only the
one on DEAD_LETTER_NOTICES is still read, since its removal hands the
notices to the watchman. The builder hears artifacts-gone from the
artifact sweep seat alone, and mesh-enrolment's tokens no longer report
an issuing gate that nothing can turn off.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:35:55 +01:00
jschoubben f7f3518a04 Take the controller out of the judge, the checker's siblings and the assigner (issue 1126, B8)
The controller ended by its unassignment (ADR 0590) and mesh-sdk drops
its seat, the grants seat and placement's adopt and hand-back. The
indexer's judge still carried the controller's seat entry, the old
removal words and the two placement verbs, and counted 58 seats; the
checker still placed mesh-controller beside the node-engine's and the
catalogue's tests and failed a node-engine check without its trunk; the
assigner still asked a compose of the running controller, served adopt
and hand-back as stubs, and kept a policy-split finding that could no
longer arise.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:32:56 +01:00
jschoubben d4aa1fe11b Hear the keeper alone and stop reading the controller's lease in mesh-grants (issue 1126, B7b)
ADR 0590 §3 ends the choice of a condition source by the keeper's
take-over key: the controller is unassigned and the keeper is the one
source. The part still read the take-over key, fell back to the
controller's condition events when it was absent, and read and watched
the controller's lease, which nothing writes any more. It now hears the
keeper's events alone and reads neither.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:27:19 +01:00
jschoubben 14046f9b14 Remove the lease write, take-over and their tools from mesh-grants (issue 1126, B7a)
The controller ended by its unassignment (ADR 0590), which supersedes the
lease write and its hand-back whole and makes the grants part's list the
only one the bus node follows. The lease write, its check, the removal
marker, the take-over and hand-back asks, their five tools, the seat
claim and the lease and takeover states had nothing left to act on; the
membership notice on its subject had no reader. A whole-module test on
the bus replaces the lease write's: with no controller at all, the part
publishes a membership in its state alone, makes the module's consumer
and writes the key the bus node follows.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:25:59 +01:00
mesh-tools be88162cde Merge pull request 'Without the controller: mesh-grants makes consumers, enrolment issues, the watchman keeps dead letters, and the controller cannot be placed again (issue 1126)' (#137) from fix/1126-gates-delivery into main 2026-11-02 09:51:14 +00:00
jschoubben a192052d82 Refuse any placement of the controller (issue 1126)
mesh/delivery merged: source.main moved to novox/mesh-delivery@be88162cde31
mesh/check being carried to a192052d82fe on be88162cde31 | from 0490043990e2
The grants part, the enrolment seat and the watchman now act without
reading the lease, so a controller placed again would write beside them
with nothing saying so. The assigner refuses every proposal, seed or
joining node's write that would place mesh-controller, naming why.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 10:38:22 +01:00
jschoubben 1dae2de835 Issue enrolments and keep dead letters without the lease (issue 1126, A2)
The controller ended by its unassignment, so its lease key is absent for
good and mesh-sdk reads that as not handed away: mesh-enrolment's issue
stayed off and the watchman never took DEAD_LETTER_NOTICES, whose notices
age out after a week. Both now act without reading the lease; the
watchman still waits, and says so, while the controller's consumer on the
notices stands. Neither manifest reads the lease any more.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 10:35:40 +01:00
jschoubben e094e362f4 Run mesh-grants' handed behaviour without the lease (issue 1126, A1)
The controller ended by its unassignment and its lease key expired, so
every gate on "the lease is handed away" stayed shut for good: no account
consumer was created or matched, grant-missing was never raised, the
enrolment users kept the controller's subject, memberships were still
said on their subject and the sealed key never said LeaseHanded. The
handed branch now always runs, and a hand-back is gone.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 10:33:14 +01:00
jschoubben 8850a5673e view_reach takes a module, so one module's reach is asked without reading the whole repository
mesh/delivery merged: source.main moved to novox/mesh-delivery@0490043990e2
mesh/check being carried to 8850a5673e61 on 0490043990e2 | from cc9c681fa348
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 10:24:54 +01:00
jschoubben 8f87dbe94d mesh-delivery-teller: read asks from the router's bucket the grant names
mesh/delivery merged: source.main moved to novox/mesh-delivery@cc9c681fa348
mesh/check being carried to 8f87dbe94d76 on cc9c681fa348 | from f1c33439292d
The teller read the router's record from KV_asks, a bucket no composed grant
names, so every read was denied and its operator answers could not be read
back. It now reads messenger_asks, as mesh-grants does. The integration bed
grants the real subject and reads a record on the teller's credential, so it
no longer agrees with the mistake. (issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 09:44:26 +01:00
jschoubben 7cee3842b8 mesh-grants: stop the controller on its placed node and read asks from the router's bucket
The lease write addressed node-service-manager at the lease holder's host, a
hostname no node carries ("novox-be" on novox), so the operator's yes timed out
at its first step and failed without a log line. The node now comes from
placement (exactly one node placing mesh-controller, else refused by name), a
failed act is logged beside its condition, and the asks record is read from
messenger_asks, the bucket the composed grant names, instead of asks, which no
grant allows. The fixtures give the lease a host unlike the node, so the tests
no longer agree with the mistake. (issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 09:42:19 +01:00
jschoubben 63c8687166 Let a bus module's tools bundle declare what it invokes (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-delivery@f1c33439292d
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…
The judge refused nats's manifest for its invokes (seat:mesh-hand-acts.hand-act,
which nats_bucket_expiry asks to record its hand act), and with one manifest
refused the grants part holds the whole bus user list, freezing every build
that adds bus permissions. The bundle asks through the node's tool runner,
which buscompose grants every tool and seat verb, so the invokes are granted:
the same condition that already lets the bundle declare emits (ADR 0357).
ADR 0235's rule stands for a bus module with no bundle.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 02:21:05 +01:00
jschoubben b13f65674b Pin mesh-sdk at dff0ce2, the enrolment group's rebased head, so every member builds the same contract (issue 774)
mesh/delivery-group group feat/l3-enrol checking: 4 of 4 member(s) ready
mesh/delivery closed: a newer head of the same pull request
mesh/check checking b13f65674b35 on 9125fcc71206 | novox/mesh-delivery#134 in group feat/l3-enrol
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:48:53 +01:00
jschoubben e6ee939bd4 The indexer's closed seat set counts fifty-eight with mesh-enrolment (ADR 0587 §1)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:48:53 +01:00
jschoubben bde60130a8 The assigner's add-node test writes no lease: since the removal group the assigner reads none and its bed has no lease bucket
The enrolment's issue waits for the lease write itself (ADR 0587 §4); the assigner takes an add-node on its warrant at any time (ADR 0567 §8). (issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:48:53 +01:00
jschoubben 795724f5ca mesh-enrolment: catch up only on an ask answered yes, and drop the address key first at confirm, decline and refused add
ADR 0589 version 29, clause 6. At start and after a catch-up an address key is an expiry never heard only when its
enrolment key is gone and the router records its ask answered yes; one declined or not answered yet is no expiry, and one
not answered stays until its ask ends. A confirm, a decline and a refused add delete the address key before their other
steps, so one stopped midway never leaves a key the catch-up would take for an expiry and unplace a node that joined. A
refused add (mesh-placement's refusal of the proposal, not a call that did not reach it) now ends the enrolment as refused;
a call that failed still waits for again. Tests: rows in the end table (after a confirm stopped before its enrolment key
went, after a decline, after a refused add, a declined ask, an ask not answered: nothing unplaced) and the integration test's
holder-was-down case reading the yes; no new function.

(issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:53 +01:00
jschoubben 44fee9eb0e mesh-enrolment: end an enrolment that expired unseen from its address key, taking the node out of placement
ADR 0589 clause 6: a placed but unconfirmed node is always taken out again, on the warrant of its ask. The address key now
names the ask and the issue's time (delivery.EnrolmentAddress, mesh-sdk c7130e3), written just before the ask is raised and
with no expiry of its own, so it outlives the enrolment key; the holder deletes it at every end. An expiry, seen or not, ends
the enrolment in this order, each step safe to repeat: remove-node on the ask's warrant (only when placement holds the
node), enrolment-expired naming the rebuilt issued event (delivery.EnrolmentIssuedID), the vault's drop of the token, and
the address key last. Reconcile finds every address key that names an ask and whose enrolment key is gone (expired while
the holder was down, before its watch caught up, or an end that stopped) and ends it; it runs once the watch has caught up,
on every expiry, and again with a growing wait while a step fails. An address reserved by an issue under way (no ask yet) is
left alone. Tests: rows in the end table (expired unseen; the holder down with a drop that fails once; a reservation under
way) and the integration test's expiry case (the address key goes; a holder that was down unplaces on its pass); no new
function.

(issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:53 +01:00
jschoubben cbcbb9644b mesh-enrolment: keep the tunnel key in delivery.Enrolment's own field
mesh-sdk 22c03af carries the machine's tunnel key on the enrolment (OverlayKey, overlay-key, required at every stage), so the
holder's wrapper that kept it beside the contract is gone; the wire is unchanged. Test fixtures give the seeded enrolments a
real tunnel key, which the contract now requires.

(issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:53 +01:00
jschoubben d1a256b429 mesh-grants: give the enrolment fixture its tunnel key, which an enrolment now requires
mesh-sdk 22c03af (feat/l3-enrol) requires overlay-key on every enrolment; the fixture of the enrolment-user subtest carries one.

(issue 774, review round 1b)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:53 +01:00
jschoubben 40bbc7e0d3 mesh-enrolment: take the tunnel key at issue, place the node on the yes, take it off again at an unconfirmed end
The operator's decision of 2026-11-02 01:05, on mesh-sdk 4224665:
- issue takes overlay_key, the machine's tunnel public key as `nox-mesh-host key` prints it, and refuses a missing one. The
  token's first tunnel is whole: key, address, range (the hub's tunnel range in placement, the one the controller's token
  carries today; a hub with none is a finding, never a guess), hub key and hub endpoint.
- On the operator's yes the node is proposed into placement at once (add-node with its tunnel key alone, on the warrant), and
  only then is the token made, so the hub peers it before redeem. Redeem writes no placement: it refuses a tunnel key other
  than the one issued for, or a node placement does not hold yet, and the certificate's call carries all four keys presented.
- A revoke, a refusal or an expiry before confirm takes the node off placement (remove-node, same warrant) before the end is
  said, and the end brings the token's drop.
- The assigner applies a remove-node: the node's key deleted at once, with no ask, placement-changed carrying the warrant.
- The tunnel key is kept as one more member of the node key's JSON, beside delivery.Enrolment, until the contract carries it.
Tests: rows in the existing tables and both integration tests (the token's Missing() is empty; the node is placed before
redeem and leaves placement when its enrolment expires); no function added, mesh-delivery stays at 816.

(issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:53 +01:00
jschoubben 70f13a3775 mesh-enrolment: place before the vault is asked, read the grants pass whole, name the expired enrolment's issued event, carry the first tunnel
- redeem writes the node into placement before it asks the vault for the account and the certificate of the serving key
  presented (the vault reads the node's address from placement); a retry by the same keys still finishes (blocker 3).
- loadedVia reads mesh-grants.user-list's pass as the part's whole Result, the list state under user_list; the fixture
  testdata/grants-pass.json was written by mesh-grants' own Result, not by hand (blocker 4).
- enrolment-expired is delivery.EnrolmentExpiry with IssuedID, through its codec; a key that expired before its content was
  seen names no issued event, so nothing is said and its token is dropped at once (blocker 6).
- issue builds the joining node's first tunnel from placement (the hub, its endpoint, the node's address) and the hub's
  record (its tunnel key) and passes it to enrolment-token; no hub, or a hub with no key or endpoint, is a finding of issue
  (ADR 0587 §3, insight of 2026-11-02).
- The assigner takes an add-node only from mesh-enrolment's own bus user (stdio.Callers), never person.mesh-enrolment.
- The watchman raises machine.<node>.node-account-fallback from a node's health report's fall back and clears it when the
  report no longer carries one (ADR 0583 §6, blocker 7).
- mesh-enrolment's ten unit test functions are two table tests now (blocker 8): the repository is back at 816.

(issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:44 +01:00
jschoubben 189e64c486 mesh-enrolment: its integration test, whole on a real bus
The holder's code over a real nats-server bucket with a time to live per key: issue, the operator's yes, redeem as enrol.ace,
the node confirming as the login its key of node-logins names (the stand-in vault writes it at node-account), node-enrolled
and placement-changed said on their subjects, the user enrol.ace composed while the key is live and gone with it, the token
dropped only after the end; and a token never redeemed expires on the bus, is heard, says enrolment-expired and is dropped.
Found by it: a bucket with a time to live per key must keep one revision per key, or the bus expires nothing (nats-server
2.11.17), so the state enrolments declares no history. Paid for: the integration test is paid by merging the builder's
archive-only and root-module integration tests into one table test; the module's ten unit tests by merging the assigner's
three small composition tests into one table (net 2 of 10 paid; the rest is for the lead, the repository being over budget).

(issue 774, 774/S60)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:44 +01:00
jschoubben 1a1d3a1ecc Compose both of a rotating node's logins from the vault's node-logins, so its engine can test-dial the new one
ADR 0583 §1 (insight 2026-11-01) and §2: the grants part reads the vault's node-logins as its named reader and
composes a node user per login, both while the node rotates and the new one alone after rotation-done; each node's
engine reads its own node's key alone (composed by mesh-sdk while the vault serves rotate-node). The part follows the
state by watch, so a rotation begun or ended composes again at once. A second login is judged its node's when the
list is compared (Placed), and a hash changing by a rotation node-logins names is not counted as a writer changing
several hashes at once (sealed.go). The state not on the bus yet is a standing gap: every node logs in as node.<n>,
as today. Tested as a subtest of TestGrants, no new test function. Built against the group's mesh-sdk head 5000774
through a go.work outside the worktree; the pin is the lead's.

(issue 774, 774/S53)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:44 +01:00
jschoubben 288364f28d Compose each enrol.<node> user from the seat mesh-enrolment's keys, so a token issued by the seat can be redeemed
ADR 0587 §4: the grants part composes enrol.<node> from a key at live and the vault's hash, granted only the seat's
redeem and its own inbox; §5: when the key ends the user goes. The part reads mesh-enrolment.enrolments as its named
reader and follows it by watch: a key reaching live composes the user, and its end (by the holder, or by the bus at
its expiry) is a change of the key that takes it out, so no consume of the seat's ending events is added beside the
watch (the subtask named one; the watch already hears every end, the bus's expiry included). From the lease write the
user loses the controller's enrolment subject (§6) and the standing gap of the controller's tokens is dropped; before
it the gap stands. Tested as a subtest of TestGrants, no new test function. Built against the group's mesh-sdk head
5000774 through a go.work outside the worktree; the pin is the lead's.

(issue 774, 774/S52)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:32 +01:00
jschoubben 447bcf3a6b The assigner takes an add-node from mesh-enrolment and writes the joining node with no ask
propose takes the enrolment's warrant {node, address, ask, warrant} and refuses an add-node from any caller but
mesh-enrolment, with no warrant or with a warrant naming another node or address (delivery.AddNodeRefusal); the joining node
counts as having a record for this one change, as ADR 0587 §7 changes ADR 0567 §2. The assigner's process then writes
placement.<node> at once, with no node to answer and no ask raised, and says placement-changed carrying the enrolment's
warrant; the hand-act record of that warrant is the enrolment's, made when it was given. The holder of mesh-enrolment calls
propose with the change, the keys and the warrant read from the router's record of the ask. Tests: rows appended to the
seat's verbs test and a case to the whole-seat integration test; none added, so none to pay for (the budget is paid in
774/S60).

(issue 774, 774/S59)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:45:12 +01:00
jschoubben 07d26adcd3 mesh-enrolment: read the group's mesh-sdk contract instead of its own copies of the shapes
The seat, the enrolment state, redeem and its proof, the answer the vault signs, and the vault's two verbs' answers are now
mesh-sdk's (catalogue and delivery), which the vault, the grants part and the engine read too; the proof is the contract's
RedeemProof, the answer carries the login, account, certificate and the vault's signing key (read from its state signers),
and the bucket's values are written through the contract's codecs. The expiry event names the issued event by message id as
well as by the sequence, which a bundle cannot learn. The module builds against the group's mesh-sdk head through a go.work
outside the tree; the lead re-pins go.mod before the push. Tests merged into the same functions; none added.

(issue 774, 774/S54-S58)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:46 +01:00
jschoubben 02214cf633 mesh-enrolment: revoke, the expiry marker and the drop of the token
revoke ends an enrolment at any stage without an Approve and records it as an act done by hand (the vault retires an
account minted for a redeem under way); the bus's expiry marker of a key at live says enrolment-expired with the issued
event; the durable consumer of the seat's own ending events asks the vault to drop the token and its hash, acknowledging
only once the vault answers, never before an end. No event or key holds a token (ADR 0587 §5, §8). Run starts the watch,
the consumer and the operator's answers. Tests added: two table-style tests.

(issue 774, 774/S58)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:46 +01:00
jschoubben f904ff425e mesh-enrolment: confirm closes an enrolment on the node's own call
Only the node dialled as node.<node> confirms (the runtime's proof of the caller is the confirmation, ADR 0587 §6); the
holder then says node-enrolled with the key's content and deletes the address key (while it still names the node) and the
node's key. A stop between the event and the deletes is finished by calling confirm again; the event keeps one message.
release now answers what it could not delete. Tests added: one table-style test.

(issue 774, 774/S57)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:46 +01:00
jschoubben 89370b6f5c mesh-enrolment: redeem, the joining machine's one call
redeem answers only enrol.<node> as the runtime proves it, checks the proof against the identity key presented, spends the
token once by compare-and-set (the keys lose their expiry), asks the vault for the node's account and certificate, writes
the node into placement on the issue's warrant, answers once the bus node loaded node.<node>, signed by the vault; the
same keys called again finish what is not done and answer the same (ADR 0587 §5-§7). The proof is mesh-host's EnrolProof
with the node's name in the token's place, since the seat never holds the secret. Tests added: two table-style tests.

(issue 774, 774/S56)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:46 +01:00
jschoubben 4edd0e526c mesh-enrolment: issue, the operator's ask and the warrant, and again
issue refuses at once naming every finding (a bad name, a name a node record, a placement key or an open enrolment
holds, an expiry past a day, any call before the lease write), reserves the lowest free address by creating its key,
writes the enrolment at asked and asks the operator (ADR 0587 §4). On the yes the warrant is recorded, the vault
makes the token and both keys go live with the bus's expiry; a no or an expired ask takes both keys and says
enrolment-refused. Tests added: three table-style tests; none deleted here (paid with 774/S59's merges).

(issue 774, 774/S55)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:46 +01:00
jschoubben 5d5ee69775 Add the module mesh-enrolment: the seat's skeleton, its bucket of open enrolments and the tokens verb
The holder of the seat mesh-enrolment (ADR 0587 §1, §2, §8) runs as a bundle on its own account, keeps the bucket
enrolments with a time to live per key, and answers tokens: every open enrolment or one node's with stage, expiry,
ask and revision, the bucket's fill, whether issuing is on and why, and for one node at live the token sealed by
the vault and relayed, kept nowhere. Tests added: two (one table-style test of tokens, the manifest test); the
repository is over its budget, paid by merging tests in the assigner's seat tests in 774/S59's commit.

(issue 774, 774/S54)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:44:46 +01:00
jschoubben 17ef1d577d Pin mesh-sdk at c21d8fd, the removal group's head, so every member builds the same contract (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-delivery@9125fcc71206
mesh/delivery-group group feat/l3-removal merged: every member is merged
mesh/check failed: mesh-build-go: the check of novox/mesh-catalog#590: the ask mvyhumdm860c7f's step on shanks did not end within 30m0s of its start |…
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:53:42 +01:00
jschoubben da27cd1593 Name the lease write's earlier steps by their issues, not milestone labels
(issue 774, 774/S14)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:51:06 +01:00
jschoubben aa6a4e7a59 The indexer's closed seat set counts fifty-seven with mesh-grants
ADR 0582 clause 1 adds the seat mesh-grants to mesh-sdk's set; the indexer's guard of the closed set counts it.

(issue 774, 774/S1)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:51:06 +01:00
jschoubben 8bf45fbafe Integration test: mesh-grants writes the lease and hands it back on a real bus
ADR 0582 "How it is checked": against an embedded bus, a stand-in controller holds the lease in a bucket with an
expiry (2 s standing in for 15 s, through grants.LeaseExpiry, so the wait past it is seconds), with stand-in
node-service-manager and bus-tool seats (the expiry tool acting on the real bucket's stream, as nats-tools'
SetBucketExpiry does). On the warrant the key holder names mesh-grants, the expiry is gone, the controller's
create of the key is refused, and the lease still reads handed away past the old expiry; on the hand-back's
warrant the expiry is back and the controller takes the lease again. mesh-grants requires nats-server for it,
at mesh-sdk's version.

Budget: TestTheLeaseWriteOnTheBus (new, integration) is paid by merging the checker's
TestTheCheckerAwaitsTheAssignersComposition into TestTheCheckersLineSaysTheAssignersComposition as its first case.

(issue 774, 774/S23)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:40:00 +01:00