gosalyn: Case 2 design r8 for #416 (T2): root renders and checks the unit from a root-owned checkout in 2c, mosaic writes no unit, 2b via sudo -u mosaic from localadmin, step 1 hold lifted, C2-13 brief to match (webby-0025, mos-1074)
This commit is contained in:
1 parent
8645cb4560
commit
eab6e1a3a8
2 files changed
+56
-13
No files matched your search
@@ -1,4 +1,4 @@
|
||||
# Case 2 design: the creative writing hub (r7)
|
||||
# Case 2 design: the creative writing hub (r8)
|
||||
|
||||
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).
|
||||
@@ -8,6 +8,26 @@ 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
|
||||
9ae8c73ac. Paths are relative to the repository root.
|
||||
|
||||
## Changes from r7
|
||||
|
||||
Webby's review (webby-0025, CHANGES, notes at agents/webby/work/case2-r7/REVIEW.md, 19bb3ae36).
|
||||
|
||||
- B1. r7 had root install a unit file that `mosaic` wrote. By 2c, package code has run as `mosaic` in
|
||||
2b and could rewrite that file to `User=root` or another `ExecStart`, so the user without sudo had a
|
||||
path to root, and again on every re-install. Now `mosaic` writes no unit. In 2c, root works only from
|
||||
a root-owned checkout of the reviewed commit outside `/srv/mosaic`. It renders the unit from that
|
||||
checkout's template, with the user and home as inputs, and checks `User=`, `Group=`, `ExecStart=` and
|
||||
the hardening lines against fixed values. It then writes `/etc/systemd/system/mosaic-stack.service`
|
||||
as `root:root` 0644, and it never uses `systemctl link`. Webby suggested the template in the release.
|
||||
The release sits under `/srv/mosaic`, where `mosaic` can write it, so I used a root-owned checkout
|
||||
instead. 2c refuses an installed unit that differs from the render, and a symlink at the unit path.
|
||||
C2-13 tests a unit edited to `User=root`, and 2c refuses it.
|
||||
- N1. `mosaic` has no keys and no login shell, so 2b runs as `sudo -u mosaic -H` from `localadmin`.
|
||||
Nobody adds a key to `mosaic` to make 2b work.
|
||||
|
||||
Mos lifted the mos-1050 hold on step 1 after Webby passed it (mos-1074). Section 10 still goes to
|
||||
Jason one step at a time.
|
||||
|
||||
## Changes from r6
|
||||
|
||||
Webby's review (webby-0023, CHANGES, notes at agents/webby/work/case2-r6/REVIEW.md, 216302d65).
|
||||
@@ -19,7 +39,7 @@ Webby's review (webby-0023, CHANGES, notes at agents/webby/work/case2-r6/REVIEW.
|
||||
and runs them under Jason's go for step 2, over ssh as `[email protected]` with NOPASSWD sudo.
|
||||
#423 step 1 creates that account. They use neither `melanie`'s sudo nor pve root. So the install
|
||||
depends on #423 step 1 and on nothing else in #423. C2-13 writes the unit file and doesn't install
|
||||
it. Section 9 cites Q183 for the layout, not the stopped plan.
|
||||
it. r8 replaces that, because `mosaic` must write no unit (webby-0025). Section 9 cites Q183 for the layout, not the stopped plan.
|
||||
- Mos, mos-1064 item 3. r6 said Jason's acceptance covers `melanie`'s sudo. It covers the factory's
|
||||
paths to .54 (Q182, Q183): pve root, ssh as `melanie`, and the root her sudo gives through those
|
||||
paths. It doesn't cover the Hermes processes, which run as `melanie` and could use her sudo to read
|
||||
@@ -937,7 +957,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-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-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 the `mosaic-stack.service` unit file, which root installs in 10 step 2c. 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, one marker prompt run through the host's claude with `--system-prompt-file` shows it replaces the default prompt (its version printed), free memory printed, `library.sync` fetches her repo read-only, `library.status` lists R-problems, no Discord binding exists. 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` 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, one marker prompt run through the host's claude with `--system-prompt-file` shows it replaces the default prompt (its version printed), 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=` 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-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 |
|
||||
@@ -1057,11 +1077,18 @@ What it adds is one bot account with read access to that one repo.
|
||||
doesn't run them without the go.
|
||||
- 2a, ops-01 via `localadmin`: `useradd` `mosaic` as a system user with no sudo, no supplementary
|
||||
groups and no keys. Create `/srv/mosaic` at 0700, owned by `mosaic`.
|
||||
- 2b: the install as `<user>`, by him or his delegate. `scripts/bootstrap.sh`, release package and
|
||||
- 2b: the install as `<user>`, by him or his delegate, run as `sudo -u mosaic -H` from `localadmin`.
|
||||
`mosaic` has no keys and no login shell, and nobody adds a key to make 2b work. `scripts/bootstrap.sh`, release package and
|
||||
activate, the pinned harness, and which model accounts its agents use. Model use is spend, so the
|
||||
accounts are his call.
|
||||
- 2c, ops-01 via `localadmin`: install the unit file that C2-13 writes as `mosaic-stack.service`,
|
||||
and enable it. The unit starts only at step 9.
|
||||
- 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
|
||||
`scripts/case2-unit.sh`, which renders `mosaic-stack.service` from the template with the user and
|
||||
home as inputs. It checks `User=`, `Group=`, `ExecStart=` and the hardening lines against fixed
|
||||
values and writes `/etc/systemd/system/mosaic-stack.service` as `root:root` 0644. It never uses
|
||||
`systemctl link`, and it refuses a symlink at the 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 the unit. The unit starts only at step 9.
|
||||
3. A new Gitea bot account for the hub, with read access to the books repo only. Not bean-brain, and not
|
||||
her other three repos. Her repo's access list is hers, so adding the bot means asking her or acting as
|
||||
admin, which is his call.
|
||||
|
||||
@@ -713,17 +713,26 @@ starts.
|
||||
|
||||
### Files owned
|
||||
|
||||
`scripts/case2-install.sh` and `scripts/case2-precheck.sh` (new), a unit template under
|
||||
`packages/cli/systemd/` if the existing one can't take the paths, and
|
||||
`packages/cli/tests/case2-install.test.mjs` (new).
|
||||
`scripts/case2-install.sh`, `scripts/case2-precheck.sh` and `scripts/case2-unit.sh` (new), the template
|
||||
`packages/cli/systemd/mosaic-stack.service.in` (new), and `packages/cli/tests/case2-install.test.mjs`
|
||||
(new).
|
||||
|
||||
### What ships
|
||||
|
||||
The install does: bootstrap, release package and activate, the business file with the mapping, `bean`
|
||||
and `chet` with their persona vars and files, any specialists she picks, her human id, the token slot
|
||||
path, and the `mosaic-stack.service` unit file. It runs as `<user>`, so it doesn't create `<user>` or
|
||||
install the unit. Root does those in design 10 steps 2a and 2c. Every host value is an input, so a later host move changes
|
||||
only inputs.
|
||||
path. It runs as `<user>` through `sudo -u mosaic -H` from `localadmin`, and `mosaic` gets no key. It
|
||||
doesn't create `<user>` and writes no unit file. Root does those in design 10 steps 2a and 2c. Every
|
||||
host value is an input, so a later host move changes only inputs.
|
||||
|
||||
`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:
|
||||
- It renders the unit from the checkout's template, with the user and home as inputs.
|
||||
- It refuses unless `User=`, `Group=`, `ExecStart=` and the hardening lines match fixed values.
|
||||
- It writes `/etc/systemd/system/mosaic-stack.service` as `root:root` 0644. It never uses
|
||||
`systemctl link`, and it refuses a symlink at the unit path.
|
||||
- On a re-install, it refuses an installed unit that differs from the render.
|
||||
- It reads nothing from under `<user>`'s home.
|
||||
|
||||
The precheck refuses unless all of these hold:
|
||||
- The config validates.
|
||||
@@ -741,11 +750,18 @@ It also prints free memory and verifies HOME is `/srv/mosaic`.
|
||||
|
||||
Tests run against a scratch data root and a local bare repo. Each precheck refusal has a case. The
|
||||
claude check runs against a fake claude that appends rather than replaces, and that case refuses.
|
||||
`case2-unit.sh` runs against a scratch unit directory. These cases refuse:
|
||||
- an installed unit edited to `User=root`
|
||||
- an edited `ExecStart=`
|
||||
- a template with a hardening line removed
|
||||
- a symlink at the unit path
|
||||
|
||||
A clean render writes a 0644 file. A test checks the script for `systemctl link`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
Running it on the host. Design 10 steps 2 to 7 are Jason's. Step 1 no longer waits on #421 (Q183), and
|
||||
its hold lifts once Webby approves r7. The root steps 2a and 2c are ops-01's, over ssh as `localadmin`,
|
||||
Mos lifted its hold (mos-1074). The root steps 2a and 2c are ops-01's, over ssh as `localadmin`,
|
||||
so the install waits on #423 step 1 and on nothing else in #423. The Gitea bot account and the token slot are steps 3 and 4. Stopping Hermes
|
||||
is step 9 and her choice.
|
||||
|
||||
|
||||
Reference in new issue
Block a user