gosalyn: Case 2 design r7 for #416 (T2): root steps 2a and 2c owned by ops-01 via localadmin (#423 step 1), no supplementary groups with an id -G check, Hermes sudo as Mos's residual until #423 step 5, C2-13 brief to match (webby-0023, mos-1064)

This commit is contained in:
jason.woltje committed 2026-10-20 15:02:03 -05:00
1 parent a0ce2ffb84
commit 8645cb4560
2 files changed
+65 -24

No files matched your search

+57 -18
View File
@@ -1,4 +1,4 @@
# Case 2 design: the creative writing hub (r6) # Case 2 design: the creative writing hub (r7)
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,28 @@ 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 r6
Webby's review (webby-0023, CHANGES, notes at agents/webby/work/case2-r6/REVIEW.md, 216302d65).
- B1. r6 said the install creates `mosaic`, but step 2 and C2-13 run the install as `<user>`. A user
can't create itself, and `mosaic` without sudo can't install a system unit. The #421 plan that owned
those root steps stopped. They are now their own items in section 10: 2a creates `mosaic` and
`/srv/mosaic`, and 2c installs and enables the unit after the install. ops-01 owns them (mos-1064)
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.
- 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
`/srv/mosaic`. That part is Mos's residual. Q181 keeps Hermes running, and #423 step 5 removes her
sudo once Bean and Chet are live. Section 9 and the r5 notes now say so.
- N1. A system user always has its primary group. Section 9, the C2-13 row and the precheck now say no
supplementary groups, and the precheck checks that `id -G` prints exactly one entry.
The mos-1050 hold on step 1 stays until Webby approves r7 (mos-1064).
## Changes from r5 ## Changes from r5
Mos, mos-1057, after Jason confirmed Q4 (DECISIONS Q183, read at `DECISIONS.md:1533-1545`). The #421 Mos, mos-1057, after Jason confirmed Q4 (DECISIONS Q183, read at `DECISIONS.md:1533-1545`). The #421
@@ -17,8 +39,9 @@ approves this diff. Steps 2 to 9 still go to Jason.
- Step 1 now says the install creates `mosaic` without sudo, FED R8 records Jason's acceptance, and the - Step 1 now says the install creates `mosaic` without sudo, FED R8 records Jason's acceptance, and the
in-VM tightening on .54 is Vikunja #423, which doesn't gate the install. in-VM tightening on .54 is Vikunja #423, which doesn't gate the install.
- Section 9 no longer counts on ops-01's step 2b to remove `melanie`'s sudo. That residual stays until - Section 9 no longer counts on ops-01's step 2b to remove `melanie`'s sudo. Jason's acceptance in FED
#423, and Jason's acceptance in FED R8 covers it and the factory's paths to .54. R8 covers the factory's paths to .54. The Hermes processes that could use her sudo are Mos's
residual until #423 step 5 (r7, mos-1064).
## Changes from r4 ## Changes from r4
@@ -914,7 +937,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, the `mosaic-stack.service` 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 no groups, `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 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-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 |
@@ -944,10 +967,11 @@ compaction count and clear context (GAP-MAP C2.12, C2.13). Until then, a new thr
## 9. Where it runs, Bean and Chet's move, and what can't be ready by 2026-10-27 ## 9. Where it runs, Bean and Chet's move, and what can't be ready by 2026-10-27
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 groups and no keys, home `/srv/mosaic` at on pve. `<user>` is `mosaic`, a system user with no sudo, no supplementary groups and no keys, home
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 the system unit `mosaic-stack.service` 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, Vikunja #421). The rest of the design 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
still names `<host>` and `<user>`, so a later move changes only the install inputs. The bus, data root, still names `<host>` and `<user>`, so a later move changes only the install inputs. The bus, data root,
mirror and runners live under `<user>`. It needs outbound HTTPS to git.mosaicstack.dev and the model mirror and runners live under `<user>`. It needs outbound HTTPS to git.mosaicstack.dev and the model
providers. It needs nothing from her user, because her writing reaches it through the Gitea remote she providers. It needs nothing from her user, because her writing reaches it through the Gitea remote she
@@ -959,10 +983,16 @@ measured 300 to 390 MB on dragon-lin. I estimated at least 2 GB more than the 3.
cores at 2026-10-21T09:00Z (mos-1031, mos-1038), which covers both. C2-13's precheck still prints free memory before the start. cores at 2026-10-21T09:00Z (mos-1031, mos-1038), which covers both. C2-13's precheck still prints free memory before the start.
Isolation on that host. `<user>` without sudo keeps the hub from touching her files. The reverse is the Isolation on that host. `<user>` without sudo keeps the hub from touching her files. The reverse is the
B7 residual from r2: `melanie` has passwordless sudo (mos-1000), and the factory reaches .54 through pve B7 residual from r2, and it has two parts.
root and ssh as `melanie` (jason-master-restart `projects/mosaic-mvp/MELANIE-HOST-RECON.md:27-39`). Jason accepted those paths (Q183), and FED R8 records - The factory's paths to .54: pve root, ssh as `melanie`, and the root her passwordless sudo gives
his acceptance instead of refusing to start. The in-VM tightening is Vikunja #423, and changes to the through those paths (mos-1000, jason-master-restart `projects/mosaic-mvp/MELANIE-HOST-RECON.md:27-39`).
`melanie` account wait until Bean and Chet are live (10 step 1). Jason accepted them (Q182, Q183), and FED R8 records his acceptance instead of refusing to start.
- The Hermes processes. They run as `melanie` and could use her sudo to read `/srv/mosaic`. Jason's
acceptance doesn't cover this part. It is Mos's residual (mos-1064). Q181 keeps Hermes running, and
it ends when #423 step 5 removes her sudo once Bean and Chet are live.
The in-VM tightening is Vikunja #423. Only its step 1, the `localadmin` account, gates the install
(10 step 2).
### 9.1 Moving Bean and Chet (C2-20) ### 9.1 Moving Bean and Chet (C2-20)
@@ -1014,15 +1044,24 @@ What it adds is one bot account with read access to that one repo.
1. The host is set (Q181): 10.1.1.54 as `mosaic` under `/srv/mosaic`, 16 GiB and 6 cores. Jason 1. The host is set (Q181): 10.1.1.54 as `mosaic` under `/srv/mosaic`, 16 GiB and 6 cores. Jason
confirmed Q4 (Q183), so #421 is closed and the install doesn't wait on it. confirmed Q4 (Q183), so #421 is closed and the install doesn't wait on it.
- The install creates the `mosaic` user without sudo (9). C2-13's precheck refuses if it has sudo or - Root steps on `<host>` create the `mosaic` user and its home, under step 2's go (2a). C2-13's
any group. precheck refuses if `<user>` has sudo or any supplementary group.
- FED R8 records Jason's acceptance of the factory's paths to .54. Gizmoduck's r4.10 of the - FED R8 records Jason's acceptance of the factory's paths to .54. Gizmoduck's r4.10 of the
federation design writes that wording, and it is in progress. federation design writes that wording, and it is in progress.
- The in-VM tightening on .54 is Vikunja #423. It doesn't gate the install. Changes to the `melanie` - The in-VM tightening on .54 is Vikunja #423. Only its step 1, which creates `localadmin`, gates
account wait until Bean and Chet are live. the install, through step 2. Its step 5 removes `melanie`'s sudo once Bean and Chet are live, and
2. Running the install on `<host>` as `<user>`, by him or his delegate: `scripts/bootstrap.sh`, release that ends Mos's residual (9).
package and activate, the pinned harness, and which model accounts its agents use. Model use is spend, 2. The install on `<host>`. Jason's go covers all three parts. 2a and 2c are root steps, owned by
so the accounts are his call. ops-01 (mos-1064). The path is ssh as `localadmin@10.1.1.54` with NOPASSWD sudo, which #423 step 1
creates. Neither uses `melanie`'s sudo or pve root. They are acts on Melanie's machine, so ops-01
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
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.
3. A new Gitea bot account for the hub, with read access to the books repo only. Not bean-brain, and not 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 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. admin, which is his call.
+8 -6
View File
@@ -2,8 +2,8 @@
Gosalyn, 2026-10-20 (from `date -u`), for sage-1329. Every section follows `docs/plans/BRIEF-TEMPLATE.md`. Gosalyn, 2026-10-20 (from `date -u`), for sage-1329. Every section follows `docs/plans/BRIEF-TEMPLATE.md`.
The design they build is `agents/gosalyn/work/case2/DESIGN.md` on `gosalyn/case2-design`. Webby The design they build is `agents/gosalyn/work/case2/DESIGN.md` on `gosalyn/case2-design`. Webby
approved r5 at e1635308b in webby-0021, with notes at dba33aaf6. r6 at 1508fd175 changes only section 10 approved r5 at e1635308b in webby-0021, with notes at dba33aaf6. r6 and r7 change only sections 9 and 10
step 1 and the isolation paragraph of section 9 (mos-1057, Q183), and that diff is with Webby. Below, "the design" means that and C2-13's row (mos-1057, Q183, webby-0023), and that diff is with Webby. Below, "the design" means that
file, with its section number. A builder follows the design and doesn't reopen a decision in it. A file, with its section number. A builder follows the design and doesn't reopen a decision in it. A
departure goes to Sage before the build continues. departure goes to Sage before the build continues.
@@ -721,12 +721,14 @@ starts.
The install does: bootstrap, release package and activate, the business file with the mapping, `bean` 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 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. Every host value is an input, so a later host move changes 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. only inputs.
The precheck refuses unless all of these hold: The precheck refuses unless all of these hold:
- The config validates. - The config validates.
- `<user>` has no sudo and no groups, and `bus.tester` is not on. - `sudo -n true` fails as `<user>`, and `id -G` prints exactly one entry, its primary group. A system
user always has one, so the check is for supplementary groups. `bus.tester` is not on.
- The business file, `.history`, `changes.jsonl` and persona files have the modes in design 4, and - The business file, `.history`, `changes.jsonl` and persona files have the modes in design 4, and
each persona digest matches. each persona digest matches.
- The run root is 0700. - The run root is 0700.
@@ -743,8 +745,8 @@ claude check runs against a fake claude that appends rather than replaces, and t
### Out of scope ### 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 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 r6. The in-VM tightening on .54 is #423 and doesn't gate the its hold lifts once Webby approves r7. The root steps 2a and 2c are ops-01's, over ssh as `localadmin`,
install. The Gitea bot account and the token slot are steps 3 and 4. Stopping Hermes 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. is step 9 and her choice.
### Gate ### Gate