Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8f2bc7233 | ||
|
|
9f60021a00 | ||
|
|
e29d8ec909 |
No files matched your search
@@ -5585,7 +5585,3 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r
|
||||
2026-10-24T23:37:46Z | Sage | Row 329 (#1827, P1 pinned model proxy, Magica) LANDED 61ebf2c34, pushed 23:31:27Z 10-24. Before: refactor at 669cdf42c. After: Magica's 4 commits (94cde633e, c65fb34ee, d1f6f2b19, 61ebf2c34) cherry-picked onto 669cdf42c, diff vs the gated tree limited to drift paths, range-diff in scratch. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r329l/wt (gate.sh 8b4978887 c8a9bcf94), started 23:08:12Z at load 14.35, GATE GREEN 23:29:14Z at load 8.67, no rerun, no test or timeout edit (mos-1634). Host note: two Codex workers cleaned T3 worktrees from about 23:14Z (mos-1639, Vikunja 632). Approvals: Dewey r2 c31643 rev 4047, Boyd r2 c31642 rev 4048. #1827 comment 31650, closed. Vikunja 237 comment 1109 (stays open for P2-P7); C4 P10 red filed as Vikunja 631. Queue: note rev 4063, done rev 4064, committed b24d917ec. DEFERRED: N2 and R2-N1 bound to row 331, N1 bound to P7. Overlap: row 284 (packages/bus/src/broker.mjs) and row 157 (scripts/mosaic-config.mjs) rebase onto 61ebf2c34 before their gates.
|
||||
2026-10-25T00:15:48Z | Sage | Row 284 (#1789, C2-05c library.get scope, Della) LANDED ce0b6ed41, pushed 00:09:52Z 10-25. Before: refactor at 9e2689dff (row 329 landing docs). After: fast-forward to Della's 7 rebased commits 78b932feb..ce0b6ed41; 8 paths, manifest-r4 de505cfd matches the landed tree. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r284l/wt (the 329 gate.sh with D and the compose project renamed), started 23:51:19Z at load 1.62, GATE GREEN 00:09:04Z at load 5.20, no rerun, no test or timeout edit. Counts: bus 1020/0, business 283/0, cli 999 pass 1 allowed skip (FED R7), harness 177/0, library 156/0, seat 218/0, webui 458/0, every scripts/test-*.sh rc 0; queue 34 and task 87/93 skips are the standing ones. Della's whole-file runs on ce0b6ed41: library-lookups 12/0, library-tools 5/0, query 34/0. Approval: Webby round 2 delta c31654 rev 4070 (round 1 rev 4062). Overlap sweep: rows 13, 157, 309, gyro/row310 and fethry/row332-p1 clean. #1789 comment 31655, closed. No Vikunja task for the row. Queue: note rev 4071, done rev 4072, committed 7c8dc2033. DEFERRED: Webby N1 (whole-tree listing per get) bound to the next library read-path row; N2 needs nothing; N3 is this gate.
|
||||
2026-10-25T00:18:35Z | Sage | Follow-up tasks for mos-1647 item 3 (Q109): Vikunja 633 (P7 cutover, CLIProxyAPI 8.0.42 startup Grok-npm and antigravity version refreshes, from row 332 isolated runs) and 634 (row 284 Webby N1, library get lists the whole tree). DEFERRED gains the 633 entry and the 634 number for row 284 N1. Filing script and receipt in agents/sage/work/vikunja-setup/.
|
||||
|
||||
2026-10-25 — Rockerduck row334 (#1832), before: Case 2 renders bus/Console only. Plan: reuse P1 proxy template/runner under --proxy, enforce three-unit re-install checks and proxy→bus→Console starts, cover refusals and both named mutants. Tests held for Sage CLEAR; design in agents/rockerduck/work/queue-334/BUILD.md.
|
||||
|
||||
2026-10-25 — Rockerduck row334 (#1832), RED evidence: whole case2-unit.test.mjs against baseline has 48 pass/33 expected new fail; whole case2-install.test.mjs has 27 pass/1 expected new fail. All 74 older tests pass. Implementations restored. Sage sage-4390 authorizes seat-branch candidate commit/push before GREEN; GREEN/mutants held until separate CLEAR after row331. Per-failure evidence: agents/rockerduck/work/queue-334/red-results.txt.
|
||||
@@ -0,0 +1,402 @@
|
||||
# Row 255 design: a bundled Pocket ID for users with no OIDC provider (Q203, Gitea #1760, Vikunja 458)
|
||||
|
||||
Launchpad, draft 1, 2026-10-25, on `launchpad/row255` off refactor 78b69e1e6. Brief
|
||||
`docs/plans/2026-10-21_bundled-pocket-id.md` (blob 26f34400). Reviewer Webby. Sage checks it against Q203, O6,
|
||||
REQ-WEB-2 and REQ-INST-1. REQ-INST-2 (PRD 1.10) is post-v1 (mos-1204), and so is everything here.
|
||||
|
||||
This is a design only. Nothing here installs, downloads or starts Pocket ID. It changes nothing on .53, .54 or
|
||||
dragon-lin, which keeps Authentik.
|
||||
|
||||
## Sources
|
||||
|
||||
- Q203 and O6 as the brief quotes them. PRD `docs/prd/mosaic-stack.md` REQ-WEB-2 (:902), REQ-INST-1 (:1509),
|
||||
REQ-INST-2 (:1513) and REQ-INST-3 (:1528). Lead decision 44 items 5 and 6
|
||||
(`docs/plans/2026-09-26_lead-decisions.md:655-663`).
|
||||
- The Q175 design `agents/sage/work/q175-sso/DESIGN.md` sections 4 and 10, and the code row 192 landed:
|
||||
`packages/webui/src/sso-config.mjs` (the `console.sso` shape), `packages/webui/src/sso.mjs` (the slot read,
|
||||
the discovery checks at :105-118, the client at :147-148, the callback at :133, the post-logout URI at :332).
|
||||
- Researcher's note `agents/researcher/work/2026-10-04_vikunja-pocketid.md` part 2 (v2.17.0).
|
||||
- A fresh read of upstream on 2026-10-25, from release metadata and source text only: no binary was fetched or
|
||||
run. The latest stable release is **v2.19.1** (2026-10-22). Source paths below are under
|
||||
`backend/internal/` at tag v2.19.1 in `github.com/pocket-id/pocket-id`, marked "P". Releases v2.18.0,
|
||||
v2.19.0 and v2.19.1 are not marked breaking.
|
||||
|
||||
## What the Console already needs from any provider
|
||||
|
||||
This design keeps the Console's sign-in and start checks exactly as row 192 built them. Pocket ID becomes one
|
||||
`console.sso.providers` entry, the same shape any compliant OP uses (Q175 section 4, "Several providers"). The
|
||||
Console gets no new sign-in path, no password and no proxy-header identity (O6, REQ-WEB-2, REQ-INST-2). The
|
||||
facts that drive the rest of the design:
|
||||
|
||||
- `publicOrigin` and every `issuer` must be https (`sso-config.mjs` `httpsURL`). A plain-http Pocket ID can't
|
||||
be a provider at all.
|
||||
- Each provider needs 1 to 64 `subjects`. A `console.sso` with no subject refuses to load. So the first
|
||||
person's subject has to exist before the Console starts with `--sso` (section 5).
|
||||
- The client authenticates with `client_secret_basic` (`sso.mjs:148`), so the client is confidential.
|
||||
- The slot is `<configDir>/secrets/mosaic-stack/<slot>`: a regular file, owned by the Console's uid, mode
|
||||
with no group or other bits, and outside the repo and every stack copy (`readSlot`).
|
||||
- `config.json` is user-authored. bootstrap.sh creates it and nothing overwrites it (canon invariant 2, Q175
|
||||
section 4). This matters for sections 1 and 5.
|
||||
|
||||
## 1. The choice
|
||||
|
||||
The installer is REQ-INST-3's install command (Vikunja #432; today `stack-upgrade.sh` and the per-case
|
||||
install scripts). It takes one sign-in option per instance:
|
||||
|
||||
- `--oidc existing`. The user points the Console at one or more OIDC providers they already have. Nothing
|
||||
is installed. The user writes `console.sso` themselves, as Q175 step 4 does today. The installer writes
|
||||
nothing into `console.sso`; it prints the entry shape and the two URIs to register
|
||||
(`<publicOrigin>/auth/callback`, `<publicOrigin>/auth/signed-out`).
|
||||
- `--oidc bundled`. The stack installs Pocket ID as an install component (section 2), and
|
||||
`console.sso.providers` holds exactly one entry, `id: "pocket-id"`, whose `issuer` is the component's
|
||||
origin.
|
||||
- No option keeps today's behaviour: no `console.sso`, and the Console runs without `--sso`.
|
||||
|
||||
How the bundled choice is recorded. REQ-INST-1 lets a component be switched on "in the organization file
|
||||
or system config". It has to be the system config. The organization file can be written by agents through
|
||||
`organizationWriter`, which is the reason Q175 B1 put `console.sso` in config.json, and the same reason
|
||||
holds for a component that issues the Console's identities. So config.json gets one new optional key:
|
||||
|
||||
```json
|
||||
"components": {
|
||||
"pocket-id": {
|
||||
"release": "v2.19.1",
|
||||
"origin": "https://id.example.internal",
|
||||
"port": 1411
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The loader (`scripts/mosaic-config.mjs`) checks the shape: `release` is a `vX.Y.Z` tag, and the component script refuses one that
|
||||
differs from the pin file (section 2), `origin` is an https origin, `port` is 1024 to 65535. It also cross-checks: when
|
||||
`components["pocket-id"]` is present, `console.sso` must have exactly one provider, `id` `pocket-id`,
|
||||
`issuer` equal to `origin`. A config with `console.sso` providers and no `components` is the existing
|
||||
choice and loads as today.
|
||||
|
||||
**M1, approved by Mos with three conditions (mos-1666 item 3).** `components` is a new top-level config key.
|
||||
The loader refuses unknown keys today, so this is a config schema change. The conditions:
|
||||
|
||||
1. **Rollback trap.** A copy older than P1 refuses a config that has the key, the same trap Q175 section 4
|
||||
names for `console.sso`. P1 documents the order. On a rollback past P1, first take `components` out of
|
||||
config.json (restore the backup made before the key went in), then roll back the pointer. The STATE check
|
||||
after the rollback reads config.json and confirms the key is absent.
|
||||
2. **Strict inside the key.** The loader refuses any name under `components` other than `pocket-id`, and any
|
||||
field under `pocket-id` other than `release`, `origin` and `port`. Each refusal names the key path and
|
||||
quotes no value, as `checkConsole` does.
|
||||
3. **test-config.sh cases:**
|
||||
- a valid `components` loads;
|
||||
- a malformed one fails, naming the bad key path;
|
||||
- a config without `components` loads;
|
||||
- the cross-check refuses a `console.sso` whose provider doesn't match `origin`.
|
||||
|
||||
What each choice writes into `console.sso` is in section 5. For the bundled choice, Pocket ID is the only
|
||||
providers entry. The design keeps it that way because REQ-INST-2 says "the Console's one providers entry".
|
||||
A user who later adds an existing provider moves to `--oidc existing` and removes the component (section 2).
|
||||
|
||||
## 2. The component, against REQ-INST-1
|
||||
|
||||
**Pinned.** A Pocket ID release is a set of raw binaries plus `checksums.txt` (sha256). From v2.16.0 there is
|
||||
an SPDX SBOM per binary, and the release workflow attests `checksums.txt` through Sigstore (`actions/attest`
|
||||
in `.github/workflows/release.yml`). The repo holds one pin file,
|
||||
`scripts/pocket-id-pins.sh`, sourced the way `scripts/case1-pins.sh` is: the tag, and for each supported platform (linux amd64 and arm64 to
|
||||
start) the asset name and its sha256. The build row copies the sha256 from `checksums.txt` at the tag and
|
||||
records the `gh attestation verify` output for each asset as its evidence. The pin is checked by hand at
|
||||
build time, not trusted at install time. No Pocket ID source and no binary goes in the repo. The binary is
|
||||
not in any image either, since the container install comes after v1 (REQ-INST-3).
|
||||
|
||||
Recommended pin: v2.19.1, the current release. The build row checks it again, because Pocket ID ships a
|
||||
minor every one to four weeks. Upgrading is a pin file change in a reviewed commit.
|
||||
|
||||
**Fetched and checked.** `scripts/component-pocket-id.sh fetch` downloads the pinned asset for the host's
|
||||
platform over https into a new 0700 staging directory, computes its sha256, and compares it with the pin.
|
||||
On a mismatch it deletes the file and refuses with `pocket-id-digest-mismatch`, naming the expected value
|
||||
and the computed one. On a match it installs the binary read-only (0555) at
|
||||
`<dataRoot>/components/pocket-id/bin/pocket-id-<tag>`. The fetch is the only network step, and the user
|
||||
runs it at install. Tests never run it against the network (section 9).
|
||||
|
||||
**Runs.** A systemd user unit, `mosaic-pocket-id.service` (`mosaic-pocket-id-NAME.service` for a named
|
||||
instance, as row 190 names the bus and webui units), as the instance's user:
|
||||
|
||||
- `ExecStart=<dataRoot>/components/pocket-id/bin/pocket-id-<tag>`.
|
||||
- `WorkingDirectory=<dataRoot>/components/pocket-id/run`, a directory the unit owns, so Pocket ID's habit of
|
||||
reading `.env` from the working directory reads nothing we didn't write. The build row checks there is no
|
||||
`.env` there at start.
|
||||
- Environment, all from the rendered unit:
|
||||
- `APP_URL=<origin>` and `HOST=127.0.0.1` (P `common/env_config.go`: HOST defaults to `0.0.0.0`).
|
||||
- `PORT=<port>`.
|
||||
- `DB_CONNECTION_STRING` and `UPLOAD_PATH` as absolute paths under `<dataRoot>/components/pocket-id/data/`
|
||||
(P defaults are `data/pocket-id.db` and `data/uploads`, relative to the working directory).
|
||||
- `ALLOW_USER_SIGNUPS=disabled` and `ALLOW_INSECURE_CALLBACK_URLS=false`.
|
||||
- `EMAIL_ONE_TIME_ACCESS_AS_UNAUTHENTICATED_ENABLED=false` and `..._AS_ADMIN_ENABLED=false`.
|
||||
- The `OUTBOUND_ALLOWED_HOSTS_*` allowlists set empty (added in 2.19.0).
|
||||
- No `TRUST_PROXY` unless section 6's proxy step sets it to the proxy's address.
|
||||
- `ENCRYPTION_KEY` (mandatory since v2.0, at least 16 bytes) never goes in the unit text. It lives in slot
|
||||
`<configDir>/secrets/mosaic-stack/pocket-id-encryption-key`, 0600. The component script generates it from
|
||||
`/dev/urandom` straight into the file and never prints it. The unit passes it with
|
||||
`LoadCredential=` and `ENCRYPTION_KEY_FILE=%d/...`. The build row confirms `ENCRYPTION_KEY_FILE` at the pinned
|
||||
tag; if it isn't there, it refuses rather than putting the key in `Environment=`.
|
||||
- The unit never carries `STATIC_API_KEY`. That key exists only during the enroll run (section 4).
|
||||
|
||||
The data directory holds the SQLite database, Pocket ID's signing key (stored encrypted with
|
||||
`ENCRYPTION_KEY`), and the passkey registrations. It is 0700. Agent sandboxes bind only an allowlist
|
||||
(`packages/seat/src/sandbox.mjs`), and `<dataRoot>/components/` is not on it. The build row adds a sandbox
|
||||
test that pins that.
|
||||
|
||||
**Health-checked.** `GET http://127.0.0.1:<port>/healthz` returns 204 (P `controller/healthz_controller.go`).
|
||||
`component-pocket-id.sh status` reports the unit state, the binary tag against the pin, the binary's sha256
|
||||
against the pin, and the health result. The Console's own `/healthz` already shows `sso: { "pocket-id": ready
|
||||
| unreachable | refused }` from its discovery retries, so an operator sees the provider from either side.
|
||||
|
||||
**Removable.** `component-pocket-id.sh remove` refuses while `console.sso` still names `pocket-id`, so the
|
||||
Console can't be left pointing at a provider that's gone. Once the user has taken the entry out (or moved to
|
||||
`--oidc existing`), the script stops and removes the unit, then moves the data directory to
|
||||
`<dataRoot>/components/pocket-id.removed-<utc>` with a receipt line in `<dataRoot>/state/components.log`
|
||||
(append-only). It never deletes the passkey database outright. Deleting the moved directory and the
|
||||
encryption key slot is a separate `purge` verb that names both paths and needs `--yes`. Removing the
|
||||
`components` key from config.json is the user's edit.
|
||||
|
||||
## 3. The Q175 start checks against the bundled instance
|
||||
|
||||
The Console checks are unchanged. Here is each one against Pocket ID, and the setting it needs:
|
||||
|
||||
| Check (row 192) | Pocket ID v2.19.1 | Default that fails | Fix |
|
||||
|---|---|---|---|
|
||||
| `issuer` is https (loader) | issuer = `APP_URL` exactly (P `controller/well_known_controller.go`) | `APP_URL` defaults to `http://localhost:1411` | `APP_URL=<https origin>`, section 6 |
|
||||
| discovery `issuer` equals config `issuer` | equal when `APP_URL` has no trailing slash; Pocket ID refuses a path | none | config `issuer` = `origin`, no trailing slash; the loader cross-check compares them |
|
||||
| `id_token_signing_alg_values_supported` overlaps `algs` | advertises only the current key's alg. RS256 (RSA-2048) by default (P `service/jwt_service.go`) | none | `algs: ["RS256"]` |
|
||||
| `code_challenge_methods_supported` has `S256` | advertises `plain` and `S256` | none | none. The Console always sends S256 (`sso.mjs:221`) |
|
||||
| JWKS has a usable key for `algs` | one key at `/.well-known/jwks.json` | none | none. The build row's fake OP pins `use`/`alg`/`kid` as Pocket ID emits them |
|
||||
| `end_session_endpoint` is https | `<APP_URL>/api/oidc/end-session` | http, if `APP_URL` is http | same fix as row 1 |
|
||||
| `redirect_uri` is registered | exact match first, then wildcard patterns; loopback callbacks match any port (P `utils/callback_url_util.go`) | `ALLOW_INSECURE_CALLBACK_URLS` defaults to true | register exactly `<publicOrigin>/auth/callback`, with no `*`; set `ALLOW_INSECURE_CALLBACK_URLS=false` |
|
||||
| post-logout URI | per-client `logoutCallbackURLs`, same matching | none | register exactly `<publicOrigin>/auth/signed-out` |
|
||||
| ID token alg in `algs`, `iss` match | as above | none | none |
|
||||
|
||||
Two points the Console doesn't check, set on the client anyway:
|
||||
|
||||
- `pkceEnabled: true`. Then Pocket ID refuses an authorize request that has no `code_challenge`. Pocket ID has
|
||||
no per-client S256-only setting (P `oidc/provider.go` turns on `plain` for the server). That doesn't relax
|
||||
anything: the Console sends S256 every time, and `plain` stays available only to other clients.
|
||||
- `isPublic: false`, which matches `client_secret_basic`.
|
||||
|
||||
Key rotation. `pocket-id key-rotate --alg ...` replaces the one signing key (P `cmds/key_rotate.go`). After a
|
||||
rotation to another alg, the Console's discovery check refuses on the next restart (no overlap with
|
||||
`algs`), and that is the intended fail-closed result. A rotation is a user step that updates `algs` in the
|
||||
same edit. The component script never rotates.
|
||||
|
||||
## 4. The client, and where its secret goes
|
||||
|
||||
The person running the install creates the client through `component-pocket-id.sh enroll`. No agent runs
|
||||
this verb. It refuses when any agent marker is in its environment: the `MARKERS` list in
|
||||
`packages/bus/src/human.mjs` (`MOSAIC_RUN_ID`, `MOSAIC_BUS_CAP`, `CLAUDECODE` and the rest) that the human
|
||||
boundary uses. A test pins the refusal.
|
||||
|
||||
Enroll needs admin rights before any person has a passkey. Pocket ID offers two ways to get them, and one of
|
||||
them is a hazard:
|
||||
|
||||
- `/setup` and its backing `POST /api/signup/setup` are unauthenticated: whoever calls first becomes admin
|
||||
(P `usersignup/service.go`). Sandboxed agents share the host's network namespace (`sandbox.mjs` unshares
|
||||
user, pid and ipc, not net), so any agent could reach a loopback Pocket ID and take that first admin.
|
||||
- `STATIC_API_KEY` is a fixed admin key with no expiry (P `apikey/service.go`). It doesn't count as
|
||||
completing setup, so `/setup` stays open while it's set.
|
||||
|
||||
So enroll never lets the real unit serve before an admin person exists. It runs the pinned binary once in a
|
||||
**private network namespace** (`unshare --user --net`, or `bwrap --unshare-net` with the binary and data
|
||||
directory bound), where only enroll's own loopback can reach it:
|
||||
|
||||
1. Generate a one-run static key into a 0600 temp file in a new 0700 directory, and start Pocket ID inside
|
||||
the namespace with `STATIC_API_KEY_FILE` pointing at it, plus the unit's environment.
|
||||
2. Wait for `/healthz` 204.
|
||||
3. Create the admin user, `POST /api/users` with `isAdmin: true` and the username and email given with
|
||||
`--admin-user` and `--admin-email`. Keep the returned `id`. That is the OIDC `sub` (P
|
||||
`oidc/claims_service.go` uses `user.ID`, a UUIDv4 set at creation, so it's stable).
|
||||
4. Create the Console client: client id `mosaic-console-<instance>`, callback and logout URLs exactly as in
|
||||
section 3, `pkceEnabled: true`, `isPublic: false`. Supply the secret instead of letting Pocket ID
|
||||
generate it. Enroll generates 48 random bytes, base64url, and writes them straight to slot
|
||||
`<configDir>/secrets/mosaic-stack/console-sso-pocket-id` (0600, `O_CREAT|O_EXCL`, not a symlink). It sends
|
||||
the same bytes in the create body over the namespace's loopback (`dto/oidc_dto.go` accepts a supplied
|
||||
secret of 16 or more characters). The value is never printed, logged or put in an argv, and a test pins
|
||||
that it appears in no output.
|
||||
5. Mint a one-time access token for the admin, `POST /api/users/<id>/one-time-access-token`.
|
||||
6. Stop the namespaced process, then shred and remove the static key file. The real unit has never had a
|
||||
static key, so no admin key survives enroll.
|
||||
7. Start the real unit and wait for `/healthz` 204.
|
||||
8. Print the sign-in link `<origin>/lc/<token>` to the user's terminal (section 5), and the `console.sso`
|
||||
entry (section 5).
|
||||
|
||||
The build row confirms each route and body against the pinned tag's source before it writes the client (the
|
||||
upstream read for this design didn't trace the create-client route). If any of steps 1 to 7 fails, enroll
|
||||
stops the process, removes the static key, leaves no slot file it created, and refuses with the step name.
|
||||
|
||||
The secret never enters the repo or an image. It sits in two places only: the slot file and Pocket ID's
|
||||
database (stored as a hash). The Console's `readSlot` reads it as it does any provider's.
|
||||
|
||||
## 5. The first person, and their subject in `console.sso`
|
||||
|
||||
Pocket ID is passkey-only for people (REQ-INST-2). The installing user enrolls like this:
|
||||
|
||||
1. Enroll prints `<origin>/lc/<token>`. The token is valid for one hour and is a credential, so it goes only
|
||||
to the terminal of the person who ran enroll. It goes in no log and no file.
|
||||
2. The user opens it in a browser that can reach `origin` (section 6). Pocket ID signs them in and asks them
|
||||
to add a passkey. Without one they can't sign in again. If the hour passes, `pocket-id
|
||||
one-time-access-token <username>` (P `cmds/one_time_access_token.go`), run by the user against the data
|
||||
directory, prints a new link. `component-pocket-id.sh relink` wraps that call.
|
||||
3. Enroll already has the subject, the `id` from step 3 of section 4. It doesn't wait for a sign-in or read a
|
||||
`subject-unknown` log line.
|
||||
|
||||
How the subject gets into `console.sso`. config.json is user-authored and never auto-written. Two ways
|
||||
to respect that; I recommend the first:
|
||||
|
||||
- **R1 (recommended).** Enroll prints the full entry and writes nothing:
|
||||
|
||||
```json
|
||||
"console": { "sso": { "publicOrigin": "<publicOrigin>", "providers": [ {
|
||||
"id": "pocket-id", "issuer": "<origin>", "clientId": "mosaic-console-<instance>",
|
||||
"clientSecret": { "slot": "console-sso-pocket-id" }, "algs": ["RS256"],
|
||||
"subjects": [ { "sub": "<id>", "name": "<admin-user>" } ] } ] } },
|
||||
"components": { "pocket-id": { "release": "<tag>", "origin": "<origin>", "port": <port> } }
|
||||
```
|
||||
|
||||
The user pastes it into config.json, as with Q175 step 4. Rerunning `scripts/bootstrap.sh` (or
|
||||
`--instance NAME`) then validates it through the loader, including the section 1 cross-check, and writes
|
||||
nothing. Nothing changes in invariant 2.
|
||||
- **R2 (rejected, Sage sage-4401: R1 chosen).** `enroll --write-config` would write those two keys itself, with a backup of config.json first, and
|
||||
refuse if `console` or `components` were already present. That would be the first tool to modify an
|
||||
existing config.json, against canon invariant 2's "never overwritten" practice. Not built.
|
||||
|
||||
Whose subject. Enroll creates one person, the one running it, and only that subject goes in the entry. On
|
||||
Jason's instances, only his subject goes into `console.sso` (mos-1197). Further people are added with
|
||||
Pocket ID's admin UI (a user plus a one-time link), and their subjects go into `console.sso` by the user's
|
||||
edit, never by an agent. Pocket ID users who aren't in `subjects` can sign in to Pocket ID, but the Console
|
||||
refuses them `subject-unknown`, so REQ-WEB-2's "must map to an authorized human" holds.
|
||||
|
||||
## 6. Reach: origin, TLS and proxy
|
||||
|
||||
WebAuthn takes its RP id from `APP_URL`'s host name, and its allowed origin is `APP_URL`
|
||||
(P `webauthn/module.go`). Browsers allow passkeys only on a secure origin. The Console also needs https for
|
||||
both `publicOrigin` and `issuer`. Pocket ID serves plain http, and I found no TLS listener setting in its
|
||||
environment config. So:
|
||||
|
||||
- Pocket ID always binds `127.0.0.1:<port>`. That is the default, and the component never binds anything
|
||||
else (`HOST=127.0.0.1`; the status verb refuses a unit whose `HOST` differs).
|
||||
- `origin` is an https origin served by a TLS front that the user already has for the Console. The Console
|
||||
needs one anyway, since `publicOrigin` is https. Pocket ID goes behind the same front under **its own host
|
||||
name**, for example `id.<console host>`. Pocket ID doesn't support a path prefix (`APP_URL` must have no
|
||||
path). A separate host name also keeps Pocket ID's cookies apart from the Console's
|
||||
`__Host-mosaic_*` cookies; the two share cookies if they sit on one host name on different ports.
|
||||
- The front reaches Pocket ID on loopback, so the front has to run on the same host. If it doesn't, Pocket
|
||||
ID would need a non-loopback bind, and that is a `network.expose` hard wait (REQ-WAIT-1), not a component
|
||||
option.
|
||||
- `TRUST_PROXY` is set to the front's address only, never `true`.
|
||||
- The Console fetches discovery and JWKS from `issuer` over https from the server side, so the Console's
|
||||
node has to trust the front's certificate. A public CA does that. A private CA needs
|
||||
`NODE_EXTRA_CA_CERTS` in the Console unit, which the build row adds as a render flag.
|
||||
|
||||
These steps are the user's, and on Jason's hosts they are Jason's, because each one changes who can reach
|
||||
the Console or Pocket ID, or who the host trusts:
|
||||
- J1: the DNS name for `origin`;
|
||||
- J2: the certificate for it, or a private CA and its trust in the browser and in `NODE_EXTRA_CA_CERTS`;
|
||||
- J3: the TLS front's route from `origin` to `127.0.0.1:<port>` (a Traefik change on Jason's hosts);
|
||||
- J4: any listener off loopback.
|
||||
|
||||
The component checks and refuses; it never does them itself. With none of them done, Pocket ID runs on
|
||||
loopback with an https `APP_URL` that nothing serves yet. Enroll's step 2 health probe goes straight to
|
||||
`127.0.0.1`, so enroll still works. The person can't add a passkey until J1 to J3 are done.
|
||||
|
||||
## 7. Agents
|
||||
|
||||
Unchanged. Agents keep their per-role service tokens (lead decision 44 item 6, REQ-INST-2). Pocket ID issues
|
||||
nothing an agent uses. The Console client is the only client enroll creates. No API, no client-credentials
|
||||
grant and no admin API key exist after enroll. Residuals, stated plainly:
|
||||
|
||||
- Agents can reach `127.0.0.1:<port>`, because the sandbox shares the host network. What they can reach there
|
||||
is the public sign-in pages, discovery and JWKS. Signup is disabled, email codes are off, `/setup` is
|
||||
closed because an admin exists, and there is no static key. A test of the rendered unit pins all four
|
||||
settings.
|
||||
- The data directory and the two slots are outside every sandbox bind (section 2).
|
||||
- A seat that ran as the same OS user outside a sandbox could read the data directory. The Console's
|
||||
existing slot has the same exposure; this design adds no new class of it.
|
||||
|
||||
## 8. Instances
|
||||
|
||||
One Pocket ID per named instance, never shared:
|
||||
|
||||
- Each instance has one system config (row 190), and `components` and `console.sso` live in it. A shared
|
||||
Pocket ID would make one instance's config depend on a unit, a data directory and an encryption key that
|
||||
belong to another instance's config and data root.
|
||||
- Unit names, ports, data directory, slots and removal are all per instance (`mosaic-pocket-id-NAME`,
|
||||
`<dataRoot of NAME>/components/pocket-id`), so removing one instance's component can't break another's
|
||||
sign-in.
|
||||
- Each instance needs its own `origin` (section 6), so passkeys are per instance too. A person enrolls once
|
||||
per instance.
|
||||
- An instance that wants to use another instance's Pocket ID can still do it with `--oidc existing`. Then it
|
||||
is just an OIDC provider, with its own client registered by that instance's owner. Nothing special-cases it.
|
||||
|
||||
Two instances on one host each need their own `port`. The loader can't see other instances' configs, so
|
||||
`component-pocket-id.sh` refuses at fetch and enroll when the port is already bound on loopback.
|
||||
|
||||
## 9. Tests, stand-ins only
|
||||
|
||||
No test downloads or starts a real Pocket ID, and no test touches auth.diversecanvas.com,
|
||||
mosaic.woltje.com or 127.0.0.1:7332. The stand-ins:
|
||||
|
||||
- **Fake Pocket ID OP**, a node server like row 192's fake OP, shaped from v2.19.1:
|
||||
- the discovery fields `well_known_controller.go` emits (issuer = origin, `plain` and `S256`,
|
||||
`end_session_endpoint` at `/api/oidc/end-session`, the three auth methods);
|
||||
- one RS256 JWK at `/.well-known/jwks.json`;
|
||||
- a token endpoint that takes only `client_secret_basic`;
|
||||
- `/healthz` 204.
|
||||
|
||||
The Console starts against it under `--sso` with the section 5 entry, with no check changed. Negative
|
||||
cases:
|
||||
- an http issuer refuses at load;
|
||||
- an ES256-only JWKS against `algs: ["RS256"]` refuses at discovery;
|
||||
- an issuer with a trailing slash refuses on the cross-check.
|
||||
- **Fake admin API** on the same stand-in: `POST /api/users`, the client create, the one-time token, and a
|
||||
static-key check. Enroll runs against it with the binary replaced by a stub script that serves the stand-in.
|
||||
The tests pin:
|
||||
- the namespace argv;
|
||||
- the slot file's mode and `O_EXCL`;
|
||||
- that the secret appears in no stdout, stderr or argv;
|
||||
- the static key file gone after success and after a failure at each step;
|
||||
- the refusal under an agent marker in the environment.
|
||||
- **Fetch**: a local file served by a test https server (self-signed, trusted only by the test) with a
|
||||
matching pin, a mismatched pin and a truncated file.
|
||||
- **Unit render**: the environment table in section 2. `HOST=127.0.0.1`, no `STATIC_API_KEY`, no
|
||||
`ENCRYPTION_KEY` value in the text, and `ALLOW_USER_SIGNUPS=disabled`.
|
||||
- **Loader**: test-config.sh cases for the `components` key and the cross-check, and the copy-rollback note.
|
||||
- **Sandbox**: `<dataRoot>/components/` is in no bind list.
|
||||
|
||||
## 10. Build rows, in landing order
|
||||
|
||||
All are post-v1 (REQ-INST-2). Each row has Webby or Sage's named reviewer and its own gate. None of them
|
||||
runs on .53, .54 or dragon-lin, and none installs Pocket ID on any host.
|
||||
|
||||
1. **P1, loader key.** `components["pocket-id"]` in `mosaic-config.mjs`, strict inside the key, and the
|
||||
cross-check against `console.sso` (section 1, M1 conditions from mos-1666 item 3). P1 also writes the
|
||||
rollback order into `docs/TOOLS.md` beside Q175's: remove `components` (restore the backup) before the
|
||||
pointer goes back, and the post-rollback STATE check confirms the key is absent from config.json. Gate:
|
||||
test-config.sh green with the four M1 cases.
|
||||
2. **P2, pin and fetch.** `scripts/pocket-id-pins.sh` with the build-time sha256 and the
|
||||
attestation evidence, and `component-pocket-id.sh fetch|verify`. Gate: the fetch tests against the local
|
||||
stand-in; the evidence file shows `gh attestation verify` output for each pinned asset.
|
||||
3. **P3, unit.** The unit template, `render|install|status|remove|purge`, and the encryption key slot. Gate:
|
||||
the render and remove tests, and the sandbox bind test.
|
||||
4. **P4, enroll.** The namespaced bootstrap, the client, the subject, `relink`, and printing the entry (R1),
|
||||
(R2 rejected). Gate: the enroll tests in section 9, and a secret-leak grep over every test log.
|
||||
5. **P5, Console against the fake Pocket ID.** The section 9 fake-OP start tests, and the
|
||||
`NODE_EXTRA_CA_CERTS` render flag for the Console unit. Gate: the webui suite, with no change to
|
||||
`sso.mjs` checks (a diff of `sso.mjs` and `sso-config.mjs` against the base shows none).
|
||||
6. **P6, installer option.** `--oidc existing|bundled` on REQ-INST-3's install command, calling P2 to P4 in
|
||||
order, and the docs: `docs/TOOLS.md`, the host run sheet's J1 to J4 steps. Gate: the installer's own
|
||||
suite against the stubs; the docs list J1 to J4 as the user's steps.
|
||||
|
||||
## Open items for review
|
||||
|
||||
- M1 approved by Mos with three conditions (mos-1666 item 3), written into section 1 and P1.
|
||||
- M2 dropped: Sage chose R1 and rejected R2 (sage-4401).
|
||||
- **Q1 (Webby):** the private network namespace for enroll (section 4). The other option is to start the
|
||||
real unit and race `/setup`, which I think is unsafe while agents share the host network.
|
||||
- **Q2 (Webby):** one-providers-entry for the bundled choice (section 1). REQ-INST-2's wording reads that
|
||||
way. Mixing a bundled Pocket ID with existing providers would need Sage's ruling.
|
||||
- Not covered: Pocket ID for Vikunja or Gitea people sign-in (the Researcher note's part 3), and
|
||||
client-credentials between Mosaic services. Neither is in Q203.
|
||||
@@ -1,102 +0,0 @@
|
||||
# Row 334 — Case 2 proxy unit (#1832)
|
||||
|
||||
## Design at 78b69e1e6 (2026-10-25)
|
||||
|
||||
Scope: mos-1616 ruling 1, P1 item 7 only. No template, runner, PIN,
|
||||
user-unit path, config/secret access, or live unit changes.
|
||||
|
||||
Current references: case2-unit.sh template seams 44-45, defaults 54-57,
|
||||
render 435, check_unit 489, render checks 548-551, re-install 553-573,
|
||||
active-state capture 576-580, writes/start/report 583-610.
|
||||
case2-install.sh Next: 231. P1 instance.sh PROXY_UNIT 86;
|
||||
bus-service.sh dependency insertion 93. P1 template ExecStart 13;
|
||||
run.mjs validates config 11-13 and calls startVerifiedProxy 14;
|
||||
runner.mjs refuses proxy-not-configured 8, verifyInstall 9;
|
||||
settings.mjs verifyInstall 23-30 refuses proxy-install-invalid for a hash
|
||||
mismatch. run.mjs catches that and exits 3. P1 item 7 is rows doc 86-90;
|
||||
row209 entry check 2 remains 32-36.
|
||||
|
||||
Add bare --proxy and CASE2_PROXY_TEMPLATE. Render P1's unmodified template
|
||||
with copy, PATH, node and deleted CONFIG_ENV; add User/Group plus bus's four
|
||||
hardening lines, no capabilities or EnvironmentFile. check_unit proxy uses
|
||||
exact node + copy/packages/model-proxy/src/run.mjs, NoNewPrivileges=yes,
|
||||
and every common refusal. Restrict CLAUDE_ENV expectation and capabilities
|
||||
to their existing bus/webui kinds. Insert bus Wants/After immediately after
|
||||
Description exactly as P1 does, only with --proxy. No-proxy output remains
|
||||
byte identical.
|
||||
|
||||
Re-install checks all selected units against render modulo copy id and
|
||||
compares every installed copy id. Existing proxy without --proxy refuses
|
||||
before writes, including a dangling symlink. No deletion. Enable all
|
||||
selected units, then start proxy, bus, Console. Capture/report already-running
|
||||
proxy with the others and name all three in restart-together guidance.
|
||||
Change only case2-install.sh's closing Next: to tell root to add --proxy
|
||||
when the config has a proxy section.
|
||||
|
||||
Tests first, unrun until Sage CLEAR: byte-identical non-proxy renders;
|
||||
proxy content and exact dependencies; each common/proxy check_unit refusal
|
||||
by message; rendered ExecStart wrong-PIN refusal proxy-install-invalid rc 3;
|
||||
three-unit re-install and mixed copies including proxy-only mismatch;
|
||||
installed proxy without flag preserves files; start order and failures;
|
||||
already-running report; install Next: guidance. Named mutants: remove bus
|
||||
After=; direct binary ExecStart bypassing run.mjs. Whole-file run list will
|
||||
be derived from static imports before any run; case1-accept stays off.
|
||||
|
||||
P2 is not present on this base (row331 still in progress); inspect landed
|
||||
P2 refusal code before final handoff. No flag/config false-positive check:
|
||||
P1 startVerifiedProxy refuses proxy-not-configured, run.mjs exits 3.
|
||||
|
||||
## Slot / session
|
||||
|
||||
No tests run. CLEAR pending from Sage. Canonical queue start initially
|
||||
refused: lock held by magica-331-start since 03:57:01.839Z. Same operation
|
||||
will be retried after a minute; no lock manipulation.
|
||||
|
||||
Correction to line references above: row209 entry check 2 is 29-33;
|
||||
run.mjs config validation 8-10, runner call 11, refusal exit status 18.
|
||||
case2-unit.sh is byte-identical to brief base 58b2bbb3a (sha256
|
||||
4ca8769134d83ee391bcc73af5bd042b9e2b161cac22821d29fcca710a9afad4).
|
||||
P1 paths named above have no diff between 86cb5c221 and 78b69e1e6.
|
||||
Own-worktree root npm ci --ignore-scripts --no-audit --no-fund: rc 0,
|
||||
128 packages. No tests invoked.
|
||||
|
||||
Sage sage-4346 accepts design, authorizes writing RED tests then code while
|
||||
holding all runs until CLEAR. Tests were written first, then implementation;
|
||||
no RED run has occurred. Installed proxy without --proxy explicitly refuses
|
||||
and preserves bytes; dangling symlink also refuses, never deletes. P2 code
|
||||
remains pending until row331 lands. Both wrong-flag P1 runner paths covered.
|
||||
|
||||
Import-manifest correction: expanded traversal to include multiline import
|
||||
and export-from edges (the business/library barrel modules). Closure is
|
||||
53 local modules; only the same two test entrypoints occur. The run list
|
||||
is unchanged; this is source inspection, no tests executed.
|
||||
|
||||
Queue start completed: rockerduck-334-start rev4079, row334
|
||||
briefed→in-progress. git diff --check passed. Implementation, tests and
|
||||
import manifest prepared; no tests, commits or pushes performed. Verification
|
||||
awaits Sage's CLEAR; no live host/instance access occurred.
|
||||
|
||||
## RED run authorized by sage-4387, 2026-10-25 04:21Z
|
||||
|
||||
Whole files, sequential, with each corresponding script temporarily restored
|
||||
from 78b69e1e6 and its implementation restored by EXIT trap afterwards:
|
||||
- case2-unit.test.mjs: rc1, 81 tests, 48 pass, 33 fail, 0 skips;
|
||||
TMPDIR=/mnt/storage/scratch/tmp/QD3vp5, nice=19, 77995.928687 ms.
|
||||
All 47 old tests pass; new no-proxy byte equality passes. 32 new failures
|
||||
encounter baseline unsupported --proxy usage rc4; dangling installed proxy
|
||||
symlink case gets rc0 rather than refusal because baseline ignores it.
|
||||
- case2-install.test.mjs: rc1, 28 tests, 27 pass, 1 fail, 0 skips;
|
||||
TMPDIR=/mnt/storage/scratch/tmp/YAGfKJ, nice=19, 10016.247032 ms.
|
||||
All old tests pass. New Next: guidance assertion fails because baseline
|
||||
lacks add --proxy when the config has a proxy section.
|
||||
|
||||
Every failure and cause is listed in red-results.txt; full plain-text
|
||||
outputs in red-unit.log and red-install.log. Absolute node/test paths used.
|
||||
Implementation restored, git diff --check clean. No old-test red, rerun,
|
||||
assertion change, GREEN or mutant run. P2 check still pending row331.
|
||||
Slot returned to Sage; GREEN and mutants await separate CLEAR.
|
||||
|
||||
Sage sage-4390 accepts clean RED and explicitly authorizes committing and
|
||||
pushing this candidate to rockerduck/row334 before GREEN. No force push.
|
||||
GREEN and mutants remain held for a separate CLEAR after row331 lands;
|
||||
then rebase onto landed P2 and check its missing-flag refusal code.
|
||||
@@ -1,69 +0,0 @@
|
||||
# Row334 whole-file run list (UNRUN; awaiting CLEAR)
|
||||
|
||||
Entrypoints are the brief-owned tests, which spawn the changed shell scripts.
|
||||
Their import graph is recorded below; no imported module is itself a test entrypoint.
|
||||
case1-accept.test.mjs is excluded (mos-1612).
|
||||
|
||||
packages/cli/tests/case2-unit.test.mjs -> packages/business/tests/helpers.mjs, packages/seat/src/sandbox.mjs, packages/cli/tests/helpers.mjs, packages/cli/tests/case-sudo-fixture.mjs
|
||||
packages/business/tests/helpers.mjs -> (builtins/external only)
|
||||
packages/seat/src/sandbox.mjs -> packages/business/src/vocabulary.mjs
|
||||
packages/business/src/vocabulary.mjs -> (builtins/external only)
|
||||
packages/cli/tests/helpers.mjs -> packages/bus/src/broker.mjs, packages/bus/src/store.mjs, packages/business/tests/helpers.mjs, packages/harness/tests/helpers.mjs, packages/plugins/tests/helpers.mjs, packages/cli/src/transport.mjs
|
||||
packages/bus/src/broker.mjs -> packages/bus/src/address.mjs, packages/processing-control/src/state.mjs, packages/plugins/src/kinds.mjs, packages/tasks/src/requirements.mjs, packages/bus/src/landing.mjs, packages/business/src/vocabulary.mjs
|
||||
packages/bus/src/address.mjs -> (builtins/external only)
|
||||
packages/processing-control/src/state.mjs -> (builtins/external only)
|
||||
packages/plugins/src/kinds.mjs -> (builtins/external only)
|
||||
packages/tasks/src/requirements.mjs -> (builtins/external only)
|
||||
packages/bus/src/landing.mjs -> (builtins/external only)
|
||||
packages/bus/src/store.mjs -> (builtins/external only)
|
||||
packages/harness/tests/helpers.mjs -> packages/business/src/index.mjs, packages/business/tests/helpers.mjs, packages/plugins/src/loader.mjs
|
||||
packages/business/src/index.mjs -> packages/business/src/errors.mjs, packages/business/src/vocabulary.mjs, packages/business/src/role.mjs, packages/business/src/vars.mjs, packages/business/src/credentials.mjs, packages/business/src/business.mjs, packages/business/src/access.mjs, packages/business/src/project.mjs, packages/business/src/resolve.mjs, packages/business/src/library-seats.mjs
|
||||
packages/business/src/errors.mjs -> (builtins/external only)
|
||||
packages/business/src/role.mjs -> packages/business/src/errors.mjs, packages/business/src/vocabulary.mjs, packages/business/src/util.mjs
|
||||
packages/business/src/util.mjs -> packages/business/src/errors.mjs, packages/business/src/vocabulary.mjs
|
||||
packages/business/src/vars.mjs -> packages/business/src/errors.mjs, packages/business/src/vocabulary.mjs, packages/business/src/util.mjs, packages/plugins/src/kinds.mjs
|
||||
packages/business/src/credentials.mjs -> packages/business/src/errors.mjs, packages/business/src/vocabulary.mjs, packages/business/src/util.mjs
|
||||
packages/business/src/business.mjs -> packages/business/src/errors.mjs, packages/business/src/launch.mjs, packages/business/src/vars.mjs, packages/business/src/credentials.mjs, packages/business/src/role.mjs, packages/business/src/vocabulary.mjs, packages/plugins/src/kinds.mjs, packages/library/src/mapping.mjs, packages/business/src/util.mjs
|
||||
packages/business/src/launch.mjs -> packages/business/src/util.mjs, packages/business/src/errors.mjs
|
||||
packages/library/src/mapping.mjs -> packages/library/src/problems.mjs
|
||||
packages/library/src/problems.mjs -> (builtins/external only)
|
||||
packages/business/src/access.mjs -> packages/business/src/errors.mjs, packages/business/src/business.mjs, packages/business/src/util.mjs, packages/business/src/vocabulary.mjs
|
||||
packages/business/src/project.mjs -> packages/business/src/errors.mjs, packages/business/src/vars.mjs, packages/business/src/util.mjs
|
||||
packages/business/src/resolve.mjs -> packages/business/src/errors.mjs, packages/business/src/vocabulary.mjs, packages/business/src/vars.mjs, packages/business/src/util.mjs, packages/business/src/project.mjs
|
||||
packages/business/src/library-seats.mjs -> packages/business/src/errors.mjs, packages/business/src/business.mjs, packages/business/src/vocabulary.mjs
|
||||
packages/plugins/src/loader.mjs -> packages/plugins/src/kinds.mjs
|
||||
packages/plugins/tests/helpers.mjs -> (builtins/external only)
|
||||
packages/cli/src/transport.mjs -> packages/cli/src/errors.mjs
|
||||
packages/cli/src/errors.mjs -> (builtins/external only)
|
||||
packages/cli/tests/case-sudo-fixture.mjs -> (builtins/external only)
|
||||
packages/cli/tests/case2-install.test.mjs -> packages/business/tests/helpers.mjs, packages/library/tests/helpers.mjs, packages/library/src/index.mjs, packages/seat/src/checkout.mjs, packages/project/src/git.mjs, packages/cli/tests/helpers.mjs
|
||||
packages/library/tests/helpers.mjs -> packages/library/src/mapping.mjs
|
||||
packages/library/src/index.mjs -> packages/library/src/problems.mjs, packages/library/src/mapping.mjs, packages/library/src/scope.mjs, packages/library/src/supporting.mjs, packages/library/src/links.mjs, packages/library/src/reader.mjs, packages/library/src/render.mjs, packages/library/src/survey.mjs, packages/library/src/mirror.mjs, packages/library/src/sync.mjs, packages/library/src/index-build.mjs, packages/library/src/query.mjs, packages/library/src/limits.mjs, packages/library/src/guard.mjs
|
||||
packages/library/src/scope.mjs -> (builtins/external only)
|
||||
packages/library/src/supporting.mjs -> packages/library/src/problems.mjs, packages/library/src/scope.mjs
|
||||
packages/library/src/links.mjs -> (builtins/external only)
|
||||
packages/library/src/reader.mjs -> packages/project/src/git.mjs, packages/library/src/scope.mjs, packages/library/src/links.mjs, packages/library/src/problems.mjs
|
||||
packages/project/src/git.mjs -> packages/seat/src/checkout.mjs, packages/project/src/paths.mjs, packages/processing-control/src/effects.mjs
|
||||
packages/seat/src/checkout.mjs -> packages/processing-control/src/effects.mjs
|
||||
packages/processing-control/src/effects.mjs -> packages/processing-control/src/state.mjs, packages/processing-control/src/runtimes.mjs
|
||||
packages/processing-control/src/runtimes.mjs -> packages/processing-control/src/state.mjs
|
||||
packages/project/src/paths.mjs -> (builtins/external only)
|
||||
packages/library/src/render.mjs -> (builtins/external only)
|
||||
packages/library/src/survey.mjs -> packages/library/src/mapping.mjs, packages/library/src/scope.mjs, packages/library/src/links.mjs, packages/library/src/reader.mjs, packages/library/src/problems.mjs
|
||||
packages/library/src/mirror.mjs -> packages/project/src/paths.mjs, packages/project/src/git.mjs, packages/library/src/mapping.mjs, packages/library/src/scope.mjs, packages/library/src/problems.mjs, packages/library/src/reader.mjs
|
||||
packages/library/src/sync.mjs -> packages/seat/src/checkout.mjs, packages/project/src/git.mjs, packages/library/src/mapping.mjs, packages/library/src/problems.mjs, packages/library/src/reader.mjs, packages/library/src/mirror.mjs
|
||||
packages/library/src/index-build.mjs -> packages/library/src/reader.mjs, packages/library/src/problems.mjs
|
||||
packages/library/src/query.mjs -> packages/library/src/problems.mjs, packages/library/src/mapping.mjs, packages/library/src/index-build.mjs, packages/library/src/reader.mjs
|
||||
packages/library/src/limits.mjs -> (builtins/external only)
|
||||
packages/library/src/guard.mjs -> packages/project/src/git.mjs, packages/library/src/scope.mjs, packages/library/src/reader.mjs, packages/library/src/problems.mjs, packages/library/src/mirror.mjs, packages/library/src/limits.mjs, packages/library/src/reply-protocol.mjs
|
||||
packages/library/src/reply-protocol.mjs -> (builtins/external only)
|
||||
|
||||
Whole-file commands after CLEAR (one fresh shell-mktemp TMPDIR each; nice=19):
|
||||
node --test packages/cli/tests/case2-unit.test.mjs
|
||||
node --test packages/cli/tests/case2-install.test.mjs
|
||||
|
||||
Before these: new row334 tests only on saved 78b69e1e6 scripts for RED,
|
||||
then restored implementation. After GREEN: named mutants individually,
|
||||
each with its directly relevant row334 test. No scripts/test-*.sh builder run planned.
|
||||
|
||||
Includes multiline imports and export-from module edges: 53 local modules.
|
||||
@@ -1,68 +0,0 @@
|
||||
TMPDIR=/mnt/storage/scratch/tmp/YAGfKJ nice=19
|
||||
✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (386.113555ms)
|
||||
✔ a library re-run passes and leaves every file as it was (496.225906ms)
|
||||
✔ a re-run refuses business-file-differs and leaves the file unchanged (376.395513ms)
|
||||
✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (398.216221ms)
|
||||
✔ the factory install writes no library mapping, persona or slot dir (247.905208ms)
|
||||
✔ the factory install refuses a library project and bus.tester off, before anything is written (105.176208ms)
|
||||
✔ the library install refuses bus.tester on and a persona var in the input (104.503599ms)
|
||||
✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (80.144703ms)
|
||||
✔ the library install needs --personas (7.831929ms)
|
||||
✔ a validate refusal removes the business file this run wrote (311.701322ms)
|
||||
✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (537.848604ms)
|
||||
✔ a clean library host passes the precheck with no mirror yet (509.009214ms)
|
||||
✔ the library precheck refuses a persona file at 0644 (506.697677ms)
|
||||
✔ the library precheck refuses a persona whose digest isn't persona.sha256 (495.113108ms)
|
||||
✔ the library precheck refuses a persona dir with group bits (481.900638ms)
|
||||
✔ the library precheck refuses a business file at 0644 (462.626038ms)
|
||||
✔ the library precheck refuses a changes.jsonl at 0644 (687.490332ms)
|
||||
✔ the library precheck refuses a Discord binding (499.568905ms)
|
||||
✔ --library-status refuses library-mirror-missing before the bus has made the mirror (377.717622ms)
|
||||
✔ --library-status refuses library-never-synced on a fresh mirror (410.956236ms)
|
||||
✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (417.888767ms)
|
||||
✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (481.141014ms)
|
||||
✔ --library-status takes no --profile (6.246241ms)
|
||||
✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (560.431267ms)
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (183.399079ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (118.731885ms)
|
||||
✔ C2-21: library install supplies chief identity and books publish for its real read grant (330.48101ms)
|
||||
✖ row334 install Next tells root to select proxy only when configured (302.965779ms)
|
||||
ℹ tests 28
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10016.247032
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/cli/tests/case2-install.test.mjs:495:1
|
||||
✖ row334 install Next tells root to select proxy only when configured (302.965779ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /Next: root runs case2-unit.sh[^\n]+add --proxy when the config has a proxy section/. Input:
|
||||
|
||||
'case2-install: config /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/config.json\n' +
|
||||
'case2-install: copy /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc\n' +
|
||||
'case2-install: personas installed under /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/personas/acme\n' +
|
||||
'case2-install: business file /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/businesses/acme.json written\n' +
|
||||
'case2-install: slot dir /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/secrets/acme\n' +
|
||||
'case2-install: business acme validates\n' +
|
||||
'case2-install: done. Next: root runs case2-unit.sh --copy cccccccccccccccccccccccccccccccccccccccc --business acme (design 10 step 2c), then 2d and 3.\n' +
|
||||
'case2-install: Then Jason places the library token in /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/secrets/acme (step 4), and <user> runs case2-precheck.sh --business acme --library-status (step 4a).\n'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-install.test.mjs:498:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'case2-install: config /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/config.json\ncase2-install: copy /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc\ncase2-install: personas installed under /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/personas/acme\ncase2-install: business file /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/businesses/acme.json written\ncase2-install: slot dir /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/secrets/acme\ncase2-install: business acme validates\ncase2-install: done. Next: root runs case2-unit.sh --copy cccccccccccccccccccccccccccccccccccccccc --business acme (design 10 step 2c), then 2d and 3.\ncase2-install: Then Jason places the library token in /mnt/storage/scratch/tmp/YAGfKJ/case2-install-lXA9Qd/home/.config/mosaic-dev/secrets/acme (step 4), and <user> runs case2-precheck.sh --business acme --library-status (step 4a).\n',
|
||||
expected: /Next: root runs case2-unit.sh[^\n]+add --proxy when the config has a proxy section/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
# RED results — sage-4387 (2026-10-25)
|
||||
|
||||
Baseline scripts: 78b69e1e6. Current row334 tests. Whole files, sequential.
|
||||
Both implementation scripts restored via EXIT traps after their run.
|
||||
No GREEN or mutant run. No old-test failure. No assertion changed.
|
||||
|
||||
case2-unit.test.mjs: exit 1; 81 tests, 48 pass, 33 fail, 0 skip.
|
||||
47 existing tests plus new byte-identical no-proxy test pass.
|
||||
TMPDIR=/mnt/storage/scratch/tmp/QD3vp5; nice=19; duration 77995.928687 ms.
|
||||
|
||||
Every unit failure and its observed reason:
|
||||
- row334 proxy uses P1 runner, bus dependencies and proxy bus Console start order: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses continuation by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses not-setting by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses RestrictNamespaces by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses PrivateUsers by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses SystemCallFilter by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses PermissionsStartOnly by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses EnvironmentFile by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses AmbientCapabilities by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses CapabilityBoundingSet by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses privileged by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses direct-binary by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses nnp by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses cwd by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses environment by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses install by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses user by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses group by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses ProtectSystem by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses ReadWritePaths by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses ProtectHome by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses PrivateTmp by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy refuses token by message before writing: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy template missing or symlink refuses: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 rendered ExecStart refuses wrong PIN and missing proxy with P1 status: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 reinstall repins all three and refuses a proxy copy mismatch: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 installed proxy without --proxy refuses and preserves all bytes: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 dangling proxy symlink without flag refuses without deleting: baseline ignores installed proxy symlink without flag, returns 0 instead of refusal 1.
|
||||
- row334 active proxy reports old copy and all three must restart together: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 failed mosaic-proxy.service start leaves later units unstarted: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 failed [email protected] start leaves later units unstarted: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 installed proxy enforces hardening order and remaining common refusals: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
- row334 proxy installed path rejects symlink and non-regular file: baseline does not parse --proxy: usage exit 4, before the proxy-specific assertion/setup can be reached.
|
||||
|
||||
case2-install.test.mjs: exit 1; 28 tests, 27 pass, 1 fail, 0 skip.
|
||||
TMPDIR=/mnt/storage/scratch/tmp/YAGfKJ; nice=19; duration 10016.247032 ms.
|
||||
- row334 install Next tells root to select proxy only when configured: baseline Next: lacks add --proxy when the config has a proxy section.
|
||||
|
||||
Commands used absolute /usr/bin/node --test and absolute test-file paths.
|
||||
Full plain-text outputs: red-unit.log and red-install.log beside this file.
|
||||
P2 refusal-code check remains pending row331. Slot returned to Sage.
|
||||
@@ -1,873 +0,0 @@
|
||||
TMPDIR=/mnt/storage/scratch/tmp/QD3vp5 nice=19
|
||||
✔ row253 factory socket uses actual config and target-uid query without widening hardening (351.144059ms)
|
||||
✔ row253 installer refuses unsafe location and query facts before unit writes (3621.94316ms)
|
||||
✔ row253 query timeout kills its process group and leaves no running child (7054.318085ms)
|
||||
✔ row253 protected homes refuse before any filesystem access (9.291102ms)
|
||||
✔ row253 C4 sleep query has exact inner reason and cleans all members (10796.380077ms)
|
||||
✔ row253 C4 signals query has exact inner reason and cleans all members (10819.227773ms)
|
||||
✔ row253 C4 member query has exact inner reason and cleans all members (10897.328548ms)
|
||||
✔ row253 C4 overflow is output-limit in both sudo modes (554.12811ms)
|
||||
✔ row253 C4 stalled supervisor uses the outer reason and explicit residual cleanup (14201.797743ms)
|
||||
✔ row253 C4 inline supervisor is byte-identical in both host scripts (0.888234ms)
|
||||
✔ row253 C4 cleanup budget starts after inventory pass one (46.615614ms)
|
||||
✔ row253 query reaps its group even when a child closes stdout before the query returns (152.118314ms)
|
||||
✔ row253 config parser is strict (11.560624ms)
|
||||
✔ a clean library render writes both units 0644 with the fixed lines, and enables both then starts the bus then the Console (120.676804ms)
|
||||
✔ PATH starts with the --node directory, for a node outside the default list and for /usr/bin/node (131.630623ms)
|
||||
✔ a failed bus start refuses with the Console not started (123.177864ms)
|
||||
✔ the script never links a unit file (0.18683ms)
|
||||
✔ factory renders --pm once and one MOSAIC_CLAUDE_BIN line, and without them both tokens render empty (746.075081ms)
|
||||
✔ a re-install over running units says they are still on the old copy and must restart together, never that it started them (494.63679ms)
|
||||
✔ a re-install to another copy re-renders both units, and an edited installed unit refuses (1175.719345ms)
|
||||
✔ a template with a namespace, user or system call restriction refuses (335.625113ms)
|
||||
✔ the bus template's EnvironmentFile line must be there and point at the session env, and the Console template must have none (214.600182ms)
|
||||
✔ a template holding an unknown token, PermissionsStartOnly or a privileged Exec prefix refuses (151.081813ms)
|
||||
✔ a symlink at either unit path refuses (203.555513ms)
|
||||
✔ a --node that is relative, not root-owned or under <home> refuses (27.297759ms)
|
||||
✔ a re-install with a different --node or different serve flags refuses (599.873393ms)
|
||||
✔ an installed pair whose units name different copies refuses (328.321083ms)
|
||||
✔ a copy id that is short, uppercase, holds / or .., or names no directory refuses (46.028703ms)
|
||||
✔ library refuses a privileged-floor miss, a bad proof pair and every flag outside its set (344.833107ms)
|
||||
✔ library refuses a proof file not owned by <user>'s uid (18.013172ms)
|
||||
✔ factory refuses duplicate socket, a missing --port or --business, and each proxy flag (209.757808ms)
|
||||
✔ row253 factory refuses retired TCP and a value after the socket marker (59.218628ms)
|
||||
✔ factory refuses a --claude that is relative, not root-owned or under <home>, and a re-install with another --pm or --claude (1686.680735ms)
|
||||
✔ the precheck passes on a good host and prints free memory, without the key (226.963557ms)
|
||||
✔ the precheck refuses each failed host check (1765.731095ms)
|
||||
✔ the precheck refuses a user missing from passwd, a business file that does not load, and a symlinked run root (927.260864ms)
|
||||
✔ a bwrap that fails refuses and prints the sysctl and where bwrap is (200.731617ms)
|
||||
✔ an unprivileged port floor at or below the Console's port refuses, and so does a missing Console unit (1055.360802ms)
|
||||
✔ bus.tester: library refuses it on, factory needs it on, and factory refuses a library business (927.574539ms)
|
||||
✔ the claude check runs only for a claude-code instance, and refuses a claude that appends or answers wrong (1694.074713ms)
|
||||
✔ the claude check runs the bus unit's MOSAIC_CLAUDE_BIN over claude on PATH (498.707509ms)
|
||||
✔ the session env file refuses when missing, open to others, a symlink, empty, or emptied by a later line (1155.959127ms)
|
||||
✔ inside a session's sandbox a bind below 1024 fails with EACCES, and a bind on a free high port succeeds (106.466066ms)
|
||||
✔ Vikunja 614 factory: scoped @anthropic-ai Claude path renders and passes installed read-back (1024.721235ms)
|
||||
✔ Vikunja 614 factory: plain paths still refuse every named unsafe character (117.946806ms)
|
||||
✔ Vikunja 614 precheck: explicit null and named notifier decisions pass (384.090317ms)
|
||||
✔ Vikunja 614 precheck: notifier config refuses missing, public, malformed and extra-key decisions (1596.303308ms)
|
||||
✔ row334 no-proxy renders stay byte-identical to the pre-row renderer (316.417263ms)
|
||||
✖ row334 proxy uses P1 runner, bus dependencies and proxy bus Console start order (6.436602ms)
|
||||
✖ row334 proxy refuses continuation by message before writing (6.291734ms)
|
||||
✖ row334 proxy refuses not-setting by message before writing (5.588449ms)
|
||||
✖ row334 proxy refuses RestrictNamespaces by message before writing (5.71389ms)
|
||||
✖ row334 proxy refuses PrivateUsers by message before writing (5.34285ms)
|
||||
✖ row334 proxy refuses SystemCallFilter by message before writing (5.400994ms)
|
||||
✖ row334 proxy refuses PermissionsStartOnly by message before writing (5.443023ms)
|
||||
✖ row334 proxy refuses EnvironmentFile by message before writing (5.052075ms)
|
||||
✖ row334 proxy refuses AmbientCapabilities by message before writing (5.027453ms)
|
||||
✖ row334 proxy refuses CapabilityBoundingSet by message before writing (5.604853ms)
|
||||
✖ row334 proxy refuses privileged by message before writing (5.176325ms)
|
||||
✖ row334 proxy refuses direct-binary by message before writing (5.301058ms)
|
||||
✖ row334 proxy refuses nnp by message before writing (4.978268ms)
|
||||
✖ row334 proxy refuses cwd by message before writing (4.903056ms)
|
||||
✖ row334 proxy refuses environment by message before writing (4.928207ms)
|
||||
✖ row334 proxy refuses install by message before writing (4.935225ms)
|
||||
✖ row334 proxy refuses user by message before writing (4.996881ms)
|
||||
✖ row334 proxy refuses group by message before writing (4.740792ms)
|
||||
✖ row334 proxy refuses ProtectSystem by message before writing (7.412574ms)
|
||||
✖ row334 proxy refuses ReadWritePaths by message before writing (5.039647ms)
|
||||
✖ row334 proxy refuses ProtectHome by message before writing (4.948113ms)
|
||||
✖ row334 proxy refuses PrivateTmp by message before writing (5.049564ms)
|
||||
✖ row334 proxy refuses token by message before writing (5.291145ms)
|
||||
✖ row334 proxy template missing or symlink refuses (4.77612ms)
|
||||
✖ row334 rendered ExecStart refuses wrong PIN and missing proxy with P1 status (5.907949ms)
|
||||
✖ row334 reinstall repins all three and refuses a proxy copy mismatch (5.706372ms)
|
||||
✖ row334 installed proxy without --proxy refuses and preserves all bytes (4.919106ms)
|
||||
✖ row334 dangling proxy symlink without flag refuses without deleting (127.81539ms)
|
||||
✖ row334 active proxy reports old copy and all three must restart together (5.181396ms)
|
||||
✖ row334 failed mosaic-proxy.service start leaves later units unstarted (4.827066ms)
|
||||
✖ row334 failed [email protected] start leaves later units unstarted (4.558348ms)
|
||||
✖ row334 installed proxy enforces hardening order and remaining common refusals (5.170683ms)
|
||||
✖ row334 proxy installed path rejects symlink and non-regular file (4.685001ms)
|
||||
ℹ tests 81
|
||||
ℹ suites 0
|
||||
ℹ pass 48
|
||||
ℹ fail 33
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 77995.928687
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:966:1
|
||||
✖ row334 proxy uses P1 runner, bus dependencies and proxy bus Console start order (6.436602ms)
|
||||
AssertionError [ERR_ASSERTION]: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:969:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses continuation by message before writing (6.291734ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses not-setting by message before writing (5.588449ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses RestrictNamespaces by message before writing (5.71389ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses PrivateUsers by message before writing (5.34285ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses SystemCallFilter by message before writing (5.400994ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses PermissionsStartOnly by message before writing (5.443023ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses EnvironmentFile by message before writing (5.052075ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses AmbientCapabilities by message before writing (5.027453ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses CapabilityBoundingSet by message before writing (5.604853ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses privileged by message before writing (5.176325ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses direct-binary by message before writing (5.301058ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses nnp by message before writing (4.978268ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses cwd by message before writing (4.903056ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses environment by message before writing (4.928207ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses install by message before writing (4.935225ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses user by message before writing (4.996881ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses group by message before writing (4.740792ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses ProtectSystem by message before writing (7.412574ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses ReadWritePaths by message before writing (5.039647ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses ProtectHome by message before writing (4.948113ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses PrivateTmp by message before writing (5.049564ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1004:52
|
||||
✖ row334 proxy refuses token by message before writing (5.291145ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1007:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1012:1
|
||||
✖ row334 proxy template missing or symlink refuses (4.77612ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1014:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1019:1
|
||||
✖ row334 rendered ExecStart refuses wrong PIN and missing proxy with P1 status (5.907949ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1021:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1044:1
|
||||
✖ row334 reinstall repins all three and refuses a proxy copy mismatch (5.706372ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1046:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1055:1
|
||||
✖ row334 installed proxy without --proxy refuses and preserves all bytes (4.919106ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1057:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1065:1
|
||||
✖ row334 dangling proxy symlink without flag refuses without deleting (127.81539ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 0: case2-unit: wrote /mnt/storage/scratch/tmp/QD3vp5/case2-unit-h4V4Zn/units/[email protected] and /mnt/storage/scratch/tmp/QD3vp5/case2-unit-h4V4Zn/units/mosaic-webui.service from copy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; enabled and started [email protected], then mosaic-webui.service
|
||||
|
||||
|
||||
0 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1068:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1072:1
|
||||
✖ row334 active proxy reports old copy and all three must restart together (5.181396ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1074:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1080:73
|
||||
✖ row334 failed mosaic-proxy.service start leaves later units unstarted (4.827066ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1082:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1080:73
|
||||
✖ row334 failed [email protected] start leaves later units unstarted (4.558348ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1082:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1088:1
|
||||
✖ row334 installed proxy enforces hardening order and remaining common refusals (5.170683ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1090:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-unit.test.mjs:1103:1
|
||||
✖ row334 proxy installed path rejects symlink and non-regular file (4.685001ms)
|
||||
AssertionError [ERR_ASSERTION]: expected a refusal, got 4: usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS
|
||||
|
||||
|
||||
4 !== 1
|
||||
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:292:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/rockerduck-row334/packages/cli/tests/case2-unit.test.mjs:1106:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1694,6 +1694,3 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-24 | magica | row329 R2 #1827 | ASCII management-key fix and docs verified: package66/66,bus readers261/261,CLI readers726/726,config55/55; no required failures/skips, slot returned. Preparing authorized ff review candidate; old tests unchanged.
|
||||
2026-10-24 | Sage 1ef1e4f8 | row329 landing #1827 (mos-1634) | Gate GREEN 23:29:14Z unchanged, landed 61ebf2c34 pushed 23:31:27Z; #1827 closed c31650; queue note 4063, done 4064 (b24d917ec); DEFERRED N2/R2-N1 to 331, N1 to P7; Vikunja 631 filed, 237 c1109; 284 and 157 must rebase.
|
||||
2026-10-25 | Sage 1ef1e4f8 | row284 landing #1789 | Gate GREEN 00:09:04Z unchanged, landed ce0b6ed41 pushed 00:09:52Z; #1789 closed c31655; queue note 4071, done 4072 (7c8dc2033); DEFERRED Webby N1 to the next library read-path row; row 332 PROXY_CODEX_API ruled openai-responses (sage-4331), P3 holds for row 331.
|
||||
2026-10-25 — rockerduck — row334 (#1832), Case 2 proxy system unit — design/read phase; tests held for Sage CLEAR; queue start lock diagnosed.
|
||||
2026-10-25 — rockerduck — row334 (#1832), Case 2 proxy system unit — queue start rev4079; tests then implementation written, design accepted by Sage; verification held for CLEAR, P2 refusal-code check pending row331.
|
||||
2026-10-25 — rockerduck — row334 RED-only slot sage-4387 — both whole files against pre-change scripts: 74 older tests pass, 34 new expected failures; implementations restored, slot returned; GREEN/mutants held.
|
||||
@@ -490,10 +490,3 @@ test('C2-21: library install supplies chief identity and books publish for its r
|
||||
assert.ok(doc.roles.chief.identity?.gitea?.user);
|
||||
assert.notEqual(doc.roles.chief.identity?.gitea?.user, 'writer');
|
||||
});
|
||||
|
||||
|
||||
test('row334 install Next tells root to select proxy only when configured', t => {
|
||||
const x = world(t), r = install(x);
|
||||
assert.equal(r.code, 0, r.out);
|
||||
assert.match(r.out, /Next: root runs case2-unit.sh[^\n]+add --proxy when the config has a proxy section/);
|
||||
});
|
||||
@@ -7,10 +7,10 @@ import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createServer } from "node:net";
|
||||
import { chmodSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { businessDoc, systemConfig, writeJson } from "../../business/tests/helpers.mjs";
|
||||
import { businessDoc, writeJson } from "../../business/tests/helpers.mjs";
|
||||
import { binds, prepareRun, sandboxArgv, sandboxProblem } from "../../seat/src/sandbox.mjs";
|
||||
import { tmp } from "./helpers.mjs";
|
||||
import { CASE_SUDO_SESSION, recordedLivePids, killRecordedPids, fixtureGuard, cleanupFixture, pollFixtureGone, blockingQuerySource } from "./case-sudo-fixture.mjs";
|
||||
@@ -940,171 +940,3 @@ test("Vikunja 614 precheck: notifier config refuses missing, public, malformed a
|
||||
assert.match(r.out, /REFUSED notifier config:/, `${fault}: ${r.out}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Row334: render only into the scratch hub; fake systemctl never touches the host.
|
||||
const PROXY_TEMPLATE = join(REPO, 'packages/cli/systemd/mosaic-proxy.service.in');
|
||||
const proxyFile = h => join(h.units, 'mosaic-proxy.service');
|
||||
const proxyUnit = (h, options = {}) => unit(h, { ...options, extra: ['--proxy', ...(options.extra ?? [])] });
|
||||
|
||||
test('row334 no-proxy renders stay byte-identical to the pre-row renderer', t => {
|
||||
const h = hub(t);
|
||||
// Immutable committed baseline, executed only with the same scratch seams.
|
||||
const baseline = spawnSync('git', ['show', '78b69e1e6:scripts/case2-unit.sh'], { cwd: REPO, encoding: 'utf8' });
|
||||
assert.equal(baseline.status, 0, baseline.stderr);
|
||||
const old = script(join(h.root, 'baseline/scripts/case2-unit.sh'), baseline.stdout);
|
||||
const args = ['--user', 'mosaic', '--home', h.home, '--business', 'acme', '--copy', COPY_A, '--node', USR_NODE, '--', ...libraryFlags(h)];
|
||||
const r = spawnSync(old, args, { encoding: 'utf8', env: { PATH: `${h.bin}:${process.env.PATH}`, CASE2_UNIT_DIR: h.units,
|
||||
CASE2_SYSTEMCTL: join(h.bin, 'systemctl'), CASE2_GETENT: join(h.bin, 'getent'), CASE2_BUS_TEMPLATE: BUS_TEMPLATE, CASE2_WEBUI_TEMPLATE: WEBUI_TEMPLATE } });
|
||||
assert.equal(r.status, 0, r.stderr);
|
||||
const before = [read(busFile(h)), read(webuiFile(h))];
|
||||
assert.equal(unit(h).code, 0);
|
||||
assert.deepEqual([read(busFile(h)), read(webuiFile(h))], before);
|
||||
assert.equal(existsSync(proxyFile(h)), false);
|
||||
});
|
||||
|
||||
test('row334 proxy uses P1 runner, bus dependencies and proxy bus Console start order', t => {
|
||||
const h = hub(t);
|
||||
const r = proxyUnit(h, { profile: 'factory', flags: ['--port', '443', '--business', 'acme'], extra: ['--claude', TRUE_BIN] });
|
||||
assert.equal(r.code, 0, r.err);
|
||||
const proxy = read(proxyFile(h));
|
||||
const expected = read(PROXY_TEMPLATE).replaceAll('@REPO@', copyDir(h)).replaceAll('@PATH@', '/usr/bin:/usr/local/bin:/bin')
|
||||
.replaceAll('@NODE@', USR_NODE).replace(/^@CONFIG_ENV@\n/m, '').replace('WantedBy=default.target', 'WantedBy=multi-user.target')
|
||||
.replace('TimeoutStopSec=60', `TimeoutStopSec=60\nUser=mosaic\nGroup=mosaic\n${HARDENING(h).join('\n')}`);
|
||||
assert.equal(proxy, expected);
|
||||
assert.equal(statSync(proxyFile(h)).mode & 0o777, 0o644);
|
||||
assert.match(read(busFile(h)), /Description=[^\n]+\nWants=mosaic-proxy.service\nAfter=mosaic-proxy.service\n/);
|
||||
assert.deepEqual(lines(proxy, 'ExecStart'), [`${USR_NODE} ${copyDir(h)}/packages/model-proxy/src/run.mjs`]);
|
||||
assert.doesNotMatch(proxy, /EnvironmentFile=|Capabilities=|CapabilityBoundingSet=|MOSAIC_CLAUDE_BIN/);
|
||||
assert.doesNotMatch(read(webuiFile(h)), /Wants=|After=/);
|
||||
assert.deepEqual(read(h.log).trim().split('\n'), [
|
||||
'is-active --quiet mosaic-proxy.service', 'is-active --quiet [email protected]', 'is-active --quiet mosaic-webui.service',
|
||||
'daemon-reload', 'enable mosaic-proxy.service [email protected] mosaic-webui.service',
|
||||
'start mosaic-proxy.service', 'start [email protected]', 'start mosaic-webui.service',
|
||||
]);
|
||||
});
|
||||
|
||||
const proxyRefusals = [
|
||||
['continuation', s => s.replace('Type=simple', 'Type=simple' + String.fromCharCode(92)), /a line continues onto the next/],
|
||||
['not-setting', s => s.replace('Type=simple', 'not a setting'), /neither a section/],
|
||||
...['RestrictNamespaces', 'PrivateUsers', 'SystemCallFilter', 'PermissionsStartOnly', 'EnvironmentFile', 'AmbientCapabilities', 'CapabilityBoundingSet'].map(k =>
|
||||
[k, s => s.replace('Type=simple', `Type=simple\n${k}=yes`), new RegExp(`refusing ${k}=`)]),
|
||||
['privileged', s => s.replace('ExecStart=', 'ExecStart=+'), /Exec line that runs with full privileges/],
|
||||
['direct-binary', s => s.replace('packages/model-proxy/src/run.mjs', 'opt/cli-proxy-api'), /ExecStart= must be exactly once/],
|
||||
['nnp', s => s.replace('NoNewPrivileges=yes', 'NoNewPrivileges=no'), /NoNewPrivileges= must be exactly once/],
|
||||
['cwd', s => s.replace('WorkingDirectory=@REPO@', 'WorkingDirectory=/elsewhere'), /WorkingDirectory= must be exactly once/],
|
||||
['environment', s => s.replace('Environment=PATH=@PATH@', 'Environment=PATH=/elsewhere'), /Environment= lines must be exactly/],
|
||||
['install', s => s.replace('WantedBy=default.target', 'WantedBy=other.target'), /WantedBy= must be exactly once/],
|
||||
['user', s => s.replace('Type=simple', 'Type=simple\nUser=other'), /User= must be exactly once/],
|
||||
['group', s => s.replace('Type=simple', 'Type=simple\nGroup=other'), /Group= must be exactly once/],
|
||||
...['ProtectSystem', 'ReadWritePaths', 'ProtectHome', 'PrivateTmp'].map(k =>
|
||||
[k, s => s.replace('Type=simple', `Type=simple\n${k}=other`), new RegExp(`${k}= must be exactly once`)]),
|
||||
['token', s => s.replace('Type=simple', 'Type=simple\nX-Note=@UNKNOWN@'), /a token is left after the render/],
|
||||
];
|
||||
for (const [name, edit, message] of proxyRefusals) test(`row334 proxy refuses ${name} by message before writing`, t => {
|
||||
const h = hub(t);
|
||||
const file = template(h, PROXY_TEMPLATE, `${name}.in`, edit);
|
||||
refuses(proxyUnit(h, { env: { CASE2_PROXY_TEMPLATE: file } }), message);
|
||||
assert.deepEqual(readdirSync(h.units), []);
|
||||
assert.equal(existsSync(h.log), false);
|
||||
});
|
||||
|
||||
test('row334 proxy template missing or symlink refuses', t => {
|
||||
const h = hub(t), missing = join(h.root, 'missing.in');
|
||||
refuses(proxyUnit(h, { env: { CASE2_PROXY_TEMPLATE: missing } }), /template missing or not a regular file/);
|
||||
symlinkSync(PROXY_TEMPLATE, missing);
|
||||
refuses(proxyUnit(h, { env: { CASE2_PROXY_TEMPLATE: missing } }), /template missing or not a regular file/);
|
||||
});
|
||||
|
||||
test('row334 rendered ExecStart refuses wrong PIN and missing proxy with P1 status', t => {
|
||||
const h = hub(t);
|
||||
assert.equal(proxyUnit(h).code, 0);
|
||||
const copy = copyDir(h);
|
||||
cpSync(join(REPO, 'packages/model-proxy'), join(copy, 'packages/model-proxy'), { recursive: true });
|
||||
cpSync(join(REPO, 'scripts/mosaic-config.mjs'), join(copy, 'scripts/mosaic-config.mjs'));
|
||||
const config = systemConfig(join(h.root, 'runner'));
|
||||
const doc = JSON.parse(read(config));
|
||||
mkdirSync(doc.dataRoot, { recursive: true, mode: 0o700 });
|
||||
const pin = JSON.parse(read(join(REPO, 'packages/model-proxy/PIN')));
|
||||
const binary = join(doc.dataRoot, 'opt/cliproxyapi', pin.version, 'cli-proxy-api');
|
||||
script(binary, `echo unexpected-spawn > '${h.root}/spawned'`);
|
||||
writeJson(join(dirname(config), 'proxy.json'), { port: 18541, binary });
|
||||
const argv = lines(read(proxyFile(h)), 'ExecStart')[0].split(' ');
|
||||
const run = () => spawnSync(argv[0], argv.slice(1), { encoding: 'utf8', env: { PATH: '/usr/bin:/bin', HOME: h.home, MOSAIC_CONFIG: config } });
|
||||
const bad = run();
|
||||
assert.equal(bad.status, 3, bad.stderr);
|
||||
assert.equal(bad.stderr, 'proxy-install-invalid\n');
|
||||
assert.equal(existsSync(join(h.root, 'spawned')), false);
|
||||
rmSync(join(dirname(config), 'proxy.json'));
|
||||
const absent = run();
|
||||
assert.equal(absent.status, 3, absent.stderr);
|
||||
assert.equal(absent.stderr, 'proxy-not-configured\n');
|
||||
});
|
||||
|
||||
test('row334 reinstall repins all three and refuses a proxy copy mismatch', t => {
|
||||
const h = hub(t);
|
||||
assert.equal(proxyUnit(h).code, 0);
|
||||
assert.equal(proxyUnit(h, { copy: COPY_B }).code, 0);
|
||||
for (const file of [proxyFile(h), busFile(h), webuiFile(h)]) assert.match(read(file), new RegExp(copyDir(h, COPY_B)));
|
||||
writeFileSync(proxyFile(h), read(proxyFile(h)).replaceAll(copyDir(h, COPY_B), copyDir(h)));
|
||||
const before = [proxyFile(h), busFile(h), webuiFile(h)].map(read);
|
||||
refuses(proxyUnit(h), /installed units name different copies/);
|
||||
assert.deepEqual([proxyFile(h), busFile(h), webuiFile(h)].map(read), before);
|
||||
});
|
||||
|
||||
test('row334 installed proxy without --proxy refuses and preserves all bytes', t => {
|
||||
const h = hub(t);
|
||||
assert.equal(proxyUnit(h).code, 0);
|
||||
const files = [proxyFile(h), busFile(h), webuiFile(h)], before = files.map(read);
|
||||
writeFileSync(h.log, '');
|
||||
refuses(unit(h), /installed mosaic-proxy.service requires --proxy/);
|
||||
assert.deepEqual(files.map(read), before);
|
||||
assert.equal(read(h.log), '');
|
||||
});
|
||||
|
||||
test('row334 dangling proxy symlink without flag refuses without deleting', t => {
|
||||
const h = hub(t);
|
||||
symlinkSync(join(h.root, 'absent'), proxyFile(h));
|
||||
refuses(unit(h), /installed mosaic-proxy.service requires --proxy/);
|
||||
assert.equal(readdirSync(h.units).includes('mosaic-proxy.service'), true);
|
||||
});
|
||||
|
||||
test('row334 active proxy reports old copy and all three must restart together', t => {
|
||||
const h = hub(t);
|
||||
assert.equal(proxyUnit(h).code, 0);
|
||||
const r = proxyUnit(h, { copy: COPY_B, env: { CASE2_TEST_ACTIVE: 'mosaic-proxy.service [email protected] mosaic-webui.service' } });
|
||||
assert.equal(r.code, 0, r.err);
|
||||
assert.match(r.out, /mosaic-proxy.service was already running/);
|
||||
assert.match(r.out, /restart mosaic-proxy.service, [email protected] and mosaic-webui.service together/);
|
||||
});
|
||||
for (const name of ['mosaic-proxy.service', '[email protected]']) test(`row334 failed ${name} start leaves later units unstarted`, t => {
|
||||
const h = hub(t);
|
||||
refuses(proxyUnit(h, { env: { CASE2_TEST_FAIL: `start ${name}` } }), /systemctl start .* failed/);
|
||||
assert.doesNotMatch(read(h.log), /start mosaic-webui.service/);
|
||||
if (name === 'mosaic-proxy.service') assert.doesNotMatch(read(h.log), /start [email protected]/);
|
||||
});
|
||||
|
||||
|
||||
test('row334 installed proxy enforces hardening order and remaining common refusals', t => {
|
||||
const h = hub(t);
|
||||
assert.equal(proxyUnit(h).code, 0);
|
||||
const original = read(proxyFile(h));
|
||||
for (const [edit, message] of [
|
||||
[s => s.replace('ProtectSystem=strict\nReadWritePaths=' + h.home, 'ReadWritePaths=' + h.home + '\nProtectSystem=strict'), /hardening lines must be in the order/],
|
||||
[s => s.replace('WorkingDirectory=' + copyDir(h), 'WorkingDirectory=%h'), /names no copy/],
|
||||
[s => s.replace('Type=simple', 'Type=simple\nX-Note=%h'), /%h is left; a system unit/],
|
||||
[s => s.replace('RestartSec=15', 'RestartSec=16'), /differs from the render in more than the copy id/],
|
||||
]) {
|
||||
writeFileSync(proxyFile(h), edit(original));
|
||||
refuses(proxyUnit(h), message);
|
||||
}
|
||||
});
|
||||
|
||||
test('row334 proxy installed path rejects symlink and non-regular file', t => {
|
||||
const h = hub(t);
|
||||
symlinkSync(join(h.root, 'absent'), proxyFile(h));
|
||||
refuses(proxyUnit(h), /is a symlink/);
|
||||
rmSync(proxyFile(h));
|
||||
mkdirSync(proxyFile(h));
|
||||
refuses(proxyUnit(h), /is not a regular file/);
|
||||
});
|
||||
@@ -228,7 +228,7 @@ fi
|
||||
say "business $BUSINESS validates"
|
||||
|
||||
# 8. What comes next. Root's steps run from root's own checkout.
|
||||
say "done. Next: root runs case2-unit.sh --copy $COMMIT --business $BUSINESS (add --proxy when the config has a proxy section; design 10 step 2c), then 2d and 3."
|
||||
say "done. Next: root runs case2-unit.sh --copy $COMMIT --business $BUSINESS (design 10 step 2c), then 2d and 3."
|
||||
if [ "$PROFILE" = library ]; then
|
||||
say "Then Jason places the library token in $SLOT (step 4), and <user> runs case2-precheck.sh --business $BUSINESS --library-status (step 4a)."
|
||||
fi
|
||||
|
||||
+22
-65
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Case 2 row C2-13a (design 9.3, 10 step 2c): render and install the two
|
||||
# system units a hub runs as (plus P1's proxy with --proxy), [email protected] (enabled as
|
||||
# system units a hub runs as, [email protected] (enabled as
|
||||
# mosaic-bus@<business>.service) and mosaic-webui.service, from the existing
|
||||
# templates, which it doesn't change.
|
||||
#
|
||||
# case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH
|
||||
# [--profile factory|library] [--proxy] [--pm] [--claude PATH]
|
||||
# [--profile factory|library] [--pm] [--claude PATH]
|
||||
# -- SERVE FLAGS
|
||||
#
|
||||
# Root runs it from a root-owned checkout of the reviewed commit outside
|
||||
@@ -31,8 +31,8 @@
|
||||
# --pm and --claude.
|
||||
#
|
||||
# On a re-install the installed units must equal the render in everything
|
||||
# but the copy id, and all selected units must name one copy, so a re-pin or rollback
|
||||
# re-renders them together. A unit that was already running stays on its
|
||||
# but the copy id, and both must name one copy, so a re-pin or rollback
|
||||
# re-renders the pair together. A unit that was already running stays on its
|
||||
# old copy, since `systemctl start` leaves it alone; the script says so and
|
||||
# that the bus and Console must restart together, and restarts nothing.
|
||||
#
|
||||
@@ -41,7 +41,6 @@
|
||||
# the root and root-owned checks apply only there
|
||||
# CASE2_SYSTEMCTL the systemctl command, default systemctl
|
||||
# CASE2_GETENT the getent command, default getent
|
||||
# CASE2_PROXY_TEMPLATE default packages/cli/systemd/mosaic-proxy.service.in
|
||||
# CASE2_BUS_TEMPLATE default packages/cli/systemd/mosaic-bus.service.in
|
||||
# CASE2_WEBUI_TEMPLATE default packages/webui/systemd/mosaic-webui.service.in
|
||||
#
|
||||
@@ -54,12 +53,10 @@ SYSTEMCTL="${CASE2_SYSTEMCTL:-systemctl}"
|
||||
GETENT="${CASE2_GETENT:-getent}"
|
||||
BUS_TEMPLATE="${CASE2_BUS_TEMPLATE:-$REPO/packages/cli/systemd/mosaic-bus.service.in}"
|
||||
WEBUI_TEMPLATE="${CASE2_WEBUI_TEMPLATE:-$REPO/packages/webui/systemd/mosaic-webui.service.in}"
|
||||
PROXY_TEMPLATE="${CASE2_PROXY_TEMPLATE:-$REPO/packages/cli/systemd/mosaic-proxy.service.in}"
|
||||
PROXY_UNIT=mosaic-proxy.service
|
||||
BUS_UNIT=[email protected]
|
||||
WEBUI_UNIT=mosaic-webui.service
|
||||
|
||||
usage() { echo 'usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--proxy] [--pm] [--claude PATH] -- SERVE FLAGS' >&2; exit 4; }
|
||||
usage() { echo 'usage: case2-unit.sh --user NAME --home DIR --business ID --copy ID --node PATH [--profile factory|library] [--pm] [--claude PATH] -- SERVE FLAGS' >&2; exit 4; }
|
||||
die() { echo "case2-unit: $*" >&2; exit 1; }
|
||||
# A value the unit carries as itself: no space, %, $, quote, backslash, & or |.
|
||||
plain() { case "$1" in *[!A-Za-z0-9/._+@-]*) return 1 ;; esac; }
|
||||
@@ -70,7 +67,7 @@ abs_plain() {
|
||||
[ "$1" = / ] || [ "${1%/}" = "$1" ] || die "$2 must not end in /: $1"
|
||||
}
|
||||
|
||||
PROFILE=library USER_NAME='' HOME_DIR='' BUSINESS='' COPY_ID='' NODE='' PROXY=0 PM=0 CLAUDE='' CLAUDE_SET=0
|
||||
PROFILE=library USER_NAME='' HOME_DIR='' BUSINESS='' COPY_ID='' NODE='' PM=0 CLAUDE='' CLAUDE_SET=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--profile|--user|--home|--business|--copy|--node|--claude)
|
||||
@@ -81,7 +78,6 @@ while [ $# -gt 0 ]; do
|
||||
--claude) CLAUDE="$2"; CLAUDE_SET=1 ;;
|
||||
esac
|
||||
shift 2 ;;
|
||||
--proxy) PROXY=1; shift ;;
|
||||
--pm) PM=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) usage ;;
|
||||
@@ -154,7 +150,6 @@ fi
|
||||
# The checkout the templates come from, when installing for real.
|
||||
if [ "$UNIT_DIR" = "$DEFAULT_UNIT_DIR" ]; then
|
||||
case "$REPO/" in "$HOME_DIR"/*) die "run this from a root-owned checkout outside $HOME_DIR, not $REPO" ;; esac
|
||||
[ "$PROXY" = 0 ] || root_only "$PROXY_TEMPLATE" 'proxy template'
|
||||
root_only "$BUS_TEMPLATE" 'bus template'
|
||||
root_only "$WEBUI_TEMPLATE" 'Console template'
|
||||
fi
|
||||
@@ -454,10 +449,7 @@ render() {
|
||||
fi
|
||||
if printf '%s\n' "$text" | grep -q '%h'; then die "$template: %h is left after the render"; fi
|
||||
local add="User=$USER_NAME\nGroup=$GROUP\n${HARDENING//$'\n'/\\n}\n"
|
||||
[ "$kind" != webui ] || add="$add${CAPS//$'\n'/\\n}\n"
|
||||
if [ "$kind" = bus ] && [ "$PROXY" = 1 ]; then
|
||||
text="$(printf '%s\n' "$text" | sed "/^Description=/a Wants=$PROXY_UNIT\\nAfter=$PROXY_UNIT")"
|
||||
fi
|
||||
[ "$kind" = bus ] || add="$add${CAPS//$'\n'/\\n}\n"
|
||||
# The added lines go at the end of [Service], before its trailing blank
|
||||
# lines. In [Install], WantedBy=multi-user.target replaces
|
||||
# WantedBy=default.target in both units, so both start at boot (Q189).
|
||||
@@ -525,7 +517,7 @@ check_unit() {
|
||||
one WorkingDirectory "$copy"
|
||||
# Environment= lines: PATH, and MOSAIC_CLAUDE_BIN only with --claude.
|
||||
local envs want_envs="[Service] PATH=$UNIT_PATH"
|
||||
[ "$kind" != bus ] || [ -z "$CLAUDE_ENV" ] || want_envs="$want_envs
|
||||
[ "$kind" = webui ] || [ -z "$CLAUDE_ENV" ] || want_envs="$want_envs
|
||||
[Service] MOSAIC_CLAUDE_BIN=$CLAUDE"
|
||||
envs="$(printf '%s\n' "$s" | awk -F'\t' '$2 == "Environment" { print $1 "\t" $3 }')"
|
||||
[ "$envs" = "$want_envs" ] || die "$what: the Environment= lines must be exactly: $(printf '%s' "$want_envs" | cut -f2 | tr '\n' ' ')"
|
||||
@@ -545,11 +537,6 @@ check_unit() {
|
||||
one EnvironmentFile "$ENV_FILE"
|
||||
none AmbientCapabilities
|
||||
none CapabilityBoundingSet
|
||||
elif [ "$kind" = proxy ]; then
|
||||
one ExecStart "$NODE $copy/packages/model-proxy/src/run.mjs"
|
||||
none EnvironmentFile
|
||||
none AmbientCapabilities
|
||||
none CapabilityBoundingSet
|
||||
else
|
||||
one ExecStart "$NODE $copy/packages/webui/src/cli.mjs serve $FLAGS"
|
||||
none EnvironmentFile
|
||||
@@ -567,16 +554,7 @@ check_unit webui "$WEBUI_TEXT" "rendered $WEBUI_UNIT" "$COPY"
|
||||
# copy, and each equals the render once its copy id is the new one.
|
||||
[ -d "$UNIT_DIR" ] && [ ! -L "$UNIT_DIR" ] || die "no unit directory $UNIT_DIR"
|
||||
installed_ids=''
|
||||
PAIRS=("bus:$BUS_UNIT:BUS_TEXT" "webui:$WEBUI_UNIT:WEBUI_TEXT")
|
||||
if [ "$PROXY" = 1 ]; then
|
||||
PROXY_TEXT="$(render proxy "$PROXY_TEMPLATE")" || exit 1
|
||||
check_unit proxy "$PROXY_TEXT" "rendered $PROXY_UNIT" "$COPY"
|
||||
PAIRS=("proxy:$PROXY_UNIT:PROXY_TEXT" "${PAIRS[@]}")
|
||||
else
|
||||
[ ! -e "$UNIT_DIR/$PROXY_UNIT" ] && [ ! -L "$UNIT_DIR/$PROXY_UNIT" ] \
|
||||
|| die "installed $PROXY_UNIT requires --proxy; nothing was changed"
|
||||
fi
|
||||
for pair in "${PAIRS[@]}"; do
|
||||
for pair in "bus:$BUS_UNIT:BUS_TEXT" "webui:$WEBUI_UNIT:WEBUI_TEXT"; do
|
||||
IFS=: read -r kind name var <<<"$pair"
|
||||
path="$UNIT_DIR/$name"
|
||||
[ ! -L "$path" ] || die "refusing: $path is a symlink"
|
||||
@@ -591,17 +569,13 @@ for pair in "${PAIRS[@]}"; do
|
||||
installed_ids="$installed_ids $old"
|
||||
done
|
||||
read -r -a ids <<<"$installed_ids"
|
||||
for old in "${ids[@]}"; do
|
||||
[ "$old" = "${ids[0]}" ] || die "the installed units name different copies, ${ids[0]} and $old"
|
||||
done
|
||||
[ "${#ids[@]}" -lt 2 ] || [ "${ids[0]}" = "${ids[1]}" ] || die "the installed units name different copies, ${ids[0]} and ${ids[1]}"
|
||||
|
||||
# `systemctl start` leaves a running unit on the copy it started from, so
|
||||
# note which units run before the write (row 195 owns the restart).
|
||||
BUS_NAME="mosaic-bus@$BUSINESS.service"
|
||||
NAMES=("$BUS_NAME" "$WEBUI_UNIT")
|
||||
[ "$PROXY" = 0 ] || NAMES=("$PROXY_UNIT" "${NAMES[@]}")
|
||||
running=''
|
||||
for name in "${NAMES[@]}"; do
|
||||
for name in "$BUS_NAME" "$WEBUI_UNIT"; do
|
||||
if "$SYSTEMCTL" is-active --quiet "$name"; then running="$running $name"; fi
|
||||
done
|
||||
|
||||
@@ -614,40 +588,23 @@ write_unit() {
|
||||
[ "$(id -u)" != 0 ] || chown root:root "$tmp"
|
||||
mv -fT -- "$tmp" "$UNIT_DIR/$name"
|
||||
}
|
||||
for pair in "${PAIRS[@]}"; do
|
||||
IFS=: read -r kind name var <<<"$pair"
|
||||
write_unit "$name" "${!var}"
|
||||
done
|
||||
# Enable all selected units before starting proxy (if selected), bus, Console.
|
||||
# Only the bus depends on the proxy; the Console calls the bus per request.
|
||||
write_unit "$BUS_UNIT" "$BUS_TEXT"
|
||||
write_unit "$WEBUI_UNIT" "$WEBUI_TEXT"
|
||||
# Enable both, then start the bus, then the Console (design 10 step 2c).
|
||||
# serve calls the bus per request, so the units carry no After= or Wants=.
|
||||
"$SYSTEMCTL" daemon-reload || die "systemctl daemon-reload failed; the units are written, nothing is enabled or started"
|
||||
"$SYSTEMCTL" enable "${NAMES[@]}" || die "systemctl enable ${NAMES[*]} failed; nothing is started"
|
||||
if [ "$PROXY" = 1 ]; then
|
||||
"$SYSTEMCTL" start "$PROXY_UNIT" || die "systemctl start $PROXY_UNIT failed; the bus and Console are not started"
|
||||
fi
|
||||
"$SYSTEMCTL" enable "$BUS_NAME" "$WEBUI_UNIT" || die "systemctl enable $BUS_NAME $WEBUI_UNIT failed; nothing is started"
|
||||
"$SYSTEMCTL" start "$BUS_NAME" || die "systemctl start $BUS_NAME failed; the Console is not started"
|
||||
"$SYSTEMCTL" start "$WEBUI_UNIT" || die "systemctl start $WEBUI_UNIT failed; the bus is running"
|
||||
if [ "$PROXY" = 1 ]; then
|
||||
echo "case2-unit: wrote $UNIT_DIR/$PROXY_UNIT, $UNIT_DIR/$BUS_UNIT and $UNIT_DIR/$WEBUI_UNIT from copy $COPY_ID and enabled all three"
|
||||
if [ -z "$running" ]; then
|
||||
echo "case2-unit: enabled and started $PROXY_UNIT, then $BUS_NAME, then $WEBUI_UNIT"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
if [ -z "$running" ]; then
|
||||
echo "case2-unit: wrote $UNIT_DIR/$BUS_UNIT and $UNIT_DIR/$WEBUI_UNIT from copy $COPY_ID; enabled and started $BUS_NAME, then $WEBUI_UNIT"
|
||||
exit 0
|
||||
fi
|
||||
echo "case2-unit: wrote $UNIT_DIR/$BUS_UNIT and $UNIT_DIR/$WEBUI_UNIT from copy $COPY_ID and enabled both"
|
||||
if [ -z "$running" ]; then
|
||||
echo "case2-unit: wrote $UNIT_DIR/$BUS_UNIT and $UNIT_DIR/$WEBUI_UNIT from copy $COPY_ID; enabled and started $BUS_NAME, then $WEBUI_UNIT"
|
||||
exit 0
|
||||
fi
|
||||
for name in "${NAMES[@]}"; do
|
||||
echo "case2-unit: wrote $UNIT_DIR/$BUS_UNIT and $UNIT_DIR/$WEBUI_UNIT from copy $COPY_ID and enabled both"
|
||||
for name in "$BUS_NAME" "$WEBUI_UNIT"; do
|
||||
case " $running " in
|
||||
*" $name "*) echo "case2-unit: $name was already running, so it is still on ${ids[0]:+copy }${ids[0]:-the copy it started from}, not copy $COPY_ID" ;;
|
||||
*) echo "case2-unit: started $name on copy $COPY_ID" ;;
|
||||
esac
|
||||
done
|
||||
if [ "$PROXY" = 1 ]; then
|
||||
echo "case2-unit: restart $PROXY_UNIT, $BUS_NAME and $WEBUI_UNIT together to put all three on copy $COPY_ID; until then they may run different copies"
|
||||
else
|
||||
echo "case2-unit: restart $BUS_NAME and $WEBUI_UNIT together to put both on copy $COPY_ID; until then they may run different copies"
|
||||
fi
|
||||
echo "case2-unit: restart $BUS_NAME and $WEBUI_UNIT together to put both on copy $COPY_ID; until then they may run different copies"
|
||||
Reference in new issue
Block a user