5.8 KiB
Brief: the Case 2 proxy unit, so a hub installs P1's proxy unit
Written 2026-10-24 by Sage for Mos's mos-1616 ruling 1, which approved this row. Mos ruled that Sage
approves the brief if it only adds P1 item 7's unit to scripts/case2-install.sh and
scripts/case2-unit.sh: the same unit text, the same sha256 refusal and the Wants/After lines. Anything
beyond that goes to Mos. Read the rows doc's P1 brief (docs/plans/2026-10-24_model-proxy-rows.md, P1
item 7) and row 209's brief (docs/plans/2026-10-24_row209-hub-54-window.md, entry check 2) first. The
rows doc's "Rules for every row" apply here unchanged. Line numbers are at origin/refactor 58b2bbb3a, and
P1's paths are at its round 2 candidate magica/row329 86cb5c221. Check both once P1 lands.
Brief: Case 2 proxy unit in case2-unit.sh and case2-install.sh
Why. A Case 2 hub (.54) installs through scripts/case2-install.sh (as the stack user) and
scripts/case2-unit.sh (as root, system units). P1 adds mosaic-proxy.service only to the user-unit path
(scripts/instance.sh, scripts/proxy-service.sh, scripts/bus-service.sh). case2-unit.sh writes the
bus and Console units and nothing else (:3-5, :56-57), and its bus unit has no Wants= or After= on a
proxy. Without this row, row 209 phase A can't run the proxy on .54.
What ships.
- The proxy unit, from P1's template.
case2-unit.shrenders a third system unit,mosaic-proxy.service, from P1'spackages/cli/systemd/mosaic-proxy.service.in, which this row doesn't change.- It uses the same render as the bus (
render():435): the copy for@REPO@, the unit PATH,--nodefor@NODE@, and@CONFIG_ENV@deleted, becausecase2-unit.shrenders the default instance only. - It gets the same added lines as the bus:
User=,Group=and the four hardening lines, with no capabilities and noEnvironmentFile=.WantedBy=multi-user.targetreplacesdefault.target, as for the other two units. check_unitgets aproxykind. Its fixed lines are those of P1's template:ExecStart=exactly<node> <copy>/packages/model-proxy/src/run.mjs, andNoNewPrivileges=yes. Every check that applies to both units today applies to this one too.- A new test seam
CASE2_PROXY_TEMPLATE, likeCASE2_BUS_TEMPLATE(:44, :54).
- It uses the same render as the bus (
- The sha256 refusal is P1's. The unit starts P1's
run.mjs, which checks the binary against PIN before it spawns it. This row adds no second check and no other way to start the proxy. A test runs the renderedExecStart=in a temp root, with a made-up config and a binary that doesn't match PIN, and checks P1's refusal code and exit status. - Only when the instance has a proxy. Root passes a new bare flag
--proxywhen the instance's config has aproxysection.- Without it, the render is byte for byte what it is today, and no proxy unit is written.
- With it, the bus unit gets
Wants=mosaic-proxy.serviceandAfter=mosaic-proxy.service, in the place where P1'sbus-service.shputs them, right afterDescription=. The Console unit doesn't change. - Root still never imports copy code or reads the config (:16-21). A wrong flag fails closed: with no proxy section, P1's runner refuses and the unit stops. With a proxy section and no flag, the proxy doesn't run and P2's launch preflight refuses. BUILD.md checks both against the landed P1 and P2 code and names the refusal codes.
- Re-install and start.
- With
--proxy, the three installed units must name one copy, and each must equal the render once its copy id is the new one, the same rule as now (:557-571). - Without
--proxy, an installedmosaic-proxy.servicerefuses the run. The script never deletes a unit file. - Enable all of them, then start the proxy, then the bus, then the Console. A unit that was already running is reported the way the bus and Console are now (:576-610), and the message names all three as the units to restart together.
- With
- case2-install.sh. Only its closing "Next:" line (:231) changes, to tell root to add
--proxywhen the config has aproxysection. If the builder finds that line needs no change, BUILD.md says why and the file stays as it is.
Out of scope. P1's template, runner and PIN; the user-unit path; any named-instance render; reading the
config from root; the proxy's config or key; mosaic proxy init; any live host, unit or /etc write
outside a test's temp root.
Tests, in packages/cli/tests/case2-unit.test.mjs (and case2-install.test.mjs if item 5 changes the
script):
- with no
--proxy, the bus and Console renders equal today's, byte for byte; - with
--proxy, the proxy unit's content, and the bus unit'sWants=andAfter=lines; - each
check_unitrefusal on the proxy kind, by its message; - P1's PIN refusal through the rendered
ExecStart=; - a re-install whose three units name two copies refuses;
- an installed proxy unit with no
--proxyrefuses and deletes nothing; - the start order through the
CASE2_SYSTEMCTLseam's log.
Gate.
- The builder is a new Codex gpt-6.1-sol thread and worktree off refactor (Q246, mos-1542). The row starts when P1 (row 329) lands, and the builder rebases on it before any run.
- Boyd gives the first Codex review. Webby gives the Claude review of the root-run unit boundary and the landing. Sage runs the landing gate.
- The run list for every whole-file run comes from imports and goes in the builder's report before the run.
packages/cli/tests/case1-accept.test.mjsstays off unless Mos approves (mos-1612). - Every touched test file runs whole. Every package suite but queue, plus every
scripts/test-*.sh, is green on Sage's landing gate. - Named mutants: the bus unit loses its
After=line under--proxy; the proxy unit'sExecStart=skipsrun.mjsand starts the binary directly.