webui: Console pinned copy, update step and service hardening (#1571)
Row 77, round 3 (round 2 rebased onto row 95). Rocko built it. This covers row 75's N1-N5 and Vikunja 146 and 147. `install --copy ID` refuses unless every ExecStart path of the bus unit, read from `systemctl --user show` so drop-ins count, lies inside that copy. It also refuses when the unit isn't loaded, no unit is loaded, ExecStart has no path, or systemctl fails. COPY_PATHS gains packages/tasks/src and packages/tasks/fields.json. Every copy directory is 0555. TOOLS.md and the webui README give the removal command and the rollback floor. Candidate manifest d9880f4c92d1d976027562a03cc5dd07512954e86f49f1da0ebb34e642a955f5 (10 paths, 8 applied by the patch, 2 packet files already committed), base2e26fbe1, packet agents/rocko/work/queue-77/r2b/. Approvals on round 3: Darkwing 27979 and Filbert 27985 (Filbert round 2: 27898). Sage's gate rerun 22:32:51Z-22:35:12Z on459c4fc6plus the patch (row 92 included), manifest 10/10: webui 101, control-board 124, and every scripts/test-*.sh exited 0 (test-release 4, Docker cases skipped at a dead socket). Live at the next Console install and restart (decision 109). Mos makes that call. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+65
-12
@@ -241,8 +241,9 @@ Exit codes: `0` ok · `1` failed or outcome unknown · `2` invalid input ·
|
||||
## Console service (`scripts/webui-service.sh`)
|
||||
|
||||
```bash
|
||||
scripts/webui-service.sh render [FLAGS] # print the unit
|
||||
scripts/webui-service.sh install [--dir DIR] [--no-reload] [FLAGS]
|
||||
scripts/webui-service.sh copy COMMIT [--copies DIR] # make a pinned copy
|
||||
scripts/webui-service.sh render [--copy ID [--copies DIR]] [FLAGS] # print the unit
|
||||
scripts/webui-service.sh install --copy ID [--copies DIR] [--dir DIR] [--no-reload] [FLAGS]
|
||||
scripts/webui-service.sh uninstall [--dir DIR] [--no-reload]
|
||||
scripts/webui-service.sh status [--dir DIR]
|
||||
# FLAGS: [--port N] [--business ID] [--board URL]
|
||||
@@ -257,19 +258,71 @@ preview set and is refused otherwise (exit 1, nothing written); ExecStart
|
||||
then ends with `--send`, and the Console needs the system config at start
|
||||
(`--send needs the bus configured`, exit 2). Without `--send` the unit is
|
||||
read-only. The template is
|
||||
`packages/webui/systemd/mosaic-webui.service.in`. `install` writes a temp
|
||||
file, renames it and runs `systemctl --user daemon-reload`; `uninstall`
|
||||
refuses while the unit is active; `status` prints the unit state, whether
|
||||
the installed unit sends, and `GET /healthz` on the installed port. Each
|
||||
value is held to a plain
|
||||
character set the unit can carry (no space, `%`, `$`, quote, backslash,
|
||||
`&` or `|`) and refused otherwise. The unit sets no `MOSAIC_*`, `CLAUDE*`,
|
||||
`CODEX*` or `PI_*` variable. Serve's exit 2 (usage, config, or a port
|
||||
already in use) is not restarted.
|
||||
`packages/webui/systemd/mosaic-webui.service.in`.
|
||||
|
||||
The unit runs a pinned copy, not the working checkout (row 77, #1571).
|
||||
`copy COMMIT` resolves COMMIT to its full id, refuses one not reachable
|
||||
from `refs/heads/refactor`, and writes `git archive` of the paths the
|
||||
Console loads into `DIR/<id>/`, by default
|
||||
`${XDG_DATA_HOME:-$HOME/.local/share}/mosaic-console/<id>/`. It writes a
|
||||
temp directory beside the target, makes every file and directory read-only
|
||||
(so a writer that replaces by rename can't change it either), then renames
|
||||
it, so the copy's path is always whole. A copy that exists is left alone
|
||||
(`exists:`). `install --copy ID` and `render --copy ID` point
|
||||
`WorkingDirectory` and ExecStart at the copy and pass serve
|
||||
`--checkout <this checkout>`, where the queue read still runs; `RELEASE`,
|
||||
the roles, the config check and the bus child come from the copy. The bus
|
||||
child is the human transport, `packages/bus/src/human-cli.mjs`, and the bus
|
||||
checks that path against its own, so the bus must run from the same copy
|
||||
(below). `install` refuses without `--copy`, or when the copy is missing.
|
||||
`render` without `--copy` prints the unit for the checkout, as before.
|
||||
|
||||
`install --copy ID` also refuses unless the bus runs from the same copy
|
||||
(round 2, #225). The bus accepts a human proof only from its own
|
||||
`packages/bus/src/human-cli.mjs`, so a Console on a copy whose bus runs
|
||||
from the checkout would fail every bus call with `human-required`. With
|
||||
`--business ID`, install reads `[email protected]`; without it, every
|
||||
loaded `mosaic-bus@` unit. It reads the effective ExecStart from
|
||||
`systemctl --user show -p LoadState -p ExecStart`, so a drop-in counts,
|
||||
and every ExecStart path must lie inside the copy. It refuses, naming both
|
||||
paths, when one doesn't; when the unit is not installed or none is loaded;
|
||||
when ExecStart has no path; and when the systemctl call fails. The check
|
||||
runs before the unit state read and changes nothing.
|
||||
|
||||
`install` writes a temp file, renames it and runs
|
||||
`systemctl --user daemon-reload`. `install` and `uninstall` refuse while the
|
||||
unit is in any state but inactive or failed (`systemctl --user list-units`),
|
||||
and say Jason or Mos stops it first; `--no-reload` skips only the
|
||||
daemon-reload, never that check. If the state read itself fails they refuse
|
||||
with one line naming the call, and change nothing. `copy` and `install`
|
||||
refuse outside the canonical checkout (a `.git` directory, not a worktree's
|
||||
`.git` file); `--allow-worktree` overrides that for the tests only. `status`
|
||||
prints the unit state, the copy id it runs, whether `refactor` points at
|
||||
that commit or another, whether the installed unit sends, and
|
||||
`GET /healthz` on the installed port. It changes nothing.
|
||||
|
||||
Each value is held to a plain character set the unit can carry (no space,
|
||||
`%`, `$`, quote, backslash, `&` or `|`), and the preview values must start
|
||||
with a letter or digit; anything else is refused. With `--auth-header
|
||||
X-authentik-uid`, in any letter case, `--allow-user` must be the Authentik
|
||||
uid, `^[0-9a-f]{64}$` (lead decision 101). The unit sets no `MOSAIC_*`,
|
||||
`CLAUDE*`, `CODEX*` or `PI_*` variable. Serve's exit 2 (usage, config, or a
|
||||
port already in use) is not restarted.
|
||||
|
||||
The update step: `copy` the commit, stop the unit, `install --copy ID`
|
||||
with the same flags, start it. Rollback is the same steps with the earlier
|
||||
id, which must be at or after row 77's landing: an older `cli.mjs` refuses
|
||||
serve's `--checkout` ("unknown option"). The queue view isn't pinned: it
|
||||
reads the checkout, so it shows the queue as it is now. Copies are kept
|
||||
until someone removes them, with `chmod -R u+w DIR && rm -rf DIR` since
|
||||
they are read-only; nothing prunes them. The step can't be used yet: the
|
||||
bus still runs from the checkout (`copy` doesn't include
|
||||
`scripts/mosaic`), so `install --copy ID` refuses until a later row puts
|
||||
the bus on the copy.
|
||||
|
||||
There is no start, stop, enable or restart verb. The Console reads the bus
|
||||
through the human transport, so whoever starts it holds the human's
|
||||
capability. Jason starts and restarts it
|
||||
capability. Jason starts, stops and restarts it
|
||||
(`systemctl --user enable --now mosaic-webui`), and so may Mos under lead
|
||||
decision 102, which accepts that sends from a Console Mos started enter the
|
||||
trail as Jason (`human-cli`). Any other agent starting it, even through
|
||||
|
||||
Reference in New Issue
Block a user