Why: hq ADR 0328 (accepted with the operator's Approve, 2026-10-12) supersedes ADR 0325: a module asks for a library by name and use; the mesh keeps each library under a group mesh-<library> and gives each joining module a mesh-<module> user whose primary group is its library's. Issue 567.
What (node-engine half, contract 6 link.LibraryContract; nothing new happens until the controller sends contract-6 fields):
groups, identities (accounts with home /nonexistent, so a primary-group change never regroups files; a / home is moved off without -m first), libraries per machine; refused: 0/root groups, clashes, nested roots, the machine's own trees. Alpine via adduser/addgroup/deluser/delgroup.
Keep without a warrant only an empty root or one in shape, root and a bounded sample beneath (fails closed); sub-paths holding old data wait; new/empty sub-paths made with the root's owner, the group, g+rwx, setgid.
Take act on the operator's warrant (library:<name>): chgrp + g+rw / g+rwx+s recursively, owner/owner bits/others never changed, no link, same filesystem, bounded and resumable; hardlinked files regrouped when an account owns them, root-owned ones left (counts said); setuid/setgid cleared on regrouped files and never restored.
Disk not there / unnamed filesystem: containers held, waits reason disk/unknown; tool runner joins the library group.
ADR 0325's ACL code, mask repair and watch removed. No ADR 0325 take or identity ever ran anywhere (its controller half was never merged), so no ACL cleanup is needed.
Old-shape declarations applied exactly as before.
Known limits (from review): a ZFS pool not imported on first sight is not known to be missing until it mounts (then the move check makes it wait); an apply arriving in the instant before a take walk registers waits for that walk; the walk's own registration has no direct test.
Checked: merge-check.sh green (whole suite under -race); as root in a user namespace only issue 358's namespace-only failure. Independent adversarial review over four rounds, every finding fixed with a failing-first test (mutation-checked); final verdict APPROVE ("would run it as root against the 12k-file library").
Delivery effects: node-engine restarts on ace, g14, novox, shanks; nothing changes on any folder until the controller half is delivered.
**Why**: hq ADR 0328 (accepted with the operator's Approve, 2026-10-12) supersedes ADR 0325: a module asks for a library by name and use; the mesh keeps each library under a group `mesh-<library>` and gives each joining module a `mesh-<module>` user whose primary group is its library's. Issue 567.
**What** (node-engine half, contract 6 `link.LibraryContract`; nothing new happens until the controller sends contract-6 fields):
- `groups`, `identities` (accounts with home `/nonexistent`, so a primary-group change never regroups files; a `/` home is moved off without `-m` first), `libraries` per machine; refused: 0/root groups, clashes, nested roots, the machine's own trees. Alpine via adduser/addgroup/deluser/delgroup.
- Keep without a warrant only an empty root or one in shape, root and a bounded sample beneath (fails closed); sub-paths holding old data wait; new/empty sub-paths made with the root's owner, the group, g+rwx, setgid.
- Take act on the operator's warrant (`library:<name>`): chgrp + g+rw / g+rwx+s recursively, owner/owner bits/others never changed, no link, same filesystem, bounded and resumable; hardlinked files regrouped when an account owns them, root-owned ones left (counts said); setuid/setgid cleared on regrouped files and never restored.
- Disk not there / unnamed filesystem: containers held, waits reason `disk`/`unknown`; tool runner joins the library group.
- ADR 0325's ACL code, mask repair and watch removed. No ADR 0325 take or identity ever ran anywhere (its controller half was never merged), so no ACL cleanup is needed.
- Old-shape declarations applied exactly as before.
**Known limits** (from review): a ZFS pool not imported on first sight is not known to be missing until it mounts (then the move check makes it wait); an apply arriving in the instant before a take walk registers waits for that walk; the walk's own registration has no direct test.
**Checked**: `merge-check.sh` green (whole suite under `-race`); as root in a user namespace only issue 358's namespace-only failure. Independent adversarial review over four rounds, every finding fixed with a failing-first test (mutation-checked); final verdict APPROVE ("would run it as root against the 12k-file library").
**Delivery effects**: node-engine restarts on ace, g14, novox, shanks; nothing changes on any folder until the controller half is delivered.
ADR 0328 supersedes ADR 0325: shared data is a library with one home, kept usable by
its group rather than by ACL entries. The engine now makes each library's group and
each module's user in its libraries' groups; keeps a library's root and requested
sub-paths in its group with group write and setgid on its home, making missing
sub-paths as the root is; waits for the operator's word before touching a root that
holds data, and on the take act walks it by descriptor (no link, same filesystem,
bounded and resumed, files with other names left); holds the containers of a library
whose disk or mount is not there; and puts the tool runner's account in the group.
The ACL keeping, mask repair and watch are gone; an access is again as before ADR 0325.
A root in the group with setgid was kept without a warrant even when the data beneath
it was not in the group, which §7 says the operator is asked about. A root is now
kept without asking only when it is empty, or it and a bounded sample beneath it are
all in the group with group write (and setgid on folders). On busybox, an account the
mesh made is given its added groups with addgroup and delgroup; a change of its
primary group is refused, saying why.
usermod --gid regroups every file under an account's home, and accounts were made
with / as home: they now have /nonexistent, and an older account's home is moved
there (without its files) before its group changes. A sub-path asked for later that
holds data not in the group waits for the operator as the root would. The take act
gives the group to hardlinked files an account owns (the *arr apps' downloads) and
leaves root's; a file's setuid or setgid bit the regroup takes is never put back, so
no setgid file hands out the library's group; each is counted. An apply no longer
waits for a running take walk.
mesh/merge-gate head 281837847e03 merged into trunk 303d4037e84e — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
An earlier change dropped alpine's RemoveAccount and RemoveGroup, so alpine made no
module identity at all. The sample beneath a root in shape had no test of its own.
And an apply that found the library's lock held read the library as kept even when
what held it was a take act about to be refused for a missing disk; it now does so
only while a take walk is running, and otherwise waits and judges as always.
Deliverynovox/mesh-host@281837847e03 — delivered since 2026-10-12T20:32:10Z
Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
tier 0: mesh-host
ace: receives mesh-host
g14: receives mesh-host
novox: receives mesh-host
shanks: receives mesh-host
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
Machines
mesh-host on ace: passed — healthy 3 times over 2m39s
mesh-host on the rest running it: sent
Transitions
2026-10-12 20:23 (new) → proposed (announced): novox/mesh-host#95's head announced
2026-10-12 20:26 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
2026-10-12 20:26 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-12 20:28 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-12 20:28 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
2026-10-12 20:32 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says
The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-host@281837847e03` — **delivered** since 2026-10-12T20:32:10Z
**Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step
- tier 0: mesh-host
- ace: receives mesh-host
- g14: receives mesh-host
- novox: receives mesh-host
- shanks: receives mesh-host
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
**Machines**
- mesh-host on ace: passed — healthy 3 times over 2m39s
- mesh-host on the rest running it: sent
**Transitions**
- 2026-10-12 20:23 (new) → proposed (announced): novox/mesh-host#95's head announced
- 2026-10-12 20:26 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
- 2026-10-12 20:26 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-12 20:28 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-12 20:28 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
- 2026-10-12 20:32 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says
The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Merge check at 28183784
Judged: head 281837847e03 merged into trunk 303d4037e84e; read beside it: mesh-catalog 9c9a76d220f8, mesh-controller 13711fb5b91b, mesh-controller-main 13be75eb65c0, mesh-tools 954e1a53eef9
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
mesh/repo-check: PASS — its merge-check.sh passed
Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
tier 0: mesh-host
ace: receives mesh-host
g14: receives mesh-host
novox: receives mesh-host
shanks: receives mesh-host
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
read by no module's build: cmd/mesh-host/kept_test.go, cmd/mesh-host/libraries_test.go, cmd/mesh-host/units_test.go, internal/apply/contract_test.go, internal/apply/identities_test.go, internal/apply/kept_test.go, internal/apply/library_test.go, internal/apply/oldshape_test.go, internal/system/identities_test.go
Run by the build seat on g14 as build-1791836608800982746 (builds --log build-1791836608800982746).
--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T20:19:09Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-12T20:19:09Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): mesh-host
notes:
- cmd/mesh-host/kept_test.go, cmd/mesh-host/libraries_test.go, cmd/mesh-host/units_test.go, internal/apply/contract_test.go, internal/apply/identities_test.go, internal/apply/kept_test.go, internal/apply/library_test.go, internal/apply/oldshape_test.go and internal/system/identities_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.015s
ok github.com/novox/mesh-host/cmd/mesh-host 5.422s
ok github.com/novox/mesh-host/examples 1.016s
ok github.com/novox/mesh-host/internal/accounts 1.379s
ok github.com/novox/mesh-host/internal/apply 2.619s
ok github.com/novox/mesh-host/internal/bootstrap 1.368s
ok github.com/novox/mesh-host/internal/bundle 1.016s
ok github.com/novox/mesh-host/internal/declaration 1.056s
ok github.com/novox/mesh-host/internal/firewall 1.731s
ok github.com/novox/mesh-host/internal/identity 1.063s
ok github.com/novox/mesh-host/internal/image 1.015s
ok github.com/novox/mesh-host/internal/inventory 1.047s
ok github.com/novox/mesh-host/internal/link 10.510s
ok github.com/novox/mesh-host/internal/liveness 2.079s
ok github.com/novox/mesh-host/internal/meshcli 1.568s
ok github.com/novox/mesh-host/internal/network 1.048s
ok github.com/novox/mesh-host/internal/outward 1.036s
ok github.com/novox/mesh-host/internal/profile 1.520s
ok github.com/novox/mesh-host/internal/reachable 1.024s
ok github.com/novox/mesh-host/internal/store 1.081s
ok github.com/novox/mesh-host/internal/system 1.015s
ok github.com/novox/mesh-host/internal/tunnel 1.017s
ok github.com/novox/mesh-host/internal/units 1.011s
ok github.com/novox/mesh-host/internal/upgrade 1.039s
ok github.com/novox/mesh-host/internal/witness 1.412s
ok github.com/novox/mesh-host/packaging 1.011s
? github.com/novox/mesh-host/rootsearch [no test files]
ok github.com/novox/mesh-host/validate 1.011s
**Merge check** at `28183784`
Judged: head `281837847e03` merged into trunk `303d4037e84e`; read beside it: mesh-catalog `9c9a76d220f8`, mesh-controller `13711fb5b91b`, mesh-controller-main `13be75eb65c0`, mesh-tools `954e1a53eef9`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step
- tier 0: mesh-host
- ace: receives mesh-host
- g14: receives mesh-host
- novox: receives mesh-host
- shanks: receives mesh-host
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
- read by no module's build: cmd/mesh-host/kept_test.go, cmd/mesh-host/libraries_test.go, cmd/mesh-host/units_test.go, internal/apply/contract_test.go, internal/apply/identities_test.go, internal/apply/kept_test.go, internal/apply/library_test.go, internal/apply/oldshape_test.go, internal/system/identities_test.go
Run by the build seat on g14 as `build-1791836608800982746` (`builds --log build-1791836608800982746`).
```
--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T20:19:09Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-12T20:19:09Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): mesh-host
notes:
- cmd/mesh-host/kept_test.go, cmd/mesh-host/libraries_test.go, cmd/mesh-host/units_test.go, internal/apply/contract_test.go, internal/apply/identities_test.go, internal/apply/kept_test.go, internal/apply/library_test.go, internal/apply/oldshape_test.go and internal/system/identities_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.015s
ok github.com/novox/mesh-host/cmd/mesh-host 5.422s
ok github.com/novox/mesh-host/examples 1.016s
ok github.com/novox/mesh-host/internal/accounts 1.379s
ok github.com/novox/mesh-host/internal/apply 2.619s
ok github.com/novox/mesh-host/internal/bootstrap 1.368s
ok github.com/novox/mesh-host/internal/bundle 1.016s
ok github.com/novox/mesh-host/internal/declaration 1.056s
ok github.com/novox/mesh-host/internal/firewall 1.731s
ok github.com/novox/mesh-host/internal/identity 1.063s
ok github.com/novox/mesh-host/internal/image 1.015s
ok github.com/novox/mesh-host/internal/inventory 1.047s
ok github.com/novox/mesh-host/internal/link 10.510s
ok github.com/novox/mesh-host/internal/liveness 2.079s
ok github.com/novox/mesh-host/internal/meshcli 1.568s
ok github.com/novox/mesh-host/internal/network 1.048s
ok github.com/novox/mesh-host/internal/outward 1.036s
ok github.com/novox/mesh-host/internal/profile 1.520s
ok github.com/novox/mesh-host/internal/reachable 1.024s
ok github.com/novox/mesh-host/internal/store 1.081s
ok github.com/novox/mesh-host/internal/system 1.015s
ok github.com/novox/mesh-host/internal/tunnel 1.017s
ok github.com/novox/mesh-host/internal/units 1.011s
ok github.com/novox/mesh-host/internal/upgrade 1.039s
ok github.com/novox/mesh-host/internal/witness 1.412s
ok github.com/novox/mesh-host/packaging 1.011s
? github.com/novox/mesh-host/rootsearch [no test files]
ok github.com/novox/mesh-host/validate 1.011s
```
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.
Why: hq ADR 0328 (accepted with the operator's Approve, 2026-10-12) supersedes ADR 0325: a module asks for a library by name and use; the mesh keeps each library under a group
mesh-<library>and gives each joining module amesh-<module>user whose primary group is its library's. Issue 567.What (node-engine half, contract 6
link.LibraryContract; nothing new happens until the controller sends contract-6 fields):groups,identities(accounts with home/nonexistent, so a primary-group change never regroups files; a/home is moved off without-mfirst),librariesper machine; refused: 0/root groups, clashes, nested roots, the machine's own trees. Alpine via adduser/addgroup/deluser/delgroup.library:<name>): chgrp + g+rw / g+rwx+s recursively, owner/owner bits/others never changed, no link, same filesystem, bounded and resumable; hardlinked files regrouped when an account owns them, root-owned ones left (counts said); setuid/setgid cleared on regrouped files and never restored.disk/unknown; tool runner joins the library group.Known limits (from review): a ZFS pool not imported on first sight is not known to be missing until it mounts (then the move check makes it wait); an apply arriving in the instant before a take walk registers waits for that walk; the walk's own registration has no direct test.
Checked:
merge-check.shgreen (whole suite under-race); as root in a user namespace only issue 358's namespace-only failure. Independent adversarial review over four rounds, every finding fixed with a failing-first test (mutation-checked); final verdict APPROVE ("would run it as root against the 12k-file library").Delivery effects: node-engine restarts on ace, g14, novox, shanks; nothing changes on any folder until the controller half is delivered.
Delivery
novox/mesh-host@281837847e03— delivered since 2026-10-12T20:32:10ZDelivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
Machines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
28183784Judged: head
281837847e03merged into trunk303d4037e84e; read beside it: mesh-catalog9c9a76d220f8, mesh-controller13711fb5b91b, mesh-controller-main13be75eb65c0, mesh-tools954e1a53eef9mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step
Run by the build seat on g14 as
build-1791836608800982746(builds --log build-1791836608800982746).