Commit Graph
778 Commits
Author SHA1 Message Date
jochen 72a6d062dc Let SIGTERM cancel a running pass and bound nfs-server's clean-up at 10 s, so a hanging command cannot keep the export file past the stop timeout 2026-10-09 14:43:32 +02:00
jochen 87d6a5549c Check every share against where the backup seat keeps its data on every pass, so the hour a clean look is believed cannot hide a new repository 2026-10-09 14:43:32 +02:00
mesh-admin 9e4267bf28 Merge pull request 'mesh-cli at go/v0.1.9, and the unannounced pull request's number (hq ADR 0272, issue 347)' (#160) from feat/272-the-tagged-sdk-and-the-pull-requests-number into main 2026-10-09 11:54:32 +00:00
jochen 645961a87b mesh-cli at go/v0.1.9, and the unannounced pull request's number (hq ADR 0272, issue 347)
mesh/merge-gate pass: builds mesh-cli, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
- mesh-cli pins the SDK at its tag now that go/v0.1.9 exists, instead of a
  pseudo-version of the branch it was cut from.
- mesh-delivery's stalled line for a head the forge never announced
  carries the pull request's number, which the controller's condition
  names in its headline.
2026-10-09 13:41:59 +02:00
jochen 1dca181e03 Put every repository path through the segment helper, gitea_open_pulls's included
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
After merging #159 (issue 347): every /repos/<owner>/<name> the client builds
now takes its owner and name through one helper, whether they came from a
caller or from the forge's answer; the open-pulls fixture's shas are commits.
2026-10-09 13:41:35 +02:00
jochen 46fe6fff64 Merge remote-tracking branch 'origin/main' into fix/the-forge-tools-never-weaken-protection
# Conflicts:
#	modules/gitea/client.ts
2026-10-09 13:41:02 +02:00
mesh-admin ceab9a03b7 Merge pull request 'Announce a pull request opened after its push, and say one nobody checks (hq issue 347)' (#159) from fix/347-a-pull-request-opened-after-its-push-is-never-checked into main 2026-10-09 11:38:48 +00:00
mesh-admin 8ff8197a32 Merge pull request 'mesh-cli: the operator's command on every node, nox in zsh (hq ADR 0272)' (#156) from feat/272-mesh-cli into main 2026-10-09 11:38:44 +00:00
jochen 5eab586116 Put no caller's text in a forge path unjudged: every segment checked, every sent path checked again
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
The confirmation review of 2026-10-09 found the dedicated tools building paths
from caller text: a branch named .. became DELETE /repos/o/r/ (the repository),
a file path ../../../../version reached /api/v1/version, and a sha with
?sudo= set a status as any user. Each caller-supplied segment now goes through
one helper refusing empty, . and .. and / ? # %; a sha must be a commit; and
the client judges every path once more as it sends it: no dot or empty
segment, encoded or not, and only the allow-listed query keys.
2026-10-09 13:35:15 +02:00
jochen 32d55beb47 Say a pull request nobody checks as a delivery held, owned by mesh-delivery (hq issue 347)
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
The confirmation review asked for the delivery domain's own vocabulary
instead of a provider standing. A gitea health check would need a `tools`
list (turning gitea's tools into seat verbs), a check of another kind
beside it, and would hold the forge's own deliveries at the first-node
gate; so the delivery's owner says it, with the word it already has.

- gitea_open_pulls (gitea): every open pull request from the forge itself,
  with what its base requires and the statuses its head has.
- mesh-delivery asks it every five minutes and keeps its own first-seen
  time per head (the forge's update time moves on every comment). A head
  on a base requiring mesh/merge-gate OR mesh/repo-check (hq requires only
  the second) with neither for ten minutes is a `stalled` line in state
  `unannounced`, which the controller raises as delivery.<id>.stalled (D14);
  H2 has nothing to close. Once announced it is a delivery of its own.
- The gitea-side watcher and its provider standing are gone.
2026-10-09 13:14:36 +02:00
jochen bcc6b3f52a gitea README: how to see a late-opened pull request announced (hq issue 347)
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
2026-10-09 12:58:13 +02:00
jochen 213950e334 Merge remote-tracking branch 'origin/main' into fix/the-forge-tools-never-weaken-protection
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
2026-10-09 12:57:16 +02:00
jochen ea772a9cce Let gitea_api send only the query keys it needs, never sudo, and never approve a pull request
The confirmation review of 2026-10-09 measured GET /user?sudo=… answer the
operator's own account: the query went to the forge unjudged, and the tools'
token is the site administrator's, so any caller could act as any user.
gitea_api now sends only allow-listed query keys and refuses sudo in any case,
and a review that approves or requests changes is the operator's.
2026-10-09 12:57:16 +02:00
jochen 67bce7e189 Announce a pull request opened after its push, and say one nobody checks (hq issue 347)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
The announcer read only the repositories whose update time moved since its
last pass, less a minute. Opening, reopening or retargeting a pull request
does not move a repository's update time, so a pull request opened more
than a minute after its push was never announced, never checked, and
nothing said so (mesh-sdk #14, 2026-10-09).

- Each pass also looks at every repository holding a pull request updated
  since the floor, by the forge's own search; every ten minutes, or when the
  search fails, at every repository.
- Every five minutes the forge is asked for every open pull request; one on
  a base requiring mesh/merge-gate whose head has had none for ten minutes
  is said as the forge failing that pull request's merge check (a provider
  standing, ADR 0224), which the controller keeps as a condition, and is
  recovered when a check arrives or it closes.
- The client refuses a pull request or issue number that is not one before
  asking the forge (a misnamed tool argument sent pulls/NaN).
2026-10-09 12:55:09 +02:00
mesh-admin e9f5727cad Merge pull request 'xorg: list the server's windows, and cut a window's screenshot from the screen (hq issue 345)' (#154) from fix/xorg-windows-and-window-screenshot into main 2026-10-09 10:51:01 +00:00
mesh-admin 1833045c33 Merge pull request 'picom: the backend and damage tracking are each machine's settings (hq issue 345)' (#157) from feat/picom-backend-and-damage-settings into main 2026-10-09 10:50:58 +00:00
mesh-admin 65308a7bce Merge pull request 'betterbird: a module for the mail and calendar client (hq issue 345)' (#155) from feat/betterbird-module into main 2026-10-09 10:50:54 +00:00
jochen 6f1c559d93 mesh-cli: say an outcome not known, and hold the module to placing one file (review of hq ADR 0272)
mesh/merge-gate pass: builds claude-code, route-adapter, new: modules/mesh-cli → ace, g14, novox, shanks; no bus step; every machine composes with the chan…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
A line whose answer did not come in time is said as possibly run, with
its call, never as nothing having run. A test holds the module to one
archive resource unpacking one compiled Go bundle into /usr/local/bin.
The README says the MESH_VERB refusal is a courtesy and the controller is
the gate. SDK pinned at the go/cli branch's newest commit until go/v0.1.9.
2026-10-09 12:49:14 +02:00
jschoubben a588eb5983 xdg: keep the folders file trusted, so only the terminal sets the folders (hq issue 346)
mesh/merge-gate pass: builds xdg → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
user-dirs.dirs is shell assignments meant to be sourced by a shell, and a setting is pasted into it as
it is, so a folder of $HOME/x"; <command>; X=" set through the verb would add a command of the
caller's. With the file trusted again, the three folders are set at the controller's terminal alone
until settings are typed and quoted at render.
2026-10-09 12:40:04 +02:00
jschoubben 2d2c694306 betterbird: know a composer by its instance alone, and bound a restart's worst case (review of #155)
mesh/merge-gate pass: builds new: modules/betterbird, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
A composer window whose class is not Betterbird's own was not seen, so a restart could lose its message.
Each step of a restart now has a bound short enough that all of them together, taken in full, stay
under the runtime's 30 s: 3 s for the window tree, 8 s to quit, 3 and 5 s to start, 4 s to see it.
2026-10-09 12:34:06 +02:00
jschoubben ca1bd18de6 picom: keep the configuration trusted, so only the terminal sets the backend (review of #157)
mesh/merge-gate pass: builds picom → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
A setting is pasted into picom.conf as it is, so a backend of xrender"; write-pid-path = "… adds an
option of the caller's: picom would then overwrite any file the operator can write. With the file
trusted, both settings are set at the controller's terminal alone until settings are typed and quoted
(hq issue 346).
2026-10-09 12:33:41 +02:00
jochen ff648ad32b Send the path gitea_api judged, keep a pull request's base, merge only into a guarded branch, and delete only the tools' own idle repositories
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
Follow-ups of the review of 2026-10-09: the escape hatch sent the raw path it
had judged in normalised form; a pull request's base could be moved through
it; the merge tool would merge into a branch no rule guards; a rule named for
a branch could undercut the glob rule guarding it; and the delete tool took
any unguarded repository. It now takes only a repository of the tools' own
user with no protection and no commit in 30 days, named in full.
2026-10-09 12:26:37 +02:00
jschoubben 73f2962bab picom: the backend and damage tracking are each machine's settings (hq issue 345)
mesh/merge-gate pass: builds picom → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
A window mapped but left unpainted, or stale after a workspace switch, is a known failure of picom's glx
backend with damage tracking on some drivers, and the first suspect for the empty calendar reminder on
the workstation. Research 037 already found the backend to depend on the machine's driver. Both become
preferences whose defaults are today's (glx, use-damage true), so nothing changes until a machine is
given xrender or use-damage false to test it.
2026-10-09 12:25:08 +02:00
jschoubben 4fe8958dda xorg: refuse a window that is not on the screen, and keep an inline screenshot within the bus (review of #154)
mesh/merge-gate pass: builds xorg → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
A window that is unmapped or on a hidden workspace was cut from the screen anyway, which shows what is
there instead of it. An inline image of up to 4 MiB could not cross the bus, whose largest message is
1 MiB; it is now held to 700 KiB and halved in size until it fits, saying so.
2026-10-09 12:23:51 +02:00
jschoubben 3629089ced betterbird: never lose an unsent message, never answer an address, and keep the test apart (review of #155)
mesh/merge-gate pass: builds new: modules/betterbird, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
A restart ended Betterbird with whatever a composer held; now it refuses while a Msgcompose window is
open unless discard_unsent says the operator agreed, and answers within the runtime's 30 s. A
preference's value could carry an address, a user%40domain@host URI, a host or the home; those are
masked, recent-folder, account and cloud-file preferences and every add-on preference but the theme's
id are withheld, and a request for everything is refused. A log kept the token after Bearer or Basic.
The reminder test's Betterbird counted as the operator's and left its profile behind when it ended on
its own; it is now left out everywhere and its unit removes the profile when it stops.
2026-10-09 12:22:47 +02:00
jochen e1e043c088 Take out a mounts binary a local build left in this branch
mesh/merge-gate pass: builds claude-code, route-adapter, new: modules/mesh-cli → ace, g14, novox, shanks; no bus step; every machine composes with the chan…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
2026-10-09 12:16:58 +02:00
jochen 2594a24b35 mesh-cli: the operator's command on every node, nox in zsh (hq ADR 0272)
mesh/merge-gate pass: builds claude-code, mounts, route-adapter, new: modules/mesh-cli → ace, g14, novox, shanks; no bus step; every machine composes with …
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
The records send the operator to "the controller's terminal", and on the
control-node nothing answered (hq issue 343). mesh-cli is a module of its
own, assigned to every node and built from its own directory: its program
is unpacked into /usr/local/bin by an archive resource (one file, no link,
no copy), and its zsh contribution makes nox its alias. It holds no
credential: it asks the node-engine on its machine over the SDK's go/cli
socket, and prints the controller's answer with its exit.

The agent module's refusals and README, and the route adapter's README, now
name `mesh-cli settings set …` on the control-node instead of a
mesh-controller command nobody could type; a settings file is passed inline,
since a path would be read where the controller runs.
2026-10-09 12:11:21 +02:00
jschoubben 52a4827983 xorg: list the server's windows, and cut a window's screenshot from the screen (hq issue 345)
mesh/merge-gate pass: builds xorg → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
A reminder window that showed as transparent and empty could not be looked at: the window manager's
list leaves dialogs out and says nothing a compositor goes by, and xwd -id refused a window of a
32-bit visual with BadColor (X_QueryColors). xorg_windows answers every window with its class, and
on request its depth, type, state, role and own opacity; a window's screenshot is now cut from the
whole screen as the compositor shows it, and inline answers the image for a session that cannot
read the machine's files. Declares xorg-xwininfo, which both now use.
2026-10-09 12:07:29 +02:00
jschoubben a7953b969d betterbird: a module for the mail and calendar client, so its windows and preferences can be seen and declared (hq issue 345)
mesh/merge-gate pass: builds new: modules/betterbird, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
A calendar reminder showed as a transparent, empty window on shanks and no tool could say which
window it was or which preferences Betterbird ran with. The module owns one file, the user.js its
setting prefs fills, which its tools place whole into the profile Betterbird uses and never over an
operator's own; it reads named preferences without credentials or accounts, restarts Betterbird under
the user manager so its output reaches the journal, checks what it promises, and shows a reminder on
demand from a throw-away profile so a fix can be proved without the operator's calendar. No package:
betterbird-bin is the AUR's.
2026-10-09 12:07:29 +02:00
jochen 080c113d69 Let the forge's tools never weaken protection or merge past the checks
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
gitea_api took any endpoint with a token minted for the forge's
administrator with write:admin: an agent could drop a branch's protection
or its required checks, set the merge check's status, write to main or
merge its own pull request (novox/hq ADR 0266 §7, the review of
2026-10-09). gitea_api now reads anything but the administration and
writes only what an author writes; protection is only strengthened; a
merge needs every required status a success and merges only that head; a
guarded repository is not deleted; and the token is minted without
write:admin, a kept one re-minted at the next start.
2026-10-09 11:47:36 +02:00
mesh-admin 7339ce8556 Merge pull request 'claude-code: refuse registering what a session runs (hq issue 340)' (#149) from fix/340-what-a-session-runs-is-set-at-the-terminal into main 2026-10-09 08:00:24 +00:00
jochen d984eb84d6 claude-code: remove from or restore to only an agent account's own home
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
mesh/delivery delivered
Removing a rule from the operator's own home changes what their sessions
obey as surely as placing one: an agent could remove the rule that
constrains it (hq issue 340).
2026-10-09 09:51:18 +02:00
jochen 3677af6079 claude-code: write the home scope only into an agent account's own home
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
mesh/delivery superseded: a newer head of the same pull request
On the nodes where the agent runs as the operator, the home scope wrote the
operator's own ~/.claude, whose rules and skills load in every session, and
any node's tool could be addressed from another (hq issue 340). Take it
only for the serving node, where its home is an agent account's own; never
place a bus item in the operator's home. Ignore the built binary.
2026-10-09 09:41:52 +02:00
jochen e139fb4dd9 claude-code: set shared agent text at the controller's terminal, for now
Instructions, output styles, skills, commands and subagents for every node
or some reach every session as the mesh's own word, and the tools cannot
tell an agent from the operator; the operator decided they are set at the
terminal until a surface of their own updates them (hq issue 340). They
are settings keys, each text a list of lines; the home scope stays free.
2026-10-09 09:41:52 +02:00
jochen 0bd50fce0a The forge module's tool list test names the statuses and contains tools
mesh/merge-gate pass: builds mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
It had not been run before a merge: its expected list lacked gitea_commit_statuses since that tool came,
and now gitea_contains. The catalogue's TypeScript check, merged in from #151, runs it.
2026-10-09 03:23:19 +02:00
jochen 61ec4162d0 Merge branch 'check/typescript-modules-tested' into fix/319-316-covered-and-lifted 2026-10-09 03:23:05 +02:00
mesh-admin 63de373b9d Merge pull request 'zfs: the safety review's fixes, and the module owns the pool's scrub schedule' (#147) from fix/zfs-safety-review into main 2026-10-09 01:02:48 +00:00
jochen 6327f297ea Cover a held merge only when the forge says a later delivered merge contains it (review of #150)
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
Merge order on a protected trunk was the whole proof; the review asked for ancestry. The gitea module
gains gitea_contains, a read-only compare of two commits (total_commits 0 is contained), and mesh-delivery
asks it once per pair, off the lock like its plan ask, keeping the answer on the delivery. A forge that
cannot say leaves the merge held, not covered. Merge order stays the cheap first filter.

A later delivery now carries a module only when its own check passed (so a merge held without a passing
check is closed only by one that passed), its walk is of its own merge commit on the same trunk, and its
steps say the module reached every machine running it, not one. The words say what is known: a later
merge that contains it was delivered.
2026-10-09 02:57:18 +02:00
jochen c613efe189 claude-code: read front matter as YAML and refuse a file reference outside the item
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
A byte-order mark, a flow mapping or a merged key hid a key the line scan
looked for, and @path read any file into every session the item reached
(hq issue 340 review).
2026-10-09 02:52:57 +02:00
jochen 736743fedc claude-code: refuse registering what a session runs, so no agent can plant it in the operator's sessions
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
The tool runner is not told who called, so a hook, a managed setting, a
permission rule or a tool server registered through these tools could come
from an agent confined on another node and run as the operator there
(hq issue 340). They are set at the controller's terminal now; a skill,
subagent or command that runs something by itself is refused too, kept
hooks and settings are never rendered, and claude_code_registrations
lists what is registered and by which node.
2026-10-09 02:30:11 +02:00
jochen 2d1d4bf988 End a merge that moves nothing or that a later merge delivered, and lift a group hold its check answered (hq issues 319, 316)
mesh/merge-gate pass: builds mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
A held delivery asked a person a question with no right answer: four hq merges that build nothing,
merges adding a module the controller sends nowhere, controller merges whose change a later walk of
the same trunk already delivered, and a group member held because its composed check had not yet
answered. Only stop could end them, which marks a delivered change as an error on its pull request.

- A merge whose plan moves no module (nothing, or only a new module sent nowhere) is delivered at once.
- A new final state, covered: every module a merge moves was sent by a finished walk of a later merge
  into the same trunk, which contains it. Shown as success on the pull request; a group counts it as
  delivered.
- A group member waits published while its composed check is asked and unanswered, and a member held
  for it is lifted back to published once it passes for the heads that merged.

Read again each tick, so the deliveries already held end by themselves after the rollout.
2026-10-09 02:24:26 +02:00
jochen a9acd3dd28 Keep the scheduled run's served slot in a folder of root's alone, written never through a link
mesh/merge-gate pass: builds zfs, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Third review of #147. The run writes as root into /var/lib/zfs-scrub (0700, root's), apart from the
operator account's error counts in /var/lib/zfs-tools, and refuses a folder that is a link, open to
others or someone else's, and a file that is a link: a new file opened O_NOFOLLOW|O_EXCL, renamed over.
2026-10-09 01:55:54 +02:00
jochen bdd2d65404 Say that nothing trusts dunst's and xdg's settings, so a verb may still change them
mesh/merge-gate pass: builds dunst, xdg → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
With mesh-controller #170 a file that asks for a setting counts as trusted
unless it says otherwise, and its settings are set at the controller's
terminal alone (hq issue 339). These two are a person's own notifier look and
folder names in their home, which nothing else trusts.
2026-10-09 01:42:23 +02:00
jochen bd0182785c Run the scrub schedule as a scheduled process, never pause a scrub, and keep the pool's name the only setting in a unit
mesh/merge-gate pass: builds zfs, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
The second review of #147 found three blockers before the home server.

1. ${setting:} was not filled in a unit's name, so the distribution timers would have reached the
   machine as "zfs-scrub-weekly@${setting:scrub-pool}.timer". mesh-controller #171 fills it and refuses
   any value that is not a unit-name part. The module's own schedule needs no setting in a name now: the
   node-engine's timer for the scheduled process zfs-mesh-scrub fires `zfs-tools scrub-run` every quarter
   of an hour, and the run reads the pool, cadence, day and time from the configuration file.
2. The run does what upstream's zfs-scrub@.service does: if a scrub is running it waits for it with
   zpool wait -t scrub, otherwise it starts one and waits. It never pauses or stops a scrub, and the unit
   has no ExecStop. A resilver defers the scrub. The served slot is kept in /var/lib/zfs-tools.
3. Unassigning left the pool with no schedule. mesh-host #66 (hq ADR 0269, proposed) gives the
   distribution timer back running and enabled. The README says not to unassign before it is merged.

Also: the run comes before the distribution timers in the declaration; the day and time are checked
against fixed sets and reach no unit file (an invalid value runs nothing); a due scrub not started an
hour after its time is a warning; the error counts outlive a restart in a small state file.
2026-10-09 01:40:02 +02:00
jochen e69506a26f Make the zfs module safe to put on the home server, and give it the pool's scrub schedule
mesh/merge-gate pass: builds zfs, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
The safety review of the first version found it safe for the pool's data and asked for these fixes before
it goes on the home server:

- zfs_snapshot: no recursive. Every answer gives the dataset's free space and the pool's capacity, and
  says that the snapshot holds every block deleted or overwritten until it is destroyed by hand.
- A command that runs out of time is ended with its whole process group. An act that timed out is
  answered from the list, or as "outcome unknown", never as "NOT DONE".
- The watch runs as the operator account.
- The event follower reads ZFS's queue from its start, with a cursor per boot, so the pool's import and
  any downtime reach the journal once. It writes "dropped N events", kills zpool before waiting for it,
  and exits non-zero after five failed follows in a row.
- Past error counts are a warning, and counts that rise are unhealthy.
- The guard test catches .Run(, exec.Command(, exec.CommandContext( and process starts.
- The fixtures and the parser follow OpenZFS 2.4.4's own printers: human scan sizes under -p, no iostat
  section lines, error scrubs, per-vdev rebuild resilvers, paused scrubs.

The operator also asked the module to own the scrub. A timer and a service of the module's own run
zpool scrub -w -- <pool> from the scrub-pool, scrub-cadence, scrub-day and scrub-time settings. In the
same declaration, the distribution's weekly and monthly timers for that pool are held stopped and
disabled, so exactly one schedule scrubs it. zfs_scrub shows the schedule and the next run, with any
resilver's progress. zfs_scrub_start, zfs_scrub_pause and zfs_scrub_stop act with why. The health check
warns of a late or failed scrub and of a running resilver, and is unhealthy for a resilver that finished
with errors.
2026-10-09 01:17:34 +02:00
mesh-admin a1406e7d79 Merge pull request 'zsh: let a machine withhold node-login-shell's execute (hq ADR 0268)' (#145) from withhold-login-shell-execute into main 2026-10-08 23:12:53 +00:00
jochen c86f055384 Give the test snapshot a creation time apart from the fake clock in every zone
mesh/merge-gate pass: builds new: modules/zfs, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
On the build node, in UTC, the fixture's creation time was the fake clock's own instant, so the test
could not tell the list's time from the clock's. A day and a half earlier is apart from it everywhere.
2026-10-09 00:16:29 +02:00
jochen 7c8497225d Add the zfs module, so the mesh can see the home server's pools and snapshot a dataset
mesh/merge-gate pass: builds new: modules/zfs, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestASnapshotIsAnsweredFromTheListAfterwardsAndRecorded (0.00s)
mesh/delivery superseded: a newer head of the same pull request
The home server's pool holds the operator's media library, and no mesh tool could say whether it is
healthy, how full it is, when it was last scrubbed or what snapshots exist; the only way was a login.
This module reads all of it with fixed zfs and zpool commands (list, get, status, iostat, events,
history) and has one act, zfs_snapshot, which only adds a snapshot: refused for an unknown dataset, a
name outside [A-Za-z0-9._-], an existing snapshot or no why; run as sudo -n zfs snapshot -- ds@name;
answered from zfs list afterwards, with any other snapshot that came or went meanwhile; recorded as the
event snapshot.taken. No destroy, rollback, set or send exists, and a test holds every command to its
verb.

Health (ADR 0240): zfs_health on the watch process, unhealthy for a pool not ONLINE, device or data
errors, a pool past 90 % full or no ZFS at all; past 80 % and a scrub older than 35 days are warnings.
Beside it, the event follower (zpool events -f, as root) keeps ZFS's errors in its journal past a
reboot and is judged by its unit, the check the module does not run itself. No seat (ADR 0121), no
package: ZFS is the machine's.
2026-10-09 00:11:31 +02:00
jochen cfe1dffa45 zsh: let a machine withhold node-login-shell's execute
mesh/merge-gate pass: builds zsh → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group withhold-login-shell-execute delivered: every member is delivered
execute runs any command as the operator account, which can become root
without a person. The operator withholds it on the control-node until a
call needs a person's approval (hq ADR 0268); the other machines keep it
for now. The setting execute (serve by default) is written into a
root-owned file the tools bundle reads at start, and anything but serve
withholds the verb, so the machine's runtime does not offer it.
2026-10-09 00:10:28 +02:00
mesh-admin 3ee6c2b8ed Merge pull request 'postgres: the store's reader reads only what a module declares readable, never a secret (hq issue 337)' (#143) from fix/337-the-store-reader-reads-only-what-a-module-declares into main 2026-10-08 20:16:29 +00:00