gosalyn: Case 2 design r10, two system units (bus and Console) from the existing templates, copy id the only re-pin value (darkwing row 195)
This commit is contained in:
1 parent
51006710ac
commit
4281b0064c
2 files changed
+66
-24
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
# Case 2 design: the creative writing hub (r9)
|
# Case 2 design: the creative writing hub (r10)
|
||||||
|
|
||||||
Gosalyn, 2026-10-20 (from `date -u`). Track T2 in jason-master-restart `projects/mosaic-mvp/PLAN.md`,
|
Gosalyn, 2026-10-20 (from `date -u`). Track T2 in jason-master-restart `projects/mosaic-mvp/PLAN.md`,
|
||||||
Vikunja #416, DECISIONS Q176-Q178. The requirement is Jason's text in TARGET.md (addendum 2026-10-20).
|
Vikunja #416, DECISIONS Q176-Q178. The requirement is Jason's text in TARGET.md (addendum 2026-10-20).
|
||||||
@@ -8,6 +8,25 @@ Base: `refactor` at 9ae8c73ac. r1 cited 5d019d876. `git diff --stat 5d019d876 9a
|
|||||||
adapters roles scripts` is empty (Mos in mos-0980, Webby in webby-0001), so every `file:line` holds at
|
adapters roles scripts` is empty (Mos in mos-0980, Webby in webby-0001), so every `file:line` holds at
|
||||||
9ae8c73ac. Paths are relative to the repository root.
|
9ae8c73ac. Paths are relative to the repository root.
|
||||||
|
|
||||||
|
## Changes from r9
|
||||||
|
|
||||||
|
Darkwing's row 195 question (darkwing to gosalyn, after Duckworth's B1) found a gap. r9 named one
|
||||||
|
system unit, `mosaic-stack.service`, and never said what it runs. The stack is two units today: the bus
|
||||||
|
host (`packages/cli/systemd/mosaic-bus.service.in`) and the Console
|
||||||
|
(`packages/webui/systemd/mosaic-webui.service.in`). The slice needs both, because C2-10b is Console
|
||||||
|
chat. Now 2c renders both from those templates as system units, `mosaic-bus@<business>.service` and
|
||||||
|
`mosaic-webui.service`, with the same copy-id rule, and no new `mosaic-stack.service.in` exists. The
|
||||||
|
bus unit carries the `EnvironmentFile=` line. The Console unit sets no credential variable, as its
|
||||||
|
template says. The node path and the Console's serve flags are install inputs, fixed across re-pins.
|
||||||
|
The copy id is the only value a re-pin changes.
|
||||||
|
|
||||||
|
One thing this makes explicit. The Console template says whoever starts it holds the human's capability,
|
||||||
|
so Jason starts it, or Mos under lead decision 102. A system unit that systemd starts at boot holds that
|
||||||
|
capability with no human starting it. On her host the human is Melanie, signing in through step 5. So
|
||||||
|
the step 9 go is the human's start, and a reboot restarts the Console under that same go. If Mos or
|
||||||
|
Webby reads decisions 62 and 94 as needing a human at every start, the Console unit gets no `[Install]`
|
||||||
|
section and a human starts it after each boot.
|
||||||
|
|
||||||
## Changes from r8
|
## Changes from r8
|
||||||
|
|
||||||
Jason's Q7 answer, "Q7: yes" (DECISIONS Q186, mos-1090, sage-1469). O4 is the model path.
|
Jason's Q7 answer, "Q7: yes" (DECISIONS Q186, mos-1090, sage-1469). O4 is the model path.
|
||||||
@@ -977,7 +996,7 @@ Chains follow Q177 merge-chaining. S marks a slice row.
|
|||||||
| C2-10b | S | Console chat: cited-item renderer through `renderItems`, the status line | C2-06, C2-09a, C2-03a | Haiku high | webui tests for items, scope badges, notices, "changed since" and the status line |
|
| C2-10b | S | Console chat: cited-item renderer through `renderItems`, the status line | C2-06, C2-09a, C2-03a | Haiku high | webui tests for items, scope badges, notices, "changed since" and the status line |
|
||||||
| C2-12 | S | The setup sheet for Melanie (7.1, including N2 and N4, and what changes for Bean and Chet in 4) and the survey run sheet for `<host>` | C2-01 | Luna | doc only, reviewed against the survey's real flags |
|
| C2-12 | S | The setup sheet for Melanie (7.1, including N2 and N4, and what changes for Bean and Chet in 4) and the survey run sheet for `<host>` | C2-01 | Luna | doc only, reviewed against the survey's real flags |
|
||||||
| C2-20 | S | Persona import (9.1): a script that reads only the persona definitions of the two Hermes profiles, confirms profile to name, and writes each persona file (4) with its `persona.name` and `persona.sha256` for the business file. Prints names, byte counts and sha256 only. A `--dry-run` mode, run before Jason's go, prints only counts of files, lines and lines starting `# ` or `## ` | C2-08 | Opus (Gosalyn), run under Jason's go | fixture Hermes profile trees: the right fields are taken, memory and session files are never opened (a read trace on the fixture), a profile with no persona field refuses, a persona that would fail C2-08's fence check refuses, output holds no persona text, each written file is 0600 and its digest matches, `--dry-run` writes nothing and prints the right counts for a fixture with two heading lines and no marker string from it |
|
| C2-20 | S | Persona import (9.1): a script that reads only the persona definitions of the two Hermes profiles, confirms profile to name, and writes each persona file (4) with its `persona.name` and `persona.sha256` for the business file. Prints names, byte counts and sha256 only. A `--dry-run` mode, run before Jason's go, prints only counts of files, lines and lines starting `# ` or `## ` | C2-08 | Opus (Gosalyn), run under Jason's go | fixture Hermes profile trees: the right fields are taken, memory and session files are never opened (a read trace on the fixture), a profile with no persona field refuses, a persona that would fail C2-08's fence check refuses, output holds no persona text, each written file is 0600 and its digest matches, `--dry-run` writes nothing and prints the right counts for a fixture with two heading lines and no marker string from it |
|
||||||
| C2-13 | S | Install script and precheck for `<host>` as `<user>`: bootstrap, release activate, business file with the mapping, `bean` and `chet` with their persona vars and files, any specialists she picks, her human id, token slot path, and `scripts/case2-unit.sh` with the template `packages/cli/systemd/mosaic-stack.service.in`, which root runs from a root-owned checkout in 10 step 2c. The install writes no unit. Jason or his delegate runs it (10) | C2-04, C2-03a, C2-20 | Opus (Gosalyn), Jason's go | precheck: config validates, `<user>` has no sudo and `id -G` prints exactly one entry, `bus.tester` is not on, the business file, `.history`, `changes.jsonl` and persona files have the modes in 4 and each persona digest matches, the run root is 0700, when some creative instance's harness is claude-code, one marker prompt run through the host's claude with `--system-prompt-file` shows it replaces the default prompt (its version printed), the key file in 10 step 2d exists, is owned by `<user>` at 0600 and sets a nonempty `ZAI_API_KEY` when an instance's provider is `zai` (name only, never the value), free memory printed, `library.sync` fetches her repo read-only, `library.status` lists R-problems, no Discord binding exists. `case2-unit.sh` refuses an installed unit that differs from its render (one edited to `User=root` is the test), a symlink at the unit path, and a render whose `User=`, `Group=`, `ExecStart=`, `EnvironmentFile=` or hardening lines are off their fixed values. Tested against a scratch data root and a local bare repo |
|
| C2-13 | S | Install script and precheck for `<host>` as `<user>`: bootstrap, release activate, business file with the mapping, `bean` and `chet` with their persona vars and files, any specialists she picks, her human id, token slot path, and `scripts/case2-unit.sh`, which renders the bus and Console system units from their existing templates and which root runs from a root-owned checkout in 10 step 2c. The install writes no unit. Jason or his delegate runs it (10) | C2-04, C2-03a, C2-20 | Opus (Gosalyn), Jason's go | precheck: config validates, `<user>` has no sudo and `id -G` prints exactly one entry, `bus.tester` is not on, the business file, `.history`, `changes.jsonl` and persona files have the modes in 4 and each persona digest matches, the run root is 0700, when some creative instance's harness is claude-code, one marker prompt run through the host's claude with `--system-prompt-file` shows it replaces the default prompt (its version printed), the key file in 10 step 2d exists, is owned by `<user>` at 0600 and sets a nonempty `ZAI_API_KEY` when an instance's provider is `zai` (name only, never the value), free memory printed, `library.sync` fetches her repo read-only, `library.status` lists R-problems, no Discord binding exists. `case2-unit.sh` refuses an installed unit that differs from its render (one edited to `User=root` is the test), a symlink at either unit path, and a render whose `User=`, `Group=`, `ExecStart=`, the bus unit's `EnvironmentFile=` or hardening lines are off their fixed values. Tested against a scratch data root and a local bare repo |
|
||||||
| C2-14 | S | End-to-end acceptance on a scratch data root and a bare repo in her layout: sync, ask a creative agent in its thread, a reply whose cites verify, a playground cite as canon refused, a force-push held as diverged, the Console shows the reply as items | slice rows | Opus | one script, which the reviewer also runs |
|
| C2-14 | S | End-to-end acceptance on a scratch data root and a bare repo in her layout: sync, ask a creative agent in its thread, a reply whose cites verify, a playground cite as canon refused, a force-push held as diverged, the Console shows the reply as items | slice rows | Opus | one script, which the reviewer also runs |
|
||||||
| C2-02 | | Bus writes: `writeLibraryFile`, `commitFile` refactor, r1's S0 rule on the working tree | C2-04 | Sol (Rocko) | writer tests, `packages/project/tests` unchanged and green |
|
| C2-02 | | Bus writes: `writeLibraryFile`, `commitFile` refactor, r1's S0 rule on the working tree | C2-04 | Sol (Rocko) | writer tests, `packages/project/tests` unchanged and green |
|
||||||
| C2-03b | | Push path, race retry, `unpushed`, `library-remote-invalid` on writes | C2-02, C2-03a | Opus | push tests against a bare repo |
|
| C2-03b | | Push path, race retry, `unpushed`, `library-remote-invalid` on writes | C2-02, C2-03a | Opus | push tests against a bare repo |
|
||||||
@@ -1008,7 +1027,7 @@ compaction count and clear context (GAP-MAP C2.12, C2.13). Until then, a new thr
|
|||||||
|
|
||||||
Where it runs (Jason, Q181, relayed in mos-1031). `<host>` is her VM 10.1.1.54, W-Melanie-OC, VM 1012
|
Where it runs (Jason, Q181, relayed in mos-1031). `<host>` is her VM 10.1.1.54, W-Melanie-OC, VM 1012
|
||||||
on pve. `<user>` is `mosaic`, a system user with no sudo, no supplementary groups and no keys, home
|
on pve. `<user>` is `mosaic`, a system user with no sudo, no supplementary groups and no keys, home
|
||||||
`/srv/mosaic` at mode 0700. It runs as the system unit `mosaic-stack.service` with `ProtectHome`, so nothing it needs may
|
`/srv/mosaic` at mode 0700. It runs as two system units, `mosaic-bus@<business>.service` and `mosaic-webui.service`, both with `ProtectHome`, so nothing it needs may
|
||||||
live under `/home`. Its config directory, `<configDir>`, is `/srv/mosaic/.config/mosaic-dev`, and the token
|
live under `/home`. Its config directory, `<configDir>`, is `/srv/mosaic/.config/mosaic-dev`, and the token
|
||||||
slot and persona files live under it. ops-01's plan, section 3, set this layout. That plan stopped
|
slot and persona files live under it. ops-01's plan, section 3, set this layout. That plan stopped
|
||||||
with #421, and Q183 keeps a `mosaic` user without sudo on each VM. Section 10 step 2a creates it. The rest of the design
|
with #421, and Q183 keeps a `mosaic` user without sudo on each VM. Section 10 step 2a creates it. The rest of the design
|
||||||
@@ -1119,16 +1138,24 @@ What it adds is one bot account with read access to that one repo.
|
|||||||
not part of 10-27. It is row B (#428).
|
not part of 10-27. It is row B (#428).
|
||||||
- 2c, ops-01 via `localadmin`: root makes a checkout of the reviewed commit outside `/srv/mosaic`,
|
- 2c, ops-01 via `localadmin`: root makes a checkout of the reviewed commit outside `/srv/mosaic`,
|
||||||
owned by root, and checks that its HEAD is the commit Jason's go names. From that checkout it runs
|
owned by root, and checks that its HEAD is the commit Jason's go names. From that checkout it runs
|
||||||
`scripts/case2-unit.sh`, which renders `mosaic-stack.service` from the template with the user and
|
`scripts/case2-unit.sh`, which renders two system units from the checkout's existing templates:
|
||||||
home as inputs. It checks `User=`, `Group=`, `ExecStart=` and the hardening lines against fixed
|
`[email protected]` from `packages/cli/systemd/mosaic-bus.service.in`, enabled as
|
||||||
values and writes `/etc/systemd/system/mosaic-stack.service` as `root:root` 0644. It never uses
|
`mosaic-bus@<business>.service`, and `mosaic-webui.service` from
|
||||||
`systemctl link`, and it refuses a symlink at the unit path. On a re-install it refuses an
|
`packages/webui/systemd/mosaic-webui.service.in`. Inputs: `--user`, `--home`, `--business`,
|
||||||
installed unit that differs from the render. Root runs nothing from `/srv/mosaic` and reads no
|
`--copy`, `--node` and the Console's serve flags. In both, `@REPO@` is the copy
|
||||||
unit that `mosaic` wrote. Then it enables the unit. The unit starts only at step 9.
|
`<home>/.local/share/mosaic-stack/copies/<id>`. `%h` becomes the literal home, because a system
|
||||||
|
unit's `%h` is root's home. The render adds `User=`, `Group=`, the hardening lines and
|
||||||
|
`WantedBy=multi-user.target`. It checks `User=`, `Group=`, `ExecStart=`, the bus unit's
|
||||||
|
`EnvironmentFile=` and the hardening lines against fixed values, and the copy id is the one part
|
||||||
|
that may change between renders. It writes both under `/etc/systemd/system/` as `root:root` 0644.
|
||||||
|
It never uses `systemctl link`, and it refuses a symlink at either unit path. On a re-install it
|
||||||
|
refuses an installed unit that differs from the render. Root runs nothing from `/srv/mosaic` and
|
||||||
|
reads no unit that `mosaic` wrote. Then it enables both. They start only at step 9, the bus first
|
||||||
|
and then the Console, and they stop in the reverse order.
|
||||||
- 2d, Jason: the hub's own key on the existing Z.ai account, with a spend cap if the account offers
|
- 2d, Jason: the hub's own key on the existing Z.ai account, with a spend cap if the account offers
|
||||||
one. Model use is spend, so the key and the cap are his. Whose Z.ai account it is hasn't been said,
|
one. Model use is spend, so the key and the cap are his. Whose Z.ai account it is hasn't been said,
|
||||||
and that is his input. The key goes in `/srv/mosaic/.config/mosaic-dev/secrets/claude-session.env`
|
and that is his input. The key goes in `/srv/mosaic/.config/mosaic-dev/secrets/claude-session.env`
|
||||||
as `ZAI_API_KEY=`, owned by `mosaic`, mode 0600, the file the unit's `EnvironmentFile=` line loads
|
as `ZAI_API_KEY=`, owned by `mosaic`, mode 0600, the file the bus unit's `EnvironmentFile=` line loads
|
||||||
(the same line as `packages/cli/systemd/mosaic-bus.service.in:24`). The name says claude, but it is
|
(the same line as `packages/cli/systemd/mosaic-bus.service.in:24`). The name says claude, but it is
|
||||||
the one file decision 119 and `packages/cli/README.md` document, so I kept it. He places it, or
|
the one file decision 119 and `packages/cli/README.md` document, so I kept it. He places it, or
|
||||||
says who may, per the Q173 secrets rule. Agents never print it. Until #423 step 5, `melanie`'s
|
says who may, per the Q173 secrets rule. Agents never print it. Until #423 step 5, `melanie`'s
|
||||||
|
|||||||
@@ -710,8 +710,8 @@ Dewey approves the manifest. The webui suite is green on Sage's gate, along with
|
|||||||
|
|
||||||
### Problem
|
### Problem
|
||||||
|
|
||||||
The hub runs on 10.1.1.54 as `mosaic` under `/srv/mosaic`, as `mosaic-stack.service` with
|
The hub runs on 10.1.1.54 as `mosaic` under `/srv/mosaic`, as two system units,
|
||||||
`ProtectHome` (design 9, Q181). Nothing installs a library business there, or checks the host before it
|
`mosaic-bus@<business>.service` and `mosaic-webui.service`, with `ProtectHome` (design 9 and 10 step 2c, r10). Nothing installs a library business there, or checks the host before it
|
||||||
starts.
|
starts.
|
||||||
|
|
||||||
### Owner and reviewer
|
### Owner and reviewer
|
||||||
@@ -722,8 +722,8 @@ starts.
|
|||||||
|
|
||||||
### Files owned
|
### Files owned
|
||||||
|
|
||||||
`scripts/case2-install.sh`, `scripts/case2-precheck.sh` and `scripts/case2-unit.sh` (new), the template
|
`scripts/case2-install.sh`, `scripts/case2-precheck.sh` and `scripts/case2-unit.sh` (new),
|
||||||
`packages/cli/systemd/mosaic-stack.service.in` (new), `packages/cli/tests/case2-install.test.mjs`
|
`packages/cli/tests/case2-install.test.mjs`
|
||||||
(new), and one line of `packages/cli/README.md`: at `:713`, "nothing in the repository creates or reads
|
(new), and one line of `packages/cli/README.md`: at `:713`, "nothing in the repository creates or reads
|
||||||
it" changes to say that nothing creates it, that only the Case 2 precheck reads it, and that the
|
it" changes to say that nothing creates it, that only the Case 2 precheck reads it, and that the
|
||||||
precheck prints only variable names, never values (webby-0034 N2, webby-0035).
|
precheck prints only variable names, never values (webby-0034 N2, webby-0035).
|
||||||
@@ -749,20 +749,31 @@ precheck take `--profile factory|library`, default `library`:
|
|||||||
|
|
||||||
`case2-unit.sh` is the 2c step. Root runs it from a root-owned checkout of the reviewed commit outside
|
`case2-unit.sh` is the 2c step. Root runs it from a root-owned checkout of the reviewed commit outside
|
||||||
`/srv/mosaic`, never from the release, because `mosaic` can write the release. It does the following:
|
`/srv/mosaic`, never from the release, because `mosaic` can write the release. It does the following:
|
||||||
- It renders the unit from the checkout's template, with the user, the home and the copy id as inputs.
|
- It renders two system units from the checkout's existing templates and changes neither template:
|
||||||
- It refuses unless `User=`, `Group=`, `ExecStart=`, `EnvironmentFile=` and the hardening lines match
|
`[email protected]` from `packages/cli/systemd/mosaic-bus.service.in` (enabled as
|
||||||
fixed values. `EnvironmentFile=` is the line in `packages/cli/systemd/mosaic-bus.service.in:24`. The
|
`mosaic-bus@<business>.service`) and `mosaic-webui.service` from
|
||||||
copy id is the one part of `ExecStart=` that varies, so a re-pin to a new copy can re-render the
|
`packages/webui/systemd/mosaic-webui.service.in`. Flags: `--user`, `--home`, `--business`, `--copy ID`,
|
||||||
unit (row 195). It must be exactly 40 lowercase hex characters naming a copy that holds `scripts/mosaic`,
|
`--node PATH` and the Console's serve flags after `--`. Row 195 calls it with those names. `@REPO@` is
|
||||||
|
the copy dir. `%h` becomes the literal `--home`, because in a system unit `%h` is root's home.
|
||||||
|
`@PM_FLAGS@` and `@CLAUDE_ENV@` render empty. The render adds `User=`, `Group=`, the hardening lines
|
||||||
|
and `WantedBy=multi-user.target`.
|
||||||
|
- It refuses unless `User=`, `Group=`, `ExecStart=`, the bus unit's `EnvironmentFile=` and the
|
||||||
|
hardening lines match fixed values. `EnvironmentFile=` is the line in
|
||||||
|
`packages/cli/systemd/mosaic-bus.service.in:24`, with the literal home. The Console unit has none,
|
||||||
|
and it refuses one. `--node` must be an absolute path to a root-owned file outside `<home>`. The
|
||||||
|
serve flags pass `scripts/webui-service.sh`'s own flag checks. On a re-install, the node path and
|
||||||
|
serve flags must equal the installed unit's. The copy id is the one value that may change, so a
|
||||||
|
re-pin or a rollback to another copy re-renders both units (row 195). It must be exactly 40 lowercase hex characters naming a copy that holds `scripts/mosaic`,
|
||||||
directly under `<home>/.local/share/mosaic-stack/copies`. That is the rule `copy_root` uses
|
directly under `<home>/.local/share/mosaic-stack/copies`. That is the rule `copy_root` uses
|
||||||
(`scripts/stack-copy.sh:6`, `:20`). Anything else refuses before the render.
|
(`scripts/stack-copy.sh:6`, `:20`). Anything else refuses before the render.
|
||||||
- It refuses any `PermissionsStartOnly=`, and any `Exec*=` value whose leading run of prefix
|
- It refuses any `PermissionsStartOnly=`, and any `Exec*=` value whose leading run of prefix
|
||||||
characters (`@`, `-`, `:`, `+`, `!`) holds a `+` or `!`. systemd takes those prefixes in any order,
|
characters (`@`, `-`, `:`, `+`, `!`) holds a `+` or `!`. systemd takes those prefixes in any order,
|
||||||
so `-+/srv/mosaic/x` runs as root even with `User=mosaic`. The named-line check doesn't read these
|
so `-+/srv/mosaic/x` runs as root even with `User=mosaic`. The named-line check doesn't read these
|
||||||
lines (webby-0026 N1, webby-0027, defence in depth).
|
lines (webby-0026 N1, webby-0027, defence in depth).
|
||||||
- It writes `/etc/systemd/system/mosaic-stack.service` as `root:root` 0644. It never uses
|
- It writes both units under `/etc/systemd/system/` as `root:root` 0644. It never uses
|
||||||
`systemctl link`, and it refuses a symlink at the unit path.
|
`systemctl link`, and it refuses a symlink at either unit path.
|
||||||
- On a re-install, it refuses an installed unit that differs from the render.
|
- On a re-install, it refuses an installed unit that differs from the render, except for the copy id.
|
||||||
|
- Order: start the bus, then the Console. Stop in reverse.
|
||||||
- It reads nothing from under `<user>`'s home.
|
- It reads nothing from under `<user>`'s home.
|
||||||
|
|
||||||
The precheck refuses unless all of these hold:
|
The precheck refuses unless all of these hold:
|
||||||
@@ -805,8 +816,12 @@ output for the fixture key value finds nothing.
|
|||||||
- an installed unit edited to `User=root`
|
- an installed unit edited to `User=root`
|
||||||
- an edited `ExecStart=`
|
- an edited `ExecStart=`
|
||||||
- a template with a hardening line removed
|
- a template with a hardening line removed
|
||||||
- a template with the `EnvironmentFile=` line removed or pointing elsewhere
|
- a bus template with the `EnvironmentFile=` line removed or pointing elsewhere, and a Console template
|
||||||
- a symlink at the unit path
|
with one added
|
||||||
|
- a symlink at either unit path
|
||||||
|
- a `--node` that is relative, not root-owned, or under `<home>`
|
||||||
|
- a re-install with a different `--node` or different serve flags
|
||||||
|
- an unrendered `%h` left in either unit
|
||||||
- a template with `ExecStartPre=+/srv/mosaic/x`
|
- a template with `ExecStartPre=+/srv/mosaic/x`
|
||||||
- a template with `ExecStartPre=-+/srv/mosaic/x`
|
- a template with `ExecStartPre=-+/srv/mosaic/x`
|
||||||
- a copy id that is short, uppercase, holds `/` or `..`, or names no directory
|
- a copy id that is short, uppercase, holds `/` or `..`, or names no directory
|
||||||
|
|||||||
Reference in new issue
Block a user