Files
mesh-catalog/modules/mesh-files/README.md
T
mesh-tools 652b00b25b
mesh/merge-gate checking again: the gate refused `unknown` because the controller judged with a node-engine validator pinned before mesh-host #117; mesh-co…
mesh/repo-check checking again: the gate refused `unknown` because the controller judged with a node-engine validator pinned before mesh-host #117; mesh-co…
mesh/delivery-group group feat/owned-paths checking: 0 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
Say on every directory how it treats files it does not know, and check it (issue 1024, ADR 0422)
Every directory and archive resource carries `unknown`: move-out for a home folder whose program writes nothing there (the node-engine, delivered in mesh-host #117, moves the rest to ~/.local/state/mesh-moved/<module>/ with a list), module for ~/.ssh and ~/.claude, program elsewhere; `knows` keeps what a program or the operator relies on (powerlevel10k's compiled files, i3's scripts and the asus/g14 drop-ins, dunst's slack rule, betterbird's records, node-env's forge credential, issue 1033). Two shared folders are no longer declared. Each module's README says it. checks/owned holds every manifest to it; checks/sshdir stays while main's merge check runs it (issue 1035). Tests that pin a manifest follow the new key only. Reviewed in three rounds.
2026-10-17 16:23:03 +00:00

3.7 KiB

mesh-files

The holder of the node seat node-files (novox/hq ADR 0394, issue 900; to-be 41 WP9). It reads and manages the files and accounts of the node it runs on, through the mesh, so nothing about a node's files goes through a shell.

It replaces mesh-reads (ADR 0310), which stays beside it until mesh-files is assigned on every node (ADR 0394 §9).

Verbs

The seat's verbs, with the descriptions the controller defines (internal/catalogue/files.go).

  • Reads: stat, readlink, list, read, search, account. As in mesh-reads: bounded answers, a place a secret is kept refused by name before any read, a line shaped like a secret withheld.
  • Writes: write (a text file whole, with an optional mode), copy, move, delete (recursive on request), mkdir (parents on request), chmod (octal or u+x,go-w, recursive on request), chown (owner, optional group, recursive on request).
  • Trash: trash lists it, restore puts an item back.

Every write verb takes reason (required, for the audit trail) and override (a reason; needed only for a path a module owns).

Rights

It runs as the tool runner's account, the node's operator account, and declares no account of its own. An act is tried with that account's rights first. Where that fails with a permission error, the act is repeated through sudo -n with a fixed binary (cat, cp, mv, stat, chmod, chown, mkdir, rm under /usr/bin) and an argument list built from fixed words and one path, never a shell (sudo.go). Tests use a fake runner. Not yet through sudo: stat, list, readlink and search (they answer what the account reaches and say what it may not); read and every write do.

Owners and modes

  • copy and move keep owner and mode (cp -a); a copy refuses a target that exists.
  • A new file or folder takes its folder's owner and group.
  • An overwrite keeps the overwritten file's owner and mode.

Trash

delete, and the overwrite of write, first move the old item to <state>/trash/<id>/item with a meta.json (original path, owner, group, mode, caller, reason, time). restore puts it back and refuses when the path is taken now. Entries older than 7 days are emptied on every write call and every trash list.

Owned paths

The controller gives the holder MESH_OWNED_PATHS, a JSON object from an absolute path to module (tool). A write, move or delete of such a path, anything under it, or (for delete, move and recursive chmod and chown) a folder that holds it, is refused naming the module and tool, unless override gives a reason. The override is recorded in the act log and told to the operator, in words without a path, through operator-channel.tell. Without the word, the module starts and owns nothing.

The act log

Each write act that was done is one JSON line in <state>/acts.log: time, caller, verb, paths, reason, and for an override the override and the owned paths it went through. The caller is the bus user the runtime proved (stdio.CallerOf), or unproven; it is never read from the arguments. A refused act is not logged.

What it refuses

  • A place a secret is kept (MESH_SECRET_PLACES, and the known credential files in refuse.go) is refused by name for read, list and search. Copying or moving such a place is allowed: nothing is printed.
  • A copy or move to or from <node>:/path is refused: copying between nodes comes in step 2 (issue 903).
  • A write without a reason; a path that is not absolute; a mode that is not a mode.

Files it does not know

Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416).

  • ${dir:state}: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2).