The bus node writes the newest signed bus user list by itself, without a want (ADR 0547 bridge, issue 1945) #143

Merged
mesh-tools merged 2 commits from fix/1945-user-list-follows into main 2026-10-25 21:16:07 +00:00
Member

ADR 0547 clause 7/8 (accepted by the operator 2026-10-25), the bridge, with the controller unchanged. Tonight the bus user list reached the bus only by the operator's hand step (three times): the list travels inside the nats module's composition, applied only with a want of nats, so a new account or grant composed by the controller never reached the bus; twice that left a part crash-looping on a grant not yet loaded.

  • The node agent watches the node's compositions and takes the newest one signed by a trusted key (the same VerifyComposed and key set as an apply) whose node and module match its key and that carries <module>.bus-users.
  • When that list's digest differs from the file on disk, it writes the file alone through the apply's file step (0600), reads it back and records it as the module's. The bus module's entrypoint already reloads the server when the file changes. It needs no want, never goes back to an older list, and looks again after every apply of the bus module.
  • Guards: a list sent by digest is fetched and verified first; a list without the user controller, or with fewer than half the users of the file on disk, is refused: the file stays, the have says why.
  • It writes what it did into the bus module's have (user-list: wanted vs digest, users, source, error), read with mesh-delivery-view.node_haves {node: novox, module: nats}; nats.nats_user_can reports in_force once the bus reloaded.

Group with mesh-sdk's PR of the same branch name (this pins its head 0be0ffd). Merges cleanly with #142 (git merge-tree).

Checked: go test -race ./... and go vet -tags integration ./...; try-1792962013247173089 passed (unit and integration). Net zero new top-level tests. This is the node-engine on every node; the follower acts only where the composition carries a bus user list (the bus node).

Review: round 1 blocked on the user count (a by-digest list counted 0 users); fixed with the node/module match and the shrink guard in round 2.

Reviewed-by: review-bridge@shanks at 5e71ab6ae6

ADR 0547 clause 7/8 (accepted by the operator 2026-10-25), the bridge, with the controller unchanged. Tonight the bus user list reached the bus only by the operator's hand step (three times): the list travels inside the nats module's composition, applied only with a want of nats, so a new account or grant composed by the controller never reached the bus; twice that left a part crash-looping on a grant not yet loaded. - The node agent watches the node's compositions and takes the newest one signed by a trusted key (the same `VerifyComposed` and key set as an apply) whose node and module match its key and that carries `<module>.bus-users`. - When that list's digest differs from the file on disk, it writes the file alone through the apply's file step (0600), reads it back and records it as the module's. The bus module's entrypoint already reloads the server when the file changes. It needs no want, never goes back to an older list, and looks again after every apply of the bus module. - Guards: a list sent by digest is fetched and verified first; a list without the user `controller`, or with fewer than half the users of the file on disk, is refused: the file stays, the have says why. - It writes what it did into the bus module's have (`user-list`: wanted vs digest, users, source, error), read with `mesh-delivery-view.node_haves` {node: novox, module: nats}; `nats.nats_user_can` reports in_force once the bus reloaded. Group with mesh-sdk's PR of the same branch name (this pins its head 0be0ffd). Merges cleanly with #142 (git merge-tree). Checked: go test -race ./... and go vet -tags integration ./...; try-1792962013247173089 passed (unit and integration). Net zero new top-level tests. This is the node-engine on every node; the follower acts only where the composition carries a bus user list (the bus node). Review: round 1 blocked on the user count (a by-digest list counted 0 users); fixed with the node/module match and the shrink guard in round 2. Reviewed-by: review-bridge@shanks at 5e71ab6ae6127f385aaf0903723ac80fdf0f2edf
mesh-tools added 2 commits 2026-10-25 21:04:55 +00:00
The list reached the bus only through a want of the bus module, which nobody writes (ADR 0539 §15): on 2026-10-25 it
stood from 11:23 to 20:50 and was put back by hand. The node agent now follows its composition bucket for the newest
signed composition carrying the user list, writes that file alone through the apply's file step when it differs
(mode 0600, read back, recorded as the module's), moves nothing that reads it (the bus module's entrypoint reloads the
server in place), never goes back to an older list, looks again after every apply of the bus module, and says source,
revision, wanted and written digests, users and any error in the bus module's have (mesh-sdk UserListWritten).

Read with mesh-delivery-view.node_haves (module nats); the bus's own nats_user_can says in_force once it reloaded.
Count the bus's users from the list a digest names, take only a composition of this node and module, and refuse a list that would lock the mesh out (review of the bridge, ADR 0547 §8)
mesh/delivery merged: source.main moved to novox/mesh-host@d75c66dc7e27
mesh/delivery-group group fix/1945-user-list-follows merged: every member is merged
mesh/check pass 5e71ab6ae612 on d75c66dc7e27 | unit 303 run, 1077 kept, 5 skipped | integration 306 run, 1079 kept, 4 skipped | over bound: unit, inte…
5e71ab6ae6
B1: the controller sends the user list by digest (over 64 KiB), so its users are counted from the list as resolved, not
from the composition's inline content, which is empty then.
R1: a composition signed for another node or module than its key names is no source.
R2: a list without the controller's own user, or one that drops more than half the users of the file on the machine,
is not written; the have says why and the file on the machine is kept.
Author
Member

Delivery novox/mesh-host@5e71ab6ae612 — merged since 2026-10-25T21:16:09Z

Source — source.main moved to novox/mesh-host@d75c66dc7e. Whether it reached the machines is mesh-delivery-view's, derived from source.main, the wants and the haves.

Group fix/1945-user-list-follows: novox/mesh-host@5e71ab6ae6, novox/mesh-sdk@0be0ffd615

  • composed together: pass — the checker's verdict at the members' heads (e88fd6c706c7)

Transitions

  • 2026-10-25 21:04 (new) → proposed (announced): novox/mesh-host#143's head announced
  • 2026-10-25 21:08 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
  • 2026-10-25 21:08 ready → proposed (trunk moved): out of date: judged against trunk 5f16cf507a, the trunk is now e196058f6fb0; judged again on a recheck or when it is merged
  • 2026-10-25 21:15 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
  • 2026-10-25 21:16 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-host@5e71ab6ae612` — **merged** since 2026-10-25T21:16:09Z **Source** — source.main moved to novox/mesh-host@d75c66dc7e27. Whether it reached the machines is `mesh-delivery-view`'s, derived from `source.main`, the wants and the haves. **Group** `fix/1945-user-list-follows`: novox/mesh-host@5e71ab6ae612, novox/mesh-sdk@0be0ffd6159e - composed together: pass — the checker's verdict at the members' heads (e88fd6c706c7) **Transitions** - 2026-10-25 21:04 (new) → proposed (announced): novox/mesh-host#143's head announced - 2026-10-25 21:08 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 - 2026-10-25 21:08 ready → proposed (trunk moved): out of date: judged against trunk 5f16cf507a0f, the trunk is now e196058f6fb0; judged again on a recheck or when it is merged - 2026-10-25 21:15 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 - 2026-10-25 21:16 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 d75c66dc7e into main 2026-10-25 21:16:07 +00:00
mesh-tools deleted branch fix/1945-user-list-follows 2026-10-25 21:16:08 +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-host#143