Commit Graph
1017 Commits
Author SHA1 Message Date
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 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
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
mesh-admin 90516fcce4 Merge pull request 'zfs: the mesh sees the home server's pools and datasets, and takes a named snapshot' (#146) from feat/zfs-module into main 2026-10-08 22:30:07 +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
mesh-admin 8131aa390f Merge pull request 'dunst: reload the running notifier when the mesh rewrites its file' (#144) from fix/dunst-reloads-when-its-file-changes into main 2026-10-08 20:12:23 +00:00
jochen e0e97a96c4 Report as revoked only what the read-back shows gone, and take back what the reader passed on (review of hq issue 337)
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
A reader given a privilege WITH GRANT OPTION that passed it on made the
plain revoke fail with dependent privileges exist; the pass still listed the
table as revoked and logged it every minute. The pass now retries such a
change with CASCADE, which reaches only grants that came from the reader,
reads the grants back, reports as revoked only what is gone, and says what
is still held and why when anything is.
2026-10-08 22:11:06 +02:00
jochen 9d438184be dunst: reload the running notifier when the mesh rewrites its file
mesh/delivery delivered
mesh/merge-gate pass: builds dunst → 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
A setting changed through mesh-controller.settings rewrote dunstrc, but the
running dunst kept the old font and width until the next login. Declare the
package's dunst.service in the account's manager, without a state, with
reload-on the configuration, so the node-engine runs its ExecReload
(dunstctl reload) and what is on screen stays. Add dunst_reload to do the
same on demand and say the font and width the files set.
2026-10-08 22:08:08 +02:00
jochen 8bf50f7555 Revoke the reader's grants as their own grantors and read them back, alter its role only on drift, and report definer functions and views (review of hq issue 337)
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
A superuser's REVOKE acts as the table owner, so a grant to the reader made
by a role holding GRANT OPTION survived it while the report said revoked.
Each grant is now revoked under SET ROLE to its grantor, the grants are read
back, and anything still held is reported as a failure. The role was
altered every minute with its password in the statement, which a server
logging DDL would record and which could collide with a first query; it is
now altered only when an attribute, a setting or the password has drifted,
one ensure at a time. SECURITY DEFINER functions and views the reader may
use reach data without a grant on it, so every pass lists them. The README
says the name filter is a backstop and that the read-only session default
can be turned off by a statement.
2026-10-08 22:01:57 +02:00
jochen fb07d552a0 Name the licences store's sealed tables as they are called, so a statement naming one is refused
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 21:44:36 +02:00
jochen 4f44bf1eb5 Give the store's reader only the columns a module declares readable, never a secret, so a query cannot read the controller's signing key (hq issue 337)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
mesh/repo-check pass: its merge-check.sh passed
mesh_store_reader held pg_read_all_data, so every caller of mesh-store.query
and postgres_query could read every column the store holds: the signing key
nodes trust, sealed secrets, password hashes. The reader now holds no role;
each minute, on every database, its memberships are revoked and its grants
brought to what the consumer's contribution lists under readable, minus any
column or table whose name says it holds a secret. Before a statement runs,
the reader's own session is asked which secret columns it can read and the
statement is refused if any; a statement naming a known secret table is
refused before it is sent.
2026-10-08 21:43:15 +02:00
mesh-admin 7b307a06b7 Merge pull request 'nfs-server and mounts: share one folder at a time over the private network, mount it on demand (hq ADR 0263)' (#136) from feat/mounts-module into main 2026-10-08 19:12:21 +00:00
jochen b8e04acfaf Let nfs-server's tests see their folders as the library owner's, so the root refusal is tested and not tripped when the build seat runs them as root
mesh/merge-gate pass: builds new: modules/mounts, modules/nfs-server, sent nowhere; no bus step; every machine composes with the change as it did without (…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-08 21:05:52 +02:00
jochen 29ac6ee16a Say that the modules' own checks are the only line before root, since sudo grants the account everything
mesh/merge-gate pass: builds new: modules/mounts, modules/nfs-server, sent nowhere; no bus step; every machine composes with the change as it did without (…
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestAnExportLineSquashesEveryClientToTheFoldersOwnerToTheMeshRangeOnly (0.00s)
mesh/delivery superseded: a newer head of the same pull request
mesh/delivery-group group feat/mounts-module delivering: 0 of 3 delivered
2026-10-08 20:21:28 +02:00
mesh-admin 32307bd18b Merge pull request 'nats: say who opens and closes connections to the bus, and why each closed (hq issue 327, ADR 0265)' (#139) from fix/327-a-verb-reads-on-the-serving-connection into main 2026-10-08 18:21:27 +00:00
jochen 53cc303440 Mount a share at the access the server gave this machine, and not at all when it does not export it here 2026-10-08 20:21:17 +02:00
jochen e50978ca45 Keep a clean look below a share for an hour, so the backup check does not walk the library every 30 s 2026-10-08 20:20:05 +02:00
jochen 8738c02d00 nfs-server: export a share only to the nodes granted and wanting it, and never root's, a symlinked or nested backup, or an unmounted dataset
The review of #136 found a share could squash every client to root, be
judged at a symlink's name rather than its folder, sit inside or above a
backup repository deeper than one level, export an unmounted dataset's
empty folder, go to every address in the private network's range, and
stay exported after the module left. Each is now refused or closed, with
a test that reproduced it first:

- a folder owned by uid or gid 0 is refused;
- the path is resolved through symlinks and every check runs on the real
  folder, which the export line names;
- a share inside a backup repository (any parent) or holding one within
  four levels is refused, and a look cut short by its bound refuses;
- every line carries mp, or mp=<its filesystem's mount point>;
- a share goes only to the nodes this machine's grants setting names and
  whose mounts module wants it on the bus, at the lower access, at the
  roster's address inside the range; nothing without wants;
- the process is declared before its config, so on unassignment it finds
  the config gone, removes its export file and lets the shares go;
- confirm accepts the seat's string "true".
2026-10-08 20:20:05 +02:00
jochen d5adc1d525 Make mounts refuse what it could break: data the mesh keeps, a busy or non-empty mount point, its own share, and a mount that is not its source; say each node's wants so the server exports only to them (review of #136) 2026-10-08 20:16:27 +02:00
jochen 48fba44471 Add the nfs-server module: a node shares a folder with the mesh's machines only, every client mapped to the folder's owner (hq ADR 0263)
mesh/merge-gate fail: builds new: modules/mounts, modules/nfs-server, sent nowhere; no bus step; a manifest the change touches fails the module check: modu…
mesh/repo-check fail: its merge-check.sh failed: long-running resources without health: 70
mesh/delivery-group group feat/mounts-module rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 18:36:01 +02:00
jochen 2b1b9b0235 Let mounts write each share's mount and automount, so no node depends on a hand-written fstab line (hq ADR 0263)
A machine mounts what its assignment names: a node's share from the
provision nfs-share, whose path it reads from nfs-server's state on the
bus, and occasional sources (an SMB device, a disk) whose absence is a
state, not a fault. apply writes and arms the unit pairs as root; watch
tries each source and judges the health; the operator's fstab lines are
taken over only through adopt, on their word, once the replacement is
armed. SMB logins are an outside party's secret, given with secret accept.
2026-10-08 18:36:01 +02:00
jochen 3251758340 Add a read-only mounts module: no tool could read a machine's fstab, mount units or exports 2026-10-08 18:36:01 +02:00
mesh-admin 0ad8f02fab Merge pull request 'dunst: make the font size and width settings, defaulting to today's values (hq ADR 0262)' (#132) from feat/setting-defaults into main 2026-10-08 16:18:52 +00:00
jochen ea69e795ab Read the tested nats release from the server itself
mesh/merge-gate pass: builds nats, sent nowhere; a bus step; 1 wait(s) for a person; every machine composes with the change as it did without (4 of 4 compo…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group fix/327-a-verb-reads-on-the-serving-connection delivered: every member is delivered
mesh/delivery delivered
The build seat's test binary listed no dependencies, so the check that the
image and the tests run one release failed on a release that matched.
2026-10-08 18:07:29 +02:00
mesh-admin 53c4b47da4 Merge pull request 'Serve unlink-dangling in the systemd module (hq issue 332)' (#138) from feat/systemd-unlink-dangling into main 2026-10-08 16:01:44 +00:00
jochen 1b368f9aaf Record a removal's outcome, publish it, and read one directory once
mesh/merge-gate pass: builds systemd → 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
The record kept only the intent, so a failed unlink read as removed; each
link now gets a synced intent and outcome line, and the outcome is the
module's link.removal event so it is not only a file on one machine.
The put-back hint no longer advises a symlink, an aliased directory is
read once, a machine-wide removal from user scope is said, and the state
directory names no owner a machine without an account cannot resolve.
2026-10-08 17:55:56 +02:00
jochen 0063cc2ad1 Say who opens and closes connections to the bus, and why each closed (hq issue 327)
mesh/merge-gate pass: builds nats, sent nowhere; a bus step; 1 wait(s) for a person; every machine composes with the change as it did without (4 of 4 compo…
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheImageIsTheServerTestedHere (0.00s)
mesh/delivery-group group fix/327-a-verb-reads-on-the-serving-connection rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
The connection total rose by hundreds an hour while the open count stayed
flat, and nothing could say which clients those were.
2026-10-08 17:54:01 +02:00
jochen 87c9d9f7e5 Serve unlink-dangling, so a leftover enable link goes through the seat
systemctl disable leaves a link whose unit file is gone, and the manager
keeps asking for the unit. The verb removes only such links in the
account's or the machine's own configuration, on the person's word, and
records each with its reason before removing it (hq issue 332).
2026-10-08 17:53:53 +02:00
mesh-admin e740683ca4 Merge pull request 'Serve reset-failed and wanted-by in the systemd module (hq issue 332)' (#137) from feat/systemd-reset-failed-why-started into main 2026-10-08 15:52:52 +00:00
jochen 2ed6264b8f Refuse a glob as a unit, and make wanted-by say only what it saw
mesh/merge-gate pass: builds systemd → 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
systemctl expands *, ? and [ in a unit argument, so reset-failed or stop
of "*" would have acted on every unit. wanted-by called a link dangling on
any stat error, followed a linked .wants anywhere, and missed most of the
manager's load path; it now says each of these, and says nothing names a
unit only of the directories it searched.
2026-10-08 17:38:48 +02:00
jochen 00f1d04616 Serve reset-failed and wanted-by in the systemd module (hq issue 332)
mesh/merge-gate fail: builds systemd → ace, g14, novox, shanks; no bus step; a manifest the change touches fails the module check: systemd claims node-serv…
mesh/repo-check fail: its merge-check.sh failed: long-running resources without health: 70
mesh/delivery superseded: a newer head of the same pull request
So a leftover unit's trigger is found and its failed record cleared
through the seat, not a shell: wanted-by reads the manager's reverse
dependencies and the enable links naming the unit, saying which dangle.
2026-10-08 17:21:18 +02:00
jochen 84731d8fc6 dunst: say plainly why each default
mesh/merge-gate pass: builds dunst → 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 failed: its walk failed: a gate on a first machine (what it carried put back), a build, a machine
mesh/delivery-group group feat/setting-defaults failed: a member failed
2026-10-08 17:02:43 +02:00
jochen 4c34836037 dunst: make the font size and width settings, defaulting to today's values (hq ADR 0262)
The laptop's 242 DPI panel shows Inter 10 at less than half the workstation's size. The
defaults keep every machine's file byte for byte as it is; a machine sets its own.
2026-10-08 17:02:43 +02:00
mesh-admin bf542128ee Merge pull request 'The bar renders contributed blocks; the power module gives it the battery and power draw (hq ADR 0255)' (#127) from feat/the-bar-takes-blocks into main 2026-10-08 14:27:18 +00:00
jochen 5d7d9020b8 Say the laptop's scripts unparsed where the build seat has no bash
mesh/merge-gate pass: builds asus-zephyrus-g14, i3status-rust, power → ace, g14, novox, shanks; no bus step; every machine composes with the change as it d…
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
The Go toolchain the repository check runs in carries no bash, so the scripts' syntax
check failed there on a missing interpreter, not a broken script.
2026-10-08 16:20:45 +02:00
jochen 0d54ecc764 Keep the value a watch hands over at once, watch a dead watch again, and let the path lists grow only in the open (hq ADR 0260)
mesh/merge-gate pass: builds asus-zephyrus-g14, i3status-rust, power → ace, g14, novox, shanks; no bus step; every machine composes with the change as it d…
mesh/repo-check fail: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery superseded: a newer head of the same pull request
The SDK hands the current value over before Watch returns, and the follower had no
entry for it yet, so the first value was dropped. A silent watch is now told from an
expired key and watched again rather than polled, each write takes its own temporary
file, drop-in directories are no module's, and an entry added to the path check's lists
is refused unless its line says why.
2026-10-08 16:12:37 +02:00
jochen 4f6cbd2197 Follow bar values so a dead watch speaks, date them by their reading, and widen the path check (hq ADR 0260)
A watch that died left the block silent, a removed block kept its value, and a value
frozen at its source looked fresh because its file was new. The follower now rereads a
silent value and says so, drops removed blocks, and dates each file by the reading's
own time. The path check takes /etc and /usr/local/lib in, ignores what a container sees
inside itself, and lists the five findings standing today, which may only go.
2026-10-08 16:12:37 +02:00
jochen 76b63744e6 Carry the power draw to the bar over the bus, not through a file (hq ADR 0260)
Modules talk over the bus. The power module's bundle puts each fresh reading of its own
sampler in its state draw, keyed by the machine; the bar's block shows that state, which
the bar's bundle watches under the read the mesh grants it. A new catalogue check refuses
a module naming another module's path under /run or /var/lib.
2026-10-08 16:12:37 +02:00
jochen e1b7fcd8c9 Count each processor package once, never read a sleeping card, and give no reading for a reset counter (hq ADR 0255)
Intel shows some packages twice (MSR and MMIO), an AMD card's sensors wake it, and a
counter reset after resume read as thousands of watts. The loop's cadence and the bar
script's staleness are now tested, and the sampler running on every machine is said.
2026-10-08 16:12:37 +02:00
jochen 0040c008b5 Let the bar render blocks other modules contribute, and give it the battery and power draw from the power module (hq ADR 0255)
The bar left the battery out because it belongs to whoever knows the hardware, and had
no way to take it back in. i3status-rust now renders bar-neutral blocks through its
template; the power module contributes the battery where the machine reports one and
the power draw where something measures it, sampling the root-only energy counter as
root and publishing only a rate. The memory block shows what programs hold, the figure
memory-pressure judges by, instead of counting the cache as used.
2026-10-08 16:12:37 +02:00
mesh-admin 05e5903a9b Merge pull request 'gitea: read npm lockfiles of every version and measure every version's size' (#128) from fix/npm-lockfile-shapes into main 2026-10-08 14:02:57 +00:00
mesh-admin 86d2b3b96d Merge pull request 'artifact-store-tools: a platform manifest is its index's; list unrecorded copies of upstream images (hq ADR 0257)' (#130) from feat/a-mirrored-base-is-recorded into main 2026-10-08 14:02:51 +00:00
mesh-admin bf8143ca12 Merge pull request 'sensors: read the machine's hardware condition on every machine (hq ADR 0256)' (#129) from feat/sensors into main 2026-10-08 13:21:01 +00:00
jochen 7aba612c6c sensors: take a SAS drive's standby, whose mode holds spaces
mesh/merge-gate pass: builds new: modules/sensors, 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
The third review found the asleep match took only one-word modes, so a SAS drive in STANDBY BY TIMER or
LOW POWER was read as a failed answer and lost its reading.
2026-10-08 15:04:02 +02:00