Bus node: follow the grants part's sealed user list after the operator's take-over, never both sources; hand the vault the loaded hashes (issue 1945)
#145
Merged
mesh-toolsmerged 7 commits from feat/1945-user-list-switch into main2026-10-26 12:21:55 +00:00
Group feat/1945-user-list-switch, one Approve. On the node holding mesh-broker the node-engine follows mesh-grants' key user-list.sealed once it says follow=grants: opens the sealed object with its own key, checks the digest, keeps the controller-user and half-dropped guards, writes the file 0600 only on a change, reports follows: grants; writes nothing until the key has been heard (the last key is kept on the machine), retries a refused watch, never writes the controller's list on an apply in grants mode, and refuses an apply when the list on the machine cannot be read (the file stays). It also hands the vault, sealed to the vault's key, the hashes of the loaded list (ADR 0550 clause 4), re-asking the vault's key until it has one. Try try-1793015653572825118 passed.
Group feat/1945-user-list-switch, one Approve. On the node holding mesh-broker the node-engine follows mesh-grants' key user-list.sealed once it says follow=grants: opens the sealed object with its own key, checks the digest, keeps the controller-user and half-dropped guards, writes the file 0600 only on a change, reports follows: grants; writes nothing until the key has been heard (the last key is kept on the machine), retries a refused watch, never writes the controller's list on an apply in grants mode, and refuses an apply when the list on the machine cannot be read (the file stays). It also hands the vault, sealed to the vault's key, the hashes of the loaded list (ADR 0550 clause 4), re-asking the vault's key until it has one. Try try-1793015653572825118 passed.
Reviewed-by: review-switch@shanks at e88ddab2417069b97fe3fcc14a260dad6fd68d31
The vault holds no value for the controller's own account, the node accounts and the enrolled devices, so it cannot
hash them, and take-over waits on their hashes. The bus node's engine is the one part that has them, in the list the
bus loaded. Its follower of the user list now puts every loaded user's bcrypt hash in the bus module's have, sealed to
the vault's own key (mesh-sdk delivery.AccountsHanded), so of every reader of the have bucket the vault alone opens
it; the clear part says only the list's digest, the key's fingerprint and how many users. The vault's key is heard from
its answer to node-key, which the engine already asks at start; a first or changed key looks at the list again. It is
sealed again only when the list on the machine or the vault's key changes; a refused list hands the list kept on the
machine. The vault takes from it only the users it holds no value for.
Also: the user list follower's test sent its later puts before the watch had handed the keys there before it, so two
of its cases failed on main whenever the goroutine started late; the feed now says when the watch began.
On the node whose compositions carry the bus's list, the engine also watches mesh-grants' key user-list.sealed. While it
says follow grants, the object it names is read by name, opened with this machine's sealing key, checked against the
key's digest and the guard, and written to the same file at 0600 only when its digest differs; the bus reloads it
itself. The have says which list is followed and this machine's sealing key, which the grants part seals to.
The follower's test waits for its watch to catch up before a later put, which raced under load. Pins mesh-sdk 175a5567.
Merges fix/1945-adopt-hashes 1a190090. A new follower (one per link) waits for the grants part's key to catch up before
its first write, so a restart after take-over never writes the controller's list first. A refused or ended watch is
tried again on link.Backoff, the have says why it is not watched, and the key last heard, kept beside the node's state,
holds meanwhile. While the grants part's list is followed, the bus module's composition is applied with the list on the
machine in place of the controller's. The grants list is never handed to the vault. Pins mesh-sdk c2a410bb.
Deliverynovox/mesh-host@e88ddab24170 — merged since 2026-10-26T12:21:58Z
Source — source.main moved to novox/mesh-host@7f04d12047. Whether it reached the machines is mesh-delivery-view's, derived from source.main, the wants and the haves.
2026-10-26 12:18 (new) → proposed (announced): novox/mesh-host#145's head announced
2026-10-26 12:18 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-10-26 12:21 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@e88ddab24170` — **merged** since 2026-10-26T12:21:58Z
**Source** — source.main moved to novox/mesh-host@7f04d1204765. Whether it reached the machines is `mesh-delivery-view`'s, derived from `source.main`, the wants and the haves.
**Group** `feat/1945-user-list-switch`: novox/mesh-catalog@c9414b1c9856, novox/mesh-controller@09d2cc5056ef, novox/mesh-delivery@5c76c9d3ac84, novox/mesh-host@e88ddab24170, novox/mesh-sdk@c2a410bb6376
**Transitions**
- 2026-10-26 12:18 (new) → proposed (announced): novox/mesh-host#145's head announced
- 2026-10-26 12:18 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-10-26 12:21 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: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Group feat/1945-user-list-switch, one Approve. On the node holding mesh-broker the node-engine follows mesh-grants' key user-list.sealed once it says follow=grants: opens the sealed object with its own key, checks the digest, keeps the controller-user and half-dropped guards, writes the file 0600 only on a change, reports follows: grants; writes nothing until the key has been heard (the last key is kept on the machine), retries a refused watch, never writes the controller's list on an apply in grants mode, and refuses an apply when the list on the machine cannot be read (the file stays). It also hands the vault, sealed to the vault's key, the hashes of the loaded list (ADR 0550 clause 4), re-asking the vault's key until it has one. Try try-1793015653572825118 passed.
Reviewed-by: review-switch@shanks at
e88ddab241Delivery
novox/mesh-host@e88ddab24170— merged since 2026-10-26T12:21:58ZSource — source.main moved to novox/mesh-host@7f04d12047. Whether it reached the machines is
mesh-delivery-view's, derived fromsource.main, the wants and the haves.Group
feat/1945-user-list-switch: novox/mesh-catalog@c9414b1c98, novox/mesh-controller@09d2cc5056, novox/mesh-delivery@5c76c9d3ac, novox/mesh-host@e88ddab241, novox/mesh-sdk@c2a410bb63Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition of this change up to its merge:git log --notes=mesh-plan.mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.
mesh-delivery: the operator's Approve of this pull request's merge into main is ended: a head it covers moved on: the heads it covers are no longer the group's heads. Nothing is merged on it. The operator is asked again once the checks pass at the new head.