Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9a564e6d2 | ||
|
|
a45c6c9ce5 | ||
|
|
b47ee2c0ef | ||
|
|
e7438743ff | ||
|
|
c93df1608a | ||
|
|
f2986b9ead | ||
|
|
3df440d076 | ||
|
|
1aac8bcd7a | ||
|
|
69811540ce | ||
|
|
9e94575c5e | ||
|
|
a5251007bd | ||
|
|
4a2745a9da | ||
|
|
542b970d25 | ||
|
|
48c7c3283a | ||
|
|
40dcbabcbb |
No files matched your search
@@ -5585,3 +5585,11 @@ 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-24 Poe — row 330 / #1828 start: native Console proxy page and human-only bus verbs per Q250. Before: P1 client/config landed, no native page. Intended evidence: fake-management security/refusal tests, four named mutants, pinned offline upgrade acceptance, Gladstone/Filbert review. Tests remain held pending Sage CLEAR; P2 rebase required before gate. Notes: agents/poe/work/queue-330/BUILD.md.
|
||||
|
||||
2026-10-25 Poe — row330 / #1828 focused block: first account RED6pass/4expected; source-only fixes; eight new files88/88 green, no skips. Offline PIN8.0.42 acceptance then exited1 before initial-account checkpoint and reported nice16 despite19 wrapper. Stopped at first unexpected red, returned slot to Sage, no rerun or mutants. Root cause unresolved; no full-gate or landing claim. Evidence: agents/poe/work/queue-330/BUILD.md and green-*.txt / upgrade-8.0.42.txt.
|
||||
|
||||
2026-10-25 Poe — row330 focused retry per sage-4446: all8 files88/88 green again; uptime per step retained. After Store fixture correction, offline PIN8.0.42 acceptance reached broker-bound but refused proxy-down at initial-accounts (load5.98/15.98/16.44). Stopped, returned slot, no rerun or mutants. See agents/poe/work/queue-330/block2-* and BUILD.md. No full-gate or completion claim.
|
||||
|
||||
2026-10-25 Poe — row330 sage-4469 block: approved fixed GET provider404 normalization after exact RED2fail/9pass; GREEN10whole files99/99, pinned8.0.42 offline acceptance passed (both priority read-backs), four named mutants killed/restored with all controls green. No retries, old assertions or timeout changes. Slot returned. Evidence agents/poe/work/queue-330/404-* and BUILD.md. P2 integration/full global gate/Q246 reviews still pending; no landing claim.
|
||||
@@ -1,402 +0,0 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:47:01Z
|
||||
02:47:01 up 11 days, 18:06, 2 users, load average: 13.77, 14.23, 20.51
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:54Z
|
||||
02:46:54 up 11 days, 18:06, 2 users, load average: 13.49, 14.18, 20.53
|
||||
@@ -0,0 +1,20 @@
|
||||
P4 offline upgrade version=8.0.42 nice=16
|
||||
P4 diagnostic {"stage":"binary-start","nodePriority":16,"childPriority":16,"lastClientErrorCode":null,"nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"management-ready","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"store-open","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"broker-bound","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 empty-provider diagnostic {"httpStatus":404,"type":"object","isArray":true,"count":0}
|
||||
proxy.accounts initial empty; GET /credentials + GET /config/api-keys/claude
|
||||
proxy.login.start claude GET /oauth/auth-url; fields=url,state only
|
||||
proxy.login.submit POST /oauth/callback; fake code never ok; GET /oauth/status=wait
|
||||
proxy.login.cancel DELETE /oauth/session confirmed
|
||||
proxy.login.start codex GET /oauth/auth-url; fields=url,state only
|
||||
proxy.login.submit POST /oauth/callback; fake code never ok; GET /oauth/status=wait
|
||||
proxy.login.cancel DELETE /oauth/session confirmed
|
||||
proxy.apikey.add PUT /config/api-keys/claude; account fields=accountId,provider,displayName,status,priority; id from auth_index
|
||||
proxy.priority.set API-key PUT /config/api-keys/claude; priority read-back=7
|
||||
proxy.keys GET /config/access/api-keys; fields=keyId,management,agent,issuedAt,state; sentinel labelled; values absent
|
||||
proxy.priority.set OAuth PATCH /credentials/fields body={name,priority}; priority read-back=8
|
||||
PASS offline native-page bus contract and secret-free receipts
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":null,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":null,"childExitCode":0,"childSignal":null}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/ICE0YV
|
||||
✔ accounts have exactly the allowlisted public fields, id from credential identifier (28.859027ms)
|
||||
✔ token-like data in every upstream field never reaches public accounts (5.070752ms)
|
||||
✔ API key account is derived from proxy auth_index, never api-key, and can set priority (12.225188ms)
|
||||
✔ OAuth priority uses top-level name/priority and confirms it through list (4.580885ms)
|
||||
✔ priority-unconfirmed: accepted API-key write with unchanged read-back refuses (5.588673ms)
|
||||
✔ priority-write-refused: OAuth PATCH status 201 (3.491938ms)
|
||||
✔ priority-write-refused: OAuth PATCH status 400 (4.410853ms)
|
||||
✔ priority-invalid: unknown account refuses before any write (2.983779ms)
|
||||
✔ proxy-down: a closed management listener refuses once (1.996951ms)
|
||||
✔ client keys return ids, sentinel/foreign labels and only scoped record metadata (3.672515ms)
|
||||
ℹ tests 10
|
||||
ℹ suites 0
|
||||
ℹ pass 10
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 183.915712
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:24Z
|
||||
02:46:24 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:24Z
|
||||
02:46:24 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/WlQ8bY
|
||||
✔ fresh404 yields no accounts, then adds the first key once with no value leakage (32.08173ms)
|
||||
✔ malformed200 null remains proxy-down, no retry or write (7.8998ms)
|
||||
✔ malformed200 object remains proxy-down, no retry or write (3.878116ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 147.324337
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:23Z
|
||||
02:46:23 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:22Z
|
||||
02:46:22 up 11 days, 18:06, 2 users, load average: 13.76, 14.37, 20.83
|
||||
@@ -0,0 +1,26 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/zFmWGh
|
||||
✔ proxy.login.start: provider-not-allowed (32.675653ms)
|
||||
✔ proxy.apikey.add: provider-not-allowed (0.468026ms)
|
||||
✔ apikey-invalid empty (0.345583ms)
|
||||
✔ apikey-invalid too long (0.330348ms)
|
||||
✔ apikey-invalid charset (0.281139ms)
|
||||
✔ callback-url-invalid not URL (1.581908ms)
|
||||
✔ callback-url-invalid oversize (0.382376ms)
|
||||
✔ callback-url-invalid repeated state (0.344928ms)
|
||||
✔ login-state-unknown callback never reaches proxy (0.456109ms)
|
||||
✔ login state is scoped to the human and business (0.514738ms)
|
||||
✔ accepted callback wait is pending; only a status ok completes and retires state (0.915716ms)
|
||||
✔ expiry reports login-timeout once with no management call and retires state (0.306001ms)
|
||||
✔ cancel calls DELETE and retires state (0.39506ms)
|
||||
✔ upstream OAuth error body never crosses bus response (0.266204ms)
|
||||
✔ priority-invalid {"accountId":"bad","priority":1} (0.216492ms)
|
||||
✔ priority-invalid {"accountId":"aaaaaaaaaaaa","priority":1.5} (0.206751ms)
|
||||
✔ priority-invalid {"accountId":"aaaaaaaaaaaa","priority":1001} (0.245944ms)
|
||||
ℹ tests 17
|
||||
ℹ suites 0
|
||||
ℹ pass 17
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 107.671923
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:24Z
|
||||
02:46:24 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:24Z
|
||||
02:46:24 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
@@ -0,0 +1,50 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/WXpqyV
|
||||
✔ proxy.accounts: owner calls page once with business/human scope (26.116796ms)
|
||||
✔ proxy.accounts: tester human-required (20.297319ms)
|
||||
✔ proxy.accounts: agent human-required (16.822485ms)
|
||||
✔ proxy.accounts: user human-required (17.662186ms)
|
||||
✔ proxy.accounts: not-configured (23.121015ms)
|
||||
✔ proxy.login.start: owner calls page once with business/human scope (17.138825ms)
|
||||
✔ proxy.login.start: tester human-required (17.581147ms)
|
||||
✔ proxy.login.start: agent human-required (20.879742ms)
|
||||
✔ proxy.login.start: user human-required (24.234711ms)
|
||||
✔ proxy.login.start: not-configured (19.75879ms)
|
||||
✔ proxy.login.submit: owner calls page once with business/human scope (22.157242ms)
|
||||
✔ proxy.login.submit: tester human-required (25.380596ms)
|
||||
✔ proxy.login.submit: agent human-required (21.217295ms)
|
||||
✔ proxy.login.submit: user human-required (23.521967ms)
|
||||
✔ proxy.login.submit: not-configured (24.585017ms)
|
||||
✔ proxy.login.status: owner calls page once with business/human scope (22.913517ms)
|
||||
✔ proxy.login.status: tester human-required (21.7594ms)
|
||||
✔ proxy.login.status: agent human-required (28.198167ms)
|
||||
✔ proxy.login.status: user human-required (18.344285ms)
|
||||
✔ proxy.login.status: not-configured (20.030294ms)
|
||||
✔ proxy.login.cancel: owner calls page once with business/human scope (16.420549ms)
|
||||
✔ proxy.login.cancel: tester human-required (20.486195ms)
|
||||
✔ proxy.login.cancel: agent human-required (17.871874ms)
|
||||
✔ proxy.login.cancel: user human-required (20.801042ms)
|
||||
✔ proxy.login.cancel: not-configured (24.049445ms)
|
||||
✔ proxy.apikey.add: owner calls page once with business/human scope (20.743633ms)
|
||||
✔ proxy.apikey.add: tester human-required (20.590224ms)
|
||||
✔ proxy.apikey.add: agent human-required (20.621701ms)
|
||||
✔ proxy.apikey.add: user human-required (23.304327ms)
|
||||
✔ proxy.apikey.add: not-configured (26.176447ms)
|
||||
✔ proxy.priority.set: owner calls page once with business/human scope (18.750335ms)
|
||||
✔ proxy.priority.set: tester human-required (19.039252ms)
|
||||
✔ proxy.priority.set: agent human-required (18.745218ms)
|
||||
✔ proxy.priority.set: user human-required (17.214992ms)
|
||||
✔ proxy.priority.set: not-configured (17.152828ms)
|
||||
✔ proxy.keys: owner calls page once with business/human scope (18.060003ms)
|
||||
✔ proxy.keys: tester human-required (17.424731ms)
|
||||
✔ proxy.keys: agent human-required (14.943668ms)
|
||||
✔ proxy.keys: user human-required (16.025103ms)
|
||||
✔ proxy.keys: not-configured (16.373436ms)
|
||||
✔ API-key canary never enters broker events or unknown error output (15.813479ms)
|
||||
ℹ tests 41
|
||||
ℹ suites 0
|
||||
ℹ pass 41
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 994.095736
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:24Z
|
||||
02:46:24 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:23Z
|
||||
02:46:23 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/qBvaqm
|
||||
✔ empty provider GET404 yields empty array, discards body and receipts actual404 once (23.337829ms)
|
||||
✔ provider GET401 remains proxy-client-refused, no retry (5.03975ms)
|
||||
✔ provider GET403 remains proxy-client-refused, no retry (2.874184ms)
|
||||
✔ provider GET500 remains proxy-client-refused, no retry (2.839998ms)
|
||||
✔ getCredentials404 remains proxy-client-refused, no retry (3.031846ms)
|
||||
✔ getKeys404 remains proxy-client-refused, no retry (2.739596ms)
|
||||
✔ provider PUT404 remains proxy-client-refused, never treated as empty GET (1.681292ms)
|
||||
✔ provider GET200 array passes through, with receipt and no retry (1.893456ms)
|
||||
ℹ tests 8
|
||||
ℹ suites 0
|
||||
ℹ pass 8
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 148.993332
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:22Z
|
||||
02:46:22 up 11 days, 18:06, 2 users, load average: 13.76, 14.37, 20.83
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:22Z
|
||||
02:46:22 up 11 days, 18:06, 2 users, load average: 13.76, 14.37, 20.83
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/NaZhtr
|
||||
✔ OAuth management calls keep URL and state out of receipts (25.417454ms)
|
||||
✔ upstream key goes only in request body and upstream refusal body is discarded (5.332331ms)
|
||||
✔ redirected management calls are not followed or retried (2.562305ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 139.356091
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:23Z
|
||||
02:46:23 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:23Z
|
||||
02:46:23 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/UVkG6Q
|
||||
✔ Console production module graph cannot import the model-proxy client (93.862563ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 177.142216
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:30Z
|
||||
02:46:30 up 11 days, 18:06, 2 users, load average: 12.40, 14.06, 20.66
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:29Z
|
||||
02:46:29 up 11 days, 18:06, 2 users, load average: 12.40, 14.06, 20.66
|
||||
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/HkzUFL
|
||||
✔ proxy browser clears write-only key immediately, sends once and renders data as text (923.252097ms)
|
||||
✔ proxy browser OAuth opens safely, callback wait stays pending, status ok completes (2763.533016ms)
|
||||
✔ proxy browser read-only disables account changes (662.846864ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 4447.991189
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:29Z
|
||||
02:46:29 up 11 days, 18:06, 2 users, load average: 12.40, 14.06, 20.66
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:25Z
|
||||
02:46:25 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
@@ -0,0 +1,11 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/MVzxL3
|
||||
✔ API-key canary reaches fake once and zero responses/logs/receipts/store files (49.859682ms)
|
||||
✔ OAuth URLs, state and code stay out of broker records and management receipts (21.222227ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 266.31653
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:25Z
|
||||
02:46:25 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:25Z
|
||||
02:46:25 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
@@ -0,0 +1,20 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/pooz0I
|
||||
✔ owner proxy routes each make exactly one allowlisted call (30.857128ms)
|
||||
✔ proxy write refuses signed-out (3.336504ms)
|
||||
✔ proxy write refuses loopback (2.704228ms)
|
||||
✔ proxy write refuses cross-origin (1.698264ms)
|
||||
✔ proxy write refuses missing Fetch Metadata (1.705732ms)
|
||||
✔ all proxy writes are read-only without --send (4.69854ms)
|
||||
✔ granted user never reaches owner page or any proxy route (7.577808ms)
|
||||
✔ native page offers write-only key input and no panel script (2.379451ms)
|
||||
✔ Console caller rejects every non-proxy verb (1.337229ms)
|
||||
✔ tester socket refuses proxy page, reads and writes before any owner call (8.577124ms)
|
||||
✔ malformed API-key JSON never appears in errors or logs (1.827938ms)
|
||||
ℹ tests 11
|
||||
ℹ suites 0
|
||||
ℹ pass 11
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 255.783124
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:25Z
|
||||
02:46:25 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:24Z
|
||||
02:46:24 up 11 days, 18:06, 2 users, load average: 13.22, 14.25, 20.76
|
||||
@@ -0,0 +1,10 @@
|
||||
packages/model-proxy/tests/empty-provider.test.mjs
|
||||
packages/bus/tests/proxy-page-empty-provider.test.mjs
|
||||
packages/model-proxy/tests/page-client.test.mjs
|
||||
packages/bus/tests/proxy-page.test.mjs
|
||||
packages/bus/tests/proxy-page-state.test.mjs
|
||||
packages/bus/tests/proxy-page-accounts.test.mjs
|
||||
packages/webui/tests/proxy-page.test.mjs
|
||||
packages/webui/tests/proxy-canary.test.mjs
|
||||
packages/webui/tests/proxy-browser.test.mjs
|
||||
packages/webui/tests/proxy-architecture.test.mjs
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:47:52Z
|
||||
02:47:52 up 11 days, 18:07, 2 users, load average: 9.43, 13.06, 19.79
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:47:50Z
|
||||
02:47:50 up 11 days, 18:07, 2 users, load average: 9.43, 13.06, 19.79
|
||||
@@ -0,0 +1,8 @@
|
||||
{"mutant":"accounts-unfiltered-field","test":"packages/bus/tests/proxy-page-accounts.test.mjs","exit":1,"nice":19,"failures":2,"passes":8,"failingTests":["accounts have exactly the allowlisted public fields, id from credential identifier","token-like data in every upstream field never reaches public accounts"],"killed":true,"restored":true,"originalSha256":"14acdb4d3fdaabad508eec7378740b13c175d4bdd0c6e707a673138eff82365b"}
|
||||
{"mutant":"accounts-unfiltered-field","positiveControl":true,"test":"packages/bus/tests/proxy-page-accounts.test.mjs","exit":0,"nice":19,"failures":0,"passes":10,"failingTests":[],"positive":true}
|
||||
{"mutant":"callback-skips-state","test":"packages/bus/tests/proxy-page-state.test.mjs","exit":1,"nice":19,"failures":2,"passes":15,"failingTests":["login-state-unknown callback never reaches proxy","accepted callback wait is pending; only a status ok completes and retires state"],"killed":true,"restored":true,"originalSha256":"14acdb4d3fdaabad508eec7378740b13c175d4bdd0c6e707a673138eff82365b"}
|
||||
{"mutant":"callback-skips-state","positiveControl":true,"test":"packages/bus/tests/proxy-page-state.test.mjs","exit":0,"nice":19,"failures":0,"passes":17,"failingTests":[],"positive":true}
|
||||
{"mutant":"proxy-route-accepts-granted-user","test":"packages/webui/tests/proxy-page.test.mjs","exit":1,"nice":19,"failures":1,"passes":10,"failingTests":["granted user never reaches owner page or any proxy route"],"killed":true,"restored":true,"originalSha256":"7b7bee91604b7bd973027b177de83205db9aed366e694072fc6343b46465b5ba"}
|
||||
{"mutant":"proxy-route-accepts-granted-user","positiveControl":true,"test":"packages/webui/tests/proxy-page.test.mjs","exit":0,"nice":19,"failures":0,"passes":11,"failingTests":[],"positive":true}
|
||||
{"mutant":"apikey-logged","test":"packages/webui/tests/proxy-canary.test.mjs","exit":1,"nice":19,"failures":1,"passes":1,"failingTests":["API-key canary reaches fake once and zero responses/logs/receipts/store files"],"killed":true,"restored":true,"originalSha256":"14acdb4d3fdaabad508eec7378740b13c175d4bdd0c6e707a673138eff82365b"}
|
||||
{"mutant":"apikey-logged","positiveControl":true,"test":"packages/webui/tests/proxy-canary.test.mjs","exit":0,"nice":19,"failures":0,"passes":2,"failingTests":[],"positive":true}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/zwP2zD
|
||||
✖ fresh404 yields no accounts, then adds the first key once with no value leakage (31.629717ms)
|
||||
✔ malformed200 null remains proxy-down, no retry or write (10.121658ms)
|
||||
✔ malformed200 object remains proxy-down, no retry or write (5.73007ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 218.324391
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/proxy-page-empty-provider.test.mjs:7:1
|
||||
✖ fresh404 yields no accounts, then adds the first key once with no value leakage (31.629717ms)
|
||||
Error [ProxyError]: proxy-down
|
||||
at refuse (file:///home/jwoltje/.t3/worktrees/mosaic-stack/poe-row330/packages/model-proxy/src/common.mjs:8:39)
|
||||
at ProxyPage.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/poe-row330/packages/bus/src/proxy-page.mjs:80:7)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/poe-row330/packages/bus/tests/proxy-page-empty-provider.test.mjs:10:17)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
code: 'proxy-down'
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:06Z
|
||||
02:46:06 up 11 days, 18:05, 2 users, load average: 15.17, 14.64, 21.02
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:06Z
|
||||
02:46:06 up 11 days, 18:05, 2 users, load average: 15.17, 14.64, 21.02
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/gJcxnP
|
||||
✖ empty provider GET404 yields empty array, discards body and receipts actual404 once (30.718928ms)
|
||||
✔ provider GET401 remains proxy-client-refused, no retry (8.324236ms)
|
||||
✔ provider GET403 remains proxy-client-refused, no retry (2.996504ms)
|
||||
✔ provider GET500 remains proxy-client-refused, no retry (2.838583ms)
|
||||
✔ getCredentials404 remains proxy-client-refused, no retry (3.923009ms)
|
||||
✔ getKeys404 remains proxy-client-refused, no retry (4.502681ms)
|
||||
✔ provider PUT404 remains proxy-client-refused, never treated as empty GET (2.271108ms)
|
||||
✔ provider GET200 array passes through, with receipt and no retry (2.585158ms)
|
||||
ℹ tests 8
|
||||
ℹ suites 0
|
||||
ℹ pass 7
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 192.563954
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/model-proxy/tests/empty-provider.test.mjs:35:1
|
||||
✖ empty provider GET404 yields empty array, discards body and receipts actual404 once (30.718928ms)
|
||||
Error [ProxyError]: proxy-client-refused
|
||||
at ClientRequest.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/poe-row330/packages/model-proxy/src/client.mjs:30:49)
|
||||
at ClientRequest.wrapper (node:events:639:12)
|
||||
at ClientRequest.emit (node:events:514:20)
|
||||
at HTTPParser.parserOnIncomingClient (node:_http_client:973:27)
|
||||
at HTTPParser.parserOnHeadersComplete (node:_http_common:125:17)
|
||||
at Socket.socketOnData (node:_http_client:808:22)
|
||||
at Socket.emit (node:events:514:20)
|
||||
at addChunk (node:internal/streams/readable:575:12)
|
||||
at readableAddChunkPushByteMode (node:internal/streams/readable:526:3)
|
||||
at Readable.push (node:internal/streams/readable:406:5) {
|
||||
code: 'proxy-client-refused'
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:06Z
|
||||
02:46:06 up 11 days, 18:05, 2 users, load average: 15.17, 14.64, 21.02
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:46:05Z
|
||||
02:46:05 up 11 days, 18:05, 2 users, load average: 15.17, 14.64, 21.02
|
||||
@@ -0,0 +1,618 @@
|
||||
# Row 330 / P4 Console proxy page — #1828
|
||||
|
||||
2026-10-24 Poe. Base: refactor 78b69e1e6; branch poe/row330.
|
||||
|
||||
## Requirements and boundaries
|
||||
|
||||
Replacement brief blob 4c005dc4936c, heading line 17. At 78b69e1e6 the
|
||||
0ce22dde6 line references remain unchanged: verbs 27–36; accounts 37–44;
|
||||
OAuth 45–61; upstream API keys 62–74; priority 75–78; client keys 79–83;
|
||||
usage 84–85; Console routes/page 86–98; upgrade check 99–110;
|
||||
tests 112–132; order 142–143; gate and named mutants 145–150.
|
||||
Rows document Rules for every row remain at line 12.
|
||||
|
||||
No live proxy/config/credential access. No host proxy calls. Work uses its own
|
||||
node_modules. No tests until Sage CLEAR; run list derived from imports before
|
||||
whole-file runs. P2 must land and be rebased before gate; P4 lands after P2.
|
||||
Gladstone first review; Filbert landing review. No refactor push authority.
|
||||
|
||||
## Proposed implementation contract
|
||||
|
||||
Verbs: proxy.accounts, proxy.login.start, proxy.login.submit,
|
||||
proxy.login.status, proxy.login.cancel, proxy.apikey.add,
|
||||
proxy.priority.set, proxy.keys. The broker checks human transport before
|
||||
configuration, including granted users (human-required). Missing proxy:
|
||||
not-configured. Management connection failure: proxy-down, no retry.
|
||||
|
||||
Accounts return only accountId, provider, displayName, status, priority.
|
||||
accountId hashes the proxy credential identifier (12 hex SHA256), never a token.
|
||||
Provider/status values must be constrained and names must be derived from safe
|
||||
metadata; arbitrary credential field values must not be reflected. Exact pinned
|
||||
8.0.42 endpoint/field mapping still to be confirmed on the offline stand-in.
|
||||
|
||||
Login sessions live in bus memory, scoped to the owning business/human, with
|
||||
300-second default expiry. A pasted URL is bounded, parsed and matched to a
|
||||
pending state started by this bus. Finished/expired/unknown state refuses
|
||||
login-state-unknown; malformed URL refuses callback-url-invalid. Only status ok
|
||||
finishes sign-in; callback acceptance/wait never does. No URLs, states or codes
|
||||
in logs/receipts/errors. Start allows claude/codex only; API-key add allows z.ai
|
||||
only. API keys are write-only printable ASCII, bounded, never echoed.
|
||||
|
||||
Owner-only /proxy page; /api/proxy/accounts and /api/proxy/keys reads;
|
||||
/api/proxy/login/start, /submit, /status, /cancel; /api/proxy/apikey/add;
|
||||
/api/proxy/priority/set. Each maps to exactly one allowlisted bus call.
|
||||
Writes require named host, owner sign-in, own Origin, same-origin Fetch Metadata
|
||||
and --send. Tester, granted user and loopback refuse. Console holds no client,
|
||||
proxy settings or management key. Usage placeholder accepts P5's later view;
|
||||
P4 creates no usage source.
|
||||
|
||||
## Test plan (not yet run)
|
||||
|
||||
Management client: pinned endpoint/method/body contract, loopback-only auth,
|
||||
redirect refusal, bounded response, safe receipts, no retry.
|
||||
Bus: all eight verbs against fake server; separate refusal cases for missing
|
||||
config, tester/agent/granted user human-required, both provider allowlists,
|
||||
unknown/expired/finished state, invalid callback URL, each API-key invalid case,
|
||||
invalid priority/account id, and proxy-down. Explicit wait-is-not-ok test.
|
||||
Accounts: strict allowlist, ids from identifier, toxic-field fake, API-key account
|
||||
and priority round-trip. Keys: ids only, sentinel and foreign-key labels, P2 join.
|
||||
Console: owner success; granted/tester/loopback/signed-out/cross-origin/missing
|
||||
Fetch Metadata refusals; every write refuses without --send; caller allowlist;
|
||||
no webui import of management client or proxy config access.
|
||||
Canary: fake management receives API key exactly once, and all responses, logs,
|
||||
receipts, errors and store files contain zero copies. OAuth secrets likewise.
|
||||
Named mutants: unfiltered account field; skipped callback state check; granted
|
||||
user accepted at /api/proxy/; API-key echoed by error/log.
|
||||
Full model-proxy, bus, webui (MOSAIC_REQUIRE_EDITOR_BUILD=1), touched test files;
|
||||
Sage landing gate all packages except queue and scripts/test-*.sh. Upgrade check
|
||||
under unshare -rn on PIN 8.0.42; output must land here. No runs claimed yet.
|
||||
|
||||
## Design clarification (Sage sage-4347)
|
||||
|
||||
Committed researcher/P1 artifacts may be read in this checkout. Read v8.0.27
|
||||
management reference and P1 BUILD/REVIEW. P1 confirms 8.0.42 key list is an array
|
||||
and OAuth redirect URLs localhost:54545/callback (Claude) and
|
||||
localhost:1455/auth/callback (Codex). P1 does not record credential response
|
||||
fields, z.ai upstream config schema or credentials/fields PATCH payload.
|
||||
Those remain unconfirmed, not guessed as a pinned contract.
|
||||
|
||||
Expiry behavior: status for an expired pending state returns
|
||||
{status:"error", reason:"login-timeout"}, deletes the state and makes no proxy
|
||||
call. It is not signed in; later status/submit/cancel refuses login-state-unknown.
|
||||
No code or upstream error body is returned. Timeout can be provided as a trusted
|
||||
host setting, default300s; no page/agent verb may widen it.
|
||||
|
||||
Setup: npm ci --ignore-scripts --no-audit --no-fund completed in worktree root
|
||||
(128 packages) and packages/webui (2 packages), both launched at nice19 with
|
||||
fresh shell-created short TMPDIR. No canonical npm or node_modules links.
|
||||
Canonical queue next completed with view-unknown warning; move returned
|
||||
ok poe-330-start rev4077 row330 briefed→in-progress. No repair performed.
|
||||
|
||||
## Pre-run list (no execution)
|
||||
|
||||
Static esbuild import parsing of the initial RED entrypoints (no tests executed)
|
||||
produced red-import-graph.json and red-run-list.txt. Whole-file list:
|
||||
packages/model-proxy/tests/page-client.test.mjs;
|
||||
packages/bus/tests/proxy-page.test.mjs;
|
||||
packages/bus/tests/proxy-page-state.test.mjs;
|
||||
packages/webui/tests/proxy-page.test.mjs.
|
||||
The new state test's dynamic URL import points at the not-yet-implemented
|
||||
packages/bus/src/proxy-page.mjs; expected first RED is missing implementation.
|
||||
No case1-accept file is selected. Probe.sh is also slot work, on a random
|
||||
loopback port inside unshare-rn, using only the verified own fixture executable.
|
||||
No test or binary probe has run yet. The three open pinned contracts remain
|
||||
probe-pending. Priority tests currently choose integer range -1000..1000.
|
||||
|
||||
Sage sage-4352 authorizes P1 fetch into this fresh fixture. Fetch succeeded at
|
||||
nice19, verified archive, executable and panel hashes against PIN8.0.42, and
|
||||
installed only under /mnt/storage/scratch/tmp/76TDtz/download/opt/cliproxyapi/8.0.42.
|
||||
Receipt fetch.txt contains paths only. No host executable was used or inspected.
|
||||
|
||||
2026-10-24T04:04Z: node --check on all four new test files and probe.mjs,
|
||||
bash -n probe.sh, and git diff --check passed. Syntax checks only: no test
|
||||
module or pinned executable ran. No production implementation changed yet;
|
||||
RED-first requirement and Sage CLEAR slot gate remain in force. Canonical
|
||||
queue start is complete; waiting for the authorized test slot, not a Jason
|
||||
decision. No commit or push made.
|
||||
|
||||
## First RED and pinned contract probe (sage-4391 CLEAR)
|
||||
|
||||
2026-10-24T04:24Z. Four whole files ran serially, absolute paths, a fresh
|
||||
shell-created short TMPDIR per run, nice19 recorded in each receipt. No old
|
||||
test file ran and no test was rerun. All four exited1 as expected:
|
||||
|
||||
- page-client: 0pass/3fail. OAuth receipt test fails because oauthStart is
|
||||
undefined; upstream refusal/canary test because putUpstreamGroups is undefined;
|
||||
redirect test because getCredentials is undefined. Receipt red-page-client.txt.
|
||||
- broker proxy-page: 0pass/41fail. For each of the eight verbs, the owner gets
|
||||
unknown-verb instead of a page response, tester gets tester-refused instead of
|
||||
human-required, agent gets unknown-verb instead of human-required, granted user
|
||||
gets access-refused instead of human-required, and missing configuration gets
|
||||
unknown-verb instead of not-configured. Canary error test gets unknown-verb
|
||||
instead of proxy-down. All are the missing P4 dispatch boundary, not a fixture
|
||||
setup failure. Receipt red-broker.txt enumerates every failing test.
|
||||
- proxy-page-state: 0pass/17fail. Every named provider/key/callback/state/expiry/
|
||||
cancellation/priority case fails ERR_MODULE_NOT_FOUND for the not-yet-created
|
||||
packages/bus/src/proxy-page.mjs. Receipt red-state.txt enumerates each case.
|
||||
- webui proxy-page: 6pass/3fail. Owner routes return404; native page returns404;
|
||||
Console caller proxyCall is undefined. Signed-out, loopback, cross-origin,
|
||||
absent Fetch Metadata, no-send and granted-user boundary cases pass through
|
||||
existing general refusals. Receipt red-webui.txt.
|
||||
|
||||
probe.sh exited0 under unshare-rn, random loopback port, own config and made-up
|
||||
short key, verified PIN8.0.42 binary, nice19. Child exited and namespace closed.
|
||||
No host proxy or real instance state was used. probe.txt is shape-only output:
|
||||
|
||||
- GET /credentials:200 {files:[], observed_at:string}.
|
||||
- GET /oauth/auth-url?provider=claude and provider=codex:200 with url/state/status
|
||||
strings. DELETE /oauth/session?state=[redacted]:200 cancelled/status.
|
||||
- PUT /config/api-keys/claude with
|
||||
[{name,base-url,priority,keys:[{api-key}]}]:200 status/config-version.
|
||||
Used z.ai's Claude-compatible endpoint; no request reached that endpoint.
|
||||
- GET /config/api-keys/claude: array of groups, name/base-url/priority; keys
|
||||
include api-key and a proxy-generated auth_index. Values were not printed.
|
||||
- GET /credentials after API-key add remains files:[]: API-key credentials are
|
||||
not listed here. Accounts must join the provider config's safe metadata.
|
||||
- The script's priority PATCH branch required a credential row; no row existed,
|
||||
so it did NOT run. Credentials field payload and populated OAuth response fields
|
||||
remain unconfirmed. Reported rather than treating rc0 as full contract proof.
|
||||
|
||||
Slot returned to Sage after this report. GREEN, mutants and further binary probes
|
||||
require separate CLEAR. No implementation run, commit or push claimed.
|
||||
|
||||
## Probe 2 observed contract (sage-4394 CLEAR)
|
||||
|
||||
2026-10-24T04:25Z. probe2.sh exited0 under unshare-rn, own random loopback
|
||||
port/config/short made-up key, nice19. Fixture child exited; EXIT trap removed
|
||||
the entire shell-created fixture TMPDIR, including fabricated credential/config.
|
||||
Only verified fetched binary outside this temporary runtime root was retained.
|
||||
Receipt probe2.txt contains types/shapes only, no credential values.
|
||||
|
||||
POST /v8/management/credentials?name=[fixture filename] with JSON
|
||||
{type,access_token,refresh_token,expired,email} returns200 {status:string}.
|
||||
No multipart fallback was needed. Populated GET /credentials returns
|
||||
{files:[...],observed_at:string}. Credential row fields/types:
|
||||
account/string, account_type/string, auth_index/string, cooldowns/array,
|
||||
created_at/string, disabled/boolean, email/string, failed/number, id/string,
|
||||
label/string, modtime/string, name/string, path/string, provider/string,
|
||||
quota/object, recent_requests/array, runtime_only/boolean, size/number,
|
||||
source/string, status/string, status_message/string, success/number,
|
||||
type/string, unavailable/boolean, updated_at/string. priority/number appears
|
||||
after setting it. Fields other than the explicit public projection are dropped.
|
||||
|
||||
PATCH /credentials/fields observations:
|
||||
- {id,priority}400; {id,fields:{priority}}400.
|
||||
- {name:<OAuth id>,priority}200; {name:<OAuth name>,priority}200.
|
||||
- {name:<OAuth name>,fields:{priority}}200, but200 alone does not prove it changed
|
||||
priority (this may be an ignored unknown field); use top-level priority.
|
||||
- {auth_index,priority}400; {auth_index,fields:{priority}}400 for both OAuth
|
||||
and API-key identifiers.
|
||||
- {name:<auth_index>,priority}404 for both OAuth and API key.
|
||||
Final OAuth list has numeric priority and changed to one of the requested values.
|
||||
API-key group priority did not change. Consequently priority uses top-level
|
||||
{name,priority} for OAuth files; API-key priority must update its provider config,
|
||||
not credentials/fields. A future upgrade acceptance must verify both read-backs.
|
||||
|
||||
Slot returned after probe2. No GREEN or mutant run authorized yet.
|
||||
|
||||
## Candidate implementation (not GREEN-verified)
|
||||
|
||||
Written after the first RED run: fixed client operations and receipts, human-only
|
||||
broker dispatch before generic tester/user gates, bus-owned pending-state machine,
|
||||
strict public account projection, native owner-only HTTP routes and /proxy page.
|
||||
No management client/config access in the Console's new code. Reads use the owner
|
||||
human transport and writes independently require --send/Origin/Fetch Metadata.
|
||||
|
||||
Generated display names use the constrained provider and credential-id hash,
|
||||
rather than upstream label/email/group-name strings. This is a consequential
|
||||
security/usability tradeoff: the toxic-all-fields fake cannot reflect a credential,
|
||||
but the list identifies accounts by stable ids rather than email addresses.
|
||||
|
||||
P2 join remains provisional until P2 lands and this branch rebases. Current
|
||||
runtime seam projects model status metadata by business and does not read key
|
||||
files. New refusal tests for priority-unconfirmed (read-back mismatch) and
|
||||
priority-write-refused (PATCH other than200) are written FIRST; their source fixes
|
||||
await the next authorized first RED run. Provider config read-only auth_index
|
||||
must not be echoed in a config write; its preservation test is also pending.
|
||||
|
||||
Sage sage-4394 explicitly authorizes committing/pushing incremental candidate
|
||||
commits before GREEN on poe/row330; refactor landing remains gated on all green
|
||||
checks and reviews. No candidate commit is a verification/completion claim.
|
||||
|
||||
## Prepared next slot (not run)
|
||||
|
||||
Sage sage-4400 accepts the two priority paths. New code-specific first-RED tests
|
||||
are in proxy-page-accounts.test.mjs for priority-unconfirmed and
|
||||
priority-write-refused. This file must run first, then those source fixes may
|
||||
be written; no assertion is loosened. No second RED has occurred.
|
||||
|
||||
Focused list is eight whole files in focused-run-list.txt, generated by esbuild's
|
||||
import parser (focused-import-graph.json). Additional tests exercise actual
|
||||
Chromium UI behavior and the parsed Console import boundary. The canary now
|
||||
intercepts process stdout/stderr during the API-key operation, as well as
|
||||
structured HTTP/client logs and durable bus files. Deliberate log mutants cannot
|
||||
print their made-up key. Four slot-only named mutants are prepared in mutants.mjs;
|
||||
no mutant has executed. The upgrade check is prepared in acceptance.sh/mjs;
|
||||
it has not run. New refusal source fixes remain pending first RED.
|
||||
|
||||
Broader import inventory: gate-imports.mjs statically parses package test imports,
|
||||
traces paths to changed production modules, and adds all mandatory model-proxy,
|
||||
bus and webui test files. Exact paths are gate-run-list.txt; summary includes
|
||||
additional collateral files. case1-accept.test.mjs is explicitly excluded even
|
||||
if imported, per mos-1612. No text search of fixture payloads selects a run.
|
||||
Webui's eventual full gate requires editor build and MOSAIC_REQUIRE_EDITOR_BUILD=1.
|
||||
These inventory commands parse source only; they do not import/execute tests.
|
||||
|
||||
Sage-authorized incremental candidate40dcbabcb pushed successfully to poe/row330.
|
||||
No PR or review request yet, no refactor modification/push, no live change.
|
||||
|
||||
2026-10-24T04:42Z refactor input check requested by sage-4411:
|
||||
`git diff --name-only 78b69e1e6 775bbe236` names45 paths; intersection with
|
||||
focused-import-graph.json's111 parsed inputs is empty. Exact receipts:
|
||||
refactor-775bbe236-paths.txt and refactor-775bbe236-input-check.json.
|
||||
|
||||
Correction to the shorthand report poe-330-0011: the generated broader inventory
|
||||
contains280 selected files, not281. Mandatory199 (model-proxy/bus/webui), plus81
|
||||
additional import dependents. case1-accept remains excluded. Preserve the exact
|
||||
list, not the mistaken shorthand count. The large full graph is kept off Git;
|
||||
gate-import-summary.json cites its scratch path and SHA256. A compact per-file
|
||||
import witness and the reproducible parser script land instead.
|
||||
|
||||
Sage sage-4411 accepts first-RED account test → source fix → eight focused whole
|
||||
files → offline acceptance → four mutants. No CLEAR yet: slot order is Bubba288,
|
||||
Magica331 GREEN, then this block. Full gate follows P2 landing/rebase/key join.
|
||||
|
||||
## Focused block sage-4430 — stopped at offline acceptance failure
|
||||
|
||||
2026-10-25T05:39Z precheck on candidate48c7c3283:
|
||||
`git diff --name-only 78b69e1e6 e1d8756b5` names47 paths; none overlaps the111
|
||||
focused parsed inputs. refactor-e1d8756b5-input-check.json records the check.
|
||||
|
||||
First RED of proxy-page-accounts.test.mjs:6pass/4expected failures, rc1. Failed
|
||||
API priority write included derived auth_index; mismatch refusal was proxy-down;
|
||||
PATCH201 and400 refusals were proxy-down. Receipt red-accounts.txt.
|
||||
|
||||
Source fix only, no test edits: remove derived auth_index from provider config
|
||||
writes; require exactly200 for priority PATCH/PUT; preserve priority-write-refused
|
||||
for refused/redirected writes and priority-unconfirmed for a mismatching read-back.
|
||||
Console forwards those safe refusal codes. No raw errors/keys introduced.
|
||||
|
||||
Eight focused whole files then passed88/88 with0skip, serialized, absolute paths,
|
||||
one shell-created short TMPDIR per run. Every shell wrapper recorded nice19:
|
||||
page-client3; broker41; state17; accounts10; webui HTTP11; canary2; browser3;
|
||||
architecture1. Receipts green-*.txt. These are focused checks, not full suites
|
||||
or landing evidence. No old test file ran and no assertion/timeout changed.
|
||||
|
||||
Offline acceptance.sh on the same own PIN8.0.42 fetched executable exited1.
|
||||
Raw receipt upgrade-8.0.42.txt is only:
|
||||
|
||||
P4 offline upgrade version=8.0.42 nice=16
|
||||
P4 upgrade FAILED code=acceptance-failed
|
||||
|
||||
This was the FIRST unexpected red. It occurred before the initial empty-accounts
|
||||
checkpoint. The catch intentionally strips exception detail, so the failed
|
||||
assertion/location is not known. Node reported16 despite the absolute19 shell
|
||||
wrapper; no claim that the binary acceptance ran at19. The private namespace
|
||||
child was stopped and the shell trap deleted its own fixture root as designed.
|
||||
No host proxy, real key or real data root was involved.
|
||||
|
||||
Stopped immediately and reported poe-330-0014 to Sage; slot returned for
|
||||
Magica331r2. No rerun, post-failure source fix, test edit or timeout change.
|
||||
Four mutants were NOT RUN. Before this failure, their runner was prepared to
|
||||
restore each source and run its whole-file positive control at nice19.
|
||||
Next acceptance diagnosis needs Sage's ruling/CLEAR. Full suites, P2 rebase/key
|
||||
join, mutants and Gladstone/Filbert reviews remain outstanding.
|
||||
|
||||
## Diagnostic-only preparation — sage-4432
|
||||
|
||||
2026-10-25T05:45Z. Prepared a separate acceptance.mjs diagnostic, with no run:
|
||||
fixed stage labels binary-start, management-ready, store-open, broker-bound,
|
||||
initial-accounts; observed Node/own fixture-child priorities; whitelisted last
|
||||
management client failure code; child exit code/signal; sanitized catch class/code.
|
||||
No exception message, actual/expected, request/response body, URL or key is printed.
|
||||
A child-exit observation reads no priority once that child is known to have exited.
|
||||
|
||||
No priority setter was added. The shell wrapper and every assertion, retry count,
|
||||
pause and timeout remain unchanged, as required to repeat the failed conditions.
|
||||
Static comparison confirms assertion lines and timeout expressions unchanged;
|
||||
git diff --check passes. No acceptance, test, proxy binary or mutant executed.
|
||||
This is a diagnostic commit only, not a product fix. Slot remains with Sage;
|
||||
next run waits for CLEAR after288 gate rerun and Magica331r2. The full focused /
|
||||
acceptance / mutant block after diagnosis still requires Sage's CLEAR.
|
||||
|
||||
## Scheduling observation preparation — sage-4438 / mos-1675
|
||||
|
||||
2026-10-25T06:09Z. Slot order changed: Magica331r2, Sage's short288 diagnostic,
|
||||
then this row's diagnostic; wait for Sage CLEAR. Added only numeric scheduling
|
||||
policy observations from /proc/<own Node pid>/sched and the still-live fixture
|
||||
child's sched file. No raw proc content, other process, scheduling setter or
|
||||
host configuration access/change. Diagnostic module not executed.
|
||||
|
||||
Context supplied by Sage (not independently measured here): ananicy-cpp's node
|
||||
rule assigns BG_CPUIO (nice16, sched idle, ioclass idle), and bash's Doc-View rule
|
||||
sets nice-4. Sage reports local ASR/ffmpeg load05:41–05:50Z overlapping the failed
|
||||
acceptance. These facts explain why a launch wrapper's19 is insufficient proof
|
||||
of the Node process's later policy. Causation of the acceptance failure remains
|
||||
unestablished until the authorized stage diagnostic. No assertion, timeout,
|
||||
product code, fixture condition or priority/policy setter changed.
|
||||
|
||||
## One diagnostic reproduction — sage-4440 CLEAR
|
||||
|
||||
2026-10-25T06:14:19Z–06:14:23Z. Ran acceptance.sh exactly once on unchanged
|
||||
candidatea5251007b, with no source/assertion/timeout/priority edits in the slot.
|
||||
Start uptime/load:11 days16:34,2.90/5.40/9.26. End:11 days16:34,3.15/5.41/9.24.
|
||||
Receipts diagnostic-start-uptime.txt, diagnostic-8.0.42.txt and
|
||||
diagnostic-end-uptime.txt preserve exact output.
|
||||
|
||||
Reproduced rc1. Reached binary-start, management-ready, store-open, then:
|
||||
{stage:store-open,errorClass:Error,code:acceptance-failed}. Did not reach
|
||||
broker-bound or initial-accounts. Store-open is observed AFTER Store construction;
|
||||
remaining interval includes ProxyPage/Broker construction and human binding.
|
||||
No failed assertion location or root cause is claimed from the sanitized code.
|
||||
Readiness succeeded. lastClientErrorCode proxy-client-down records an earlier
|
||||
transient startup refusal, not a readiness failure.
|
||||
|
||||
Node and the live fixture child both observed nice16 and numeric scheduling
|
||||
policy5 at all checkpoints. This establishes scheduling conditions, not cause.
|
||||
Cleanup observed fixture child exit0 with no signal; its private temp root was
|
||||
removed by the existing trap. No live proxy/key/data access occurred.
|
||||
|
||||
Stopped after that one run and reported poe-330-0019; SLOT BACK to Sage for288
|
||||
whole-gate rerun, then Magica331r3. No focused suites, mutants or second diagnostic.
|
||||
No product or diagnostic source change after this result. Receipt-only commit
|
||||
follows outside the returned slot; next investigative step awaits Sage's ruling.
|
||||
|
||||
## Source-established fixture correction — sage-4442 option1
|
||||
|
||||
2026-10-25T06:16Z, source reading only, no run. Root cause: acceptance.mjs:74
|
||||
(at9e94575c5) passes path.join(root,'bus') to new Store. Fixture preparation at
|
||||
:24–:27 creates root/proxy/auth, root/home and config.yaml, but no root/bus.
|
||||
packages/bus/src/store.mjs:163 immediately lstatSync(dataRoot), requiring an
|
||||
existing directory; :166 then creates its OWN bus child. Thus this acceptance
|
||||
passes a nonexistent data root and gets filesystem ENOENT before Store can open.
|
||||
ENOENT was not in the diagnostic allowlist and was sanitized to acceptance-failed.
|
||||
The smallest fix is new Store(root), the existing fresh fixture data root,
|
||||
matching the focused fixtures. No production behavior or authority changes.
|
||||
|
||||
Correction to poe-330-0019 and the prior diagnostic interpretation: the
|
||||
store-open observation does NOT prove successful Store construction. observe()
|
||||
in the catch emits the same stage, so the observed line can be the catch's line.
|
||||
The successful management-ready checkpoint remains valid; there was no later
|
||||
broker-bound. Our earlier post-Store inference was wrong. Source resolves this
|
||||
without another diagnostic: the missing fixture directory explains the precise
|
||||
failure and its sanitized Error/code independently of scheduling/load.
|
||||
|
||||
Also read ProxyPage constructor validation (:27–:30), Broker business validation
|
||||
(:373–:389, :558), and bindHuman (:1482–:1487). The page settings, business id/
|
||||
human/authority/arbiters and the CLI/outsideAgent:true record meet those checks.
|
||||
No human-boundary workaround is needed or made.
|
||||
|
||||
Prepared a separate single-line acceptance fixture fix, plus this append-only
|
||||
explanation. No assertion, timeout, diagnostic extension, product change or
|
||||
priority/policy setter. No diagnostic, acceptance, test or mutant execution;
|
||||
next run waits for Sage CLEAR after the288 gate.
|
||||
|
||||
## Full focused block retry — sage-4446 CLEAR, stopped at initial accounts
|
||||
|
||||
2026-10-25T06:41Z–06:42Z on unchanged69811540c. All eight focused files ran
|
||||
whole, serialized with fresh short TMPDIR and absolute nice19 wrappers;88/88
|
||||
passed,0skip. Each step has UTC timestamp and uptime start/end in
|
||||
block2-<test-path>.txt.start/.end, with complete test receipt block2-*.txt.
|
||||
No old test file, assertion edit or timeout change.
|
||||
|
||||
Offline acceptance started06:42:18Z and ended06:42:22Z, uptime11days17:02,
|
||||
load5.98/15.98/16.44 both endpoints. Reached management-ready, store-open,
|
||||
broker-bound, then initial-accounts. Failed rc1 with code proxy-down,
|
||||
errorClass Error, before the initial-empty assertion checkpoint. Thus the
|
||||
previous missing Store root failure is traversed, but the native account-list
|
||||
boundary now refuses. No root-cause inference or source fix yet.
|
||||
|
||||
Node observed nice19/policy0 through the initial-accounts entry, and nice16/
|
||||
policy5 at the failure. Fixture child observed nice19/policy0 until cleanup,
|
||||
then cleanexit0 with no signal. Idle scheduling is accepted under mos-1678;
|
||||
these observations do not establish a scheduling cause. The remembered
|
||||
proxy-client-down remains the earlier transient readiness failure.
|
||||
|
||||
Stopped at this FIRST unexpected red and reported poe-330-0022, SLOT BACK for
|
||||
Magica331r3. No acceptance rerun, focused-file retry, product/fixture edit or
|
||||
mutant execution. New receipt block2-acceptance.txt preserves sanitized output;
|
||||
private fixture root removed by the existing trap. Receipt-only commit follows;
|
||||
next step requires Sage's ruling/CLEAR. Full gate/P2 join/reviews still pending.
|
||||
|
||||
## Empty-provider source reading and diagnostic — sage-4448/4451/4452
|
||||
|
||||
2026-10-25T06:47Z, no run. Available committed upstream reference is8.0.27,
|
||||
not a pinned8.0.42 implementation source. management-api-v8.0.27.md:25–27 says
|
||||
GET does not add omitted runtime defaults; it does not specify the HTTP status
|
||||
or body shape for a missing provider group. P1 and our previous probes measured
|
||||
provider GET only after adding a group. Baseline-empty shape remains unproven.
|
||||
|
||||
page-client.test.mjs:7–14 defaults to200/{status:ok}, but no case calls
|
||||
getUpstreamGroups. The actual accounts fake proxy-page-fixture.mjs:7,:18–19
|
||||
returns200/[] for no groups. If the real binary returns another valid shape,
|
||||
client.mjs/proxy-page.mjs has a product contract finding, not a fixture bug.
|
||||
Reported to Sage before making this diagnostic; no product fix is assumed.
|
||||
|
||||
Prepared a separate diagnostic-only change: immediately before initial account
|
||||
assertion, call client.getUpstreamGroups once directly and print only HTTP
|
||||
status, type, array flag and count (array length or object top-level key count).
|
||||
Per narrower sage-4452/mos-1683 it prints NO key names and NO values. Null is
|
||||
reported as type null/count0; no response body is printed. On client error only
|
||||
a fixed allowlisted client code is printed, never a raw error/message/body.
|
||||
Status comes from the existing safe management receipt, not another request.
|
||||
|
||||
No assertions, timeouts, retries, scheduling settings, product source or test
|
||||
fixtures changed. No module/test/binary/diagnostic executed. Wait for Sage's
|
||||
review and CLEAR after the288 processing diagnostic before one acceptance run.
|
||||
|
||||
2026-10-25T06:49Z sage-4454 review addition: diagnostic error output now includes
|
||||
httpStatus from the same existing receipt written by ManagementClient finally,
|
||||
using the identical receipt-count guard as success. No new request or raw
|
||||
exception data; this distinguishes404 from5xx without names or values. Separate
|
||||
one-line diagnostic commit, no module/test/binary execution. Hold for CLEAR after
|
||||
Sage's288 processing pair; no independent load-based run decision.
|
||||
|
||||
## Empty-provider diagnostic result — sage-4458 CLEAR
|
||||
|
||||
2026-10-25T07:32:25Z–07:32:30Z, exactly one acceptance diagnostic on unchanged
|
||||
f2986b9ea. Start load52.34/45.27/29.51; end51.91/45.30/29.60. Reached the target
|
||||
under this unusually high load; Node and fixture child nice16/policy5. Allowed
|
||||
empty-provider output: {httpStatus:404,code:proxy-client-refused}. No response
|
||||
body or key names/values were parsed/printed on that error path. Management
|
||||
readiness succeeded; broker binding succeeded; subsequent proxy.accounts refused
|
||||
proxy-down at initial-accounts. Fixture child cleanup exit0, no signal.
|
||||
|
||||
This proves the product's fresh-provider contract mismatch: same pinned8.0.42
|
||||
route returns404 before any Claude provider group is configured, while earlier
|
||||
probe/probe2 receipts prove200/array after a group is added. Our accounts fake
|
||||
returns200/[] before configuration. This is not another acceptance fixture bug.
|
||||
|
||||
Stopped, reported poe-330-0027 and returned SLOT BACK for Magica331r4. No retry,
|
||||
focused file, mutant, scheduling change, product/test/assertion/timeout edit.
|
||||
Receipts empty-provider-diagnostic.txt and .start/.end preserve the single run.
|
||||
|
||||
## Proposed product diff — NOT APPLIED, review before edits
|
||||
|
||||
proposed-empty-provider.patch gives the exact proposed source change: in the
|
||||
management client's fixed page operation, normalize404 to[] ONLY for
|
||||
GET /v8/management/config/api-keys/claude. Keep the actual404 receipt. Every other
|
||||
endpoint, method and status keeps existing errors; no broad error-to-empty fall
|
||||
back. The page then renders no accounts and can add the first z.ai group.
|
||||
This introduces no new refusal code or public schema/policy change.
|
||||
|
||||
Proposed verification, awaiting Sage: write a new client RED test for this exact
|
||||
404 normalization before applying the patch; prove credentials/key-list404 and
|
||||
provider401/403/500 remain proxy-client-refused without retry; prove200/array
|
||||
passes through. Add account-level cases for fresh404 -> empty accounts -> first
|
||||
key add, with no key in responses/receipts; malformed200 null/object still
|
||||
refuses proxy-down. Existing assertion strength stays intact. Update the
|
||||
stand-in's empty-route option only to model the observed contract; no fake can
|
||||
silently turn unknown failures into empty data. Run lists/CLEAR precede tests.
|
||||
|
||||
No product or test changed in this receipt/proposal commit. Proposed patch is
|
||||
review material, not a fix claim or a substitute for RED/GREEN/upgrade evidence.
|
||||
|
||||
## Regression preparation — sage-4460, no Node execution
|
||||
|
||||
2026-10-25T07:38Z. Authored11 cases in two new whole test files:
|
||||
packages/model-proxy/tests/empty-provider.test.mjs (8): exact fixed GET404 ->[],
|
||||
actual404 receipt/discarded canary body/one request; credentials404 and key-list404
|
||||
remain proxy-client-refused; provider401/403/500 remain proxy-client-refused;
|
||||
providerPUT404 remains proxy-client-refused (exercises GET-only guard);200 array
|
||||
passes through. Every case pins exactly one call and status receipt, no retry,
|
||||
no response-body/management-key leakage into receipts or error output.
|
||||
packages/bus/tests/proxy-page-empty-provider.test.mjs (3): fresh404 empty account
|
||||
list followed by one first-key PUT and a public safe account; malformed200 null
|
||||
and object each refuse existing proxy-down with no write/retry.
|
||||
|
||||
Stand-in addition only: optional emptyProviderResponse controls the empty-route
|
||||
status/body for these new cases. Existing fake baseline and every old assertion
|
||||
and timeout remain unchanged. Imports are declared directly to ManagementClient
|
||||
and the existing management fixture (which imports ProxyPage/client). Static
|
||||
single-line import declarations are recorded in empty-provider-prepared-imports
|
||||
and rooted in the prior esbuild graph; no fixture-text search selects tests.
|
||||
Exact initial RED list: empty-provider-red-run-list.txt. Prepared source/test/patch
|
||||
hashes: empty-provider-prepared-MANIFEST.sha256. Product files remain unmodified;
|
||||
proposed-empty-provider.patch is still NOT APPLIED.
|
||||
|
||||
Expected first RED: new exact404 client case rejects proxy-client-refused instead
|
||||
of returning[]; new fresh account case rejects proxy-down before add. No new
|
||||
refusal code; all negative cases preserve existing named refusals. Observe this
|
||||
RED under the next CLEAR before applying the approved scoped source patch.
|
||||
Required later GREEN should include both new whole files and the8 focused whole
|
||||
files because the helper is shared; full gate and P2 key-join still wait their
|
||||
separate authorized stage. No Node syntax check, test, acceptance, mutant or
|
||||
proxy process ran during preparation. Slot remains with Magica331r4.
|
||||
|
||||
## Test-first404 block — sage-4469 CLEAR
|
||||
|
||||
2026-10-25T07:46Z–07:47Z. On prepared e7438743f, unchanged product RED was
|
||||
exactly the two intended failures: model-proxy empty-provider8cases7pass/1fail
|
||||
(proxy-client-refused rather than[]), bus empty-provider3cases2pass/1fail
|
||||
(proxy-down on fresh accounts). Negative controls all passed; no skipped,
|
||||
cancelled or timed-out case. Receipts404-red-*.txt plus per-file uptime.start/.end.
|
||||
|
||||
Applied ONLY the approved proposed-empty-provider.patch: three lines in the
|
||||
client's #page catch normalize404 only for the fixed GET provider-config path.
|
||||
Actual404 receipt remains; error body was discarded by #request. No assertion,
|
||||
timeout, scheduling, public schema, other product file or refusal code changed.
|
||||
|
||||
GREEN: both new files and the existing8focused files whole, serialized,99/99
|
||||
passes,0skip/cancel. Fresh short TMPDIR per file; shell wrappers requested and
|
||||
recorded nice19. No automatic retry. Receipts404-green-*.txt and each file's
|
||||
UTC/uptime start/end. Idle scheduling is accepted under mos-1678/sage-4446.
|
||||
|
||||
Pinned8.0.42 acceptance rc0,07:46:54Z–07:47:01Z. Load13.49/14.18/20.53 ->
|
||||
13.77/14.23/20.51. Node and its fixture child observed nice16/policy5. Exact
|
||||
safe output is copied below from404-acceptance.txt; private namespace/temp-root
|
||||
cleanup completed with childexit0. No real provider credential or external
|
||||
upstream request; the made-up OAuth codes never complete sign-in.
|
||||
|
||||
P4 offline upgrade version=8.0.42 nice=16
|
||||
P4 diagnostic {"stage":"binary-start","nodePriority":16,"childPriority":16,"lastClientErrorCode":null,"nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"management-ready","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"store-open","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"broker-bound","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 empty-provider diagnostic {"httpStatus":404,"type":"object","isArray":true,"count":0}
|
||||
proxy.accounts initial empty; GET /credentials + GET /config/api-keys/claude
|
||||
proxy.login.start claude GET /oauth/auth-url; fields=url,state only
|
||||
proxy.login.submit POST /oauth/callback; fake code never ok; GET /oauth/status=wait
|
||||
proxy.login.cancel DELETE /oauth/session confirmed
|
||||
proxy.login.start codex GET /oauth/auth-url; fields=url,state only
|
||||
proxy.login.submit POST /oauth/callback; fake code never ok; GET /oauth/status=wait
|
||||
proxy.login.cancel DELETE /oauth/session confirmed
|
||||
proxy.apikey.add PUT /config/api-keys/claude; account fields=accountId,provider,displayName,status,priority; id from auth_index
|
||||
proxy.priority.set API-key PUT /config/api-keys/claude; priority read-back=7
|
||||
proxy.keys GET /config/access/api-keys; fields=keyId,management,agent,issuedAt,state; sentinel labelled; values absent
|
||||
proxy.priority.set OAuth PATCH /credentials/fields body={name,priority}; priority read-back=8
|
||||
PASS offline native-page bus contract and secret-free receipts
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":null,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":null,"childExitCode":0,"childSignal":null}
|
||||
|
||||
Named mutants rc0 overall,07:47:50Z–07:47:52Z, load9.43/13.06/19.79 at both
|
||||
endpoints. Each mutated whole file ran once, was restored byte-for-byte, then
|
||||
its restored positive control ran once before the next mutant:
|
||||
- accounts-unfiltered-field:2fail, restored control10pass;
|
||||
- callback-skips-state:2fail, restored control17pass;
|
||||
- proxy-route-accepts-granted-user:1fail, restored control11pass;
|
||||
- apikey-logged:1fail, restored control2pass.
|
||||
Runner records nice19 launch observations and original source SHA256s. All4
|
||||
killed, all4 restored, all4 controls green. No raw mutant output/key values
|
||||
were written; canary captures process output. Receipt404-mutants.txt/.start/.end.
|
||||
|
||||
SLOT BACK reported to Sage as poe-330-0030. No unexpected result in this block,
|
||||
no retry, no old assertion or timeout edits, no protected action. This is scoped
|
||||
99-test/upgrade/mutant evidence, NOT the full package/global landing gate. P2
|
||||
landing/rebase/key-record join and Q246 independent review remain outstanding.
|
||||
Final product/test diff and manifest prepared for independent review; no landing
|
||||
or refactor push authorized. No further test/process run after returning slot.
|
||||
|
||||
2026-10-25T07:56Z sage-4473 verified Gladstone exact thread
|
||||
433058fc-9cea-46e3-8554-6ddefb312762 (roster433058fc). Direct planned-seat
|
||||
read-only first Codex review handoff delivered as poe-330-0032 for frozen
|
||||
candidateb47ee2c0ef6580175e8fdd617878c4644861350e. Full24path diff, exact3line404
|
||||
regression,74hashmanifest, scoped99/acceptance/mutant receipts, and P2/globalgate
|
||||
limitations included. Explicitly no duplicate test/proxy run, private/live
|
||||
worktree/transcript access or landing claim. Sage copied as poe-330-0033.
|
||||
This is not a delegated child review round. Filbert landing review and required
|
||||
fresh Claude guard/security review remain separate/pending. No new run or
|
||||
product/test change. Frozen review source/manifest unchanged by this log append.
|
||||
|
||||
## First independent Codex review — Gladstone gladstone-0028
|
||||
|
||||
2026-10-25T07:57:51Z verdict received for frozen
|
||||
b47ee2c0ef6580175e8fdd617878c4644861350e versus78b69e1e6: scoped PASS, no blocking
|
||||
findings in the full24path product/test/docs diff. Reviewer independently
|
||||
verified74/74 hashes from committed blobs and packet diff equality to actual
|
||||
git diff; reviewed brief/design/gates and supplied99/upgrade/mutant receipts.
|
||||
No reviewer test, acceptance, mutant, proxy/provider execution, live credential
|
||||
access or repository writes. Queue next refused the linked worktree; reviewer
|
||||
did not work around it or claim queue/global readiness. Sage already received
|
||||
the detailed verdict under gladstone-0028.
|
||||
|
||||
Owner static check: package diff b47ee2c0e..current HEAD is empty; only tracking
|
||||
notes have advanced. No finding requires a product change, and no new run is
|
||||
launched. This first read-only Codex PASS does NOT waive P2 rebase/key-record
|
||||
join (runtime projection still provisional), required whole package/editor/
|
||||
collateral/global gate checks, Filbert landing review, or required fresh Claude
|
||||
guard/security review. Those remain distinct and pending. No landing authority.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Row330 / P4 review packet — #1828
|
||||
|
||||
Owner Poe; first independent Codex review Gladstone; landing review Filbert.
|
||||
Brief docs/plans/2026-10-24_proxy-page-brief.md (heading17, blob4c005dc4936c).
|
||||
Original base78b69e1e6. Candidate branch poe/row330. No landing authority.
|
||||
|
||||
## Review scope and evidence
|
||||
|
||||
review-product-test.diff is the complete changed packages/ source/test/docs diff
|
||||
from78b69e1e6. review-404-normalization.diff is the exact latest three-line product
|
||||
fix against prepared e7438743f; its tests/helper were committed first there.
|
||||
review-MANIFEST.sha256 pins every changed package file and the current offline
|
||||
acceptance/mutant scripts, diff artifacts, run list and successful scoped receipts.
|
||||
BUILD.md preserves all earlier failures and corrections append-only.
|
||||
|
||||
Current scoped verification: exact intended RED2fail/9pass, then99/99 focused
|
||||
GREEN, pinned8.0.42 offline bus acceptance, four named mutants killed with four
|
||||
restored positive controls (10/17/11/2passes). Whole-file receipts and uptime
|
||||
are404-*.txt/.start/.end. Actual idle scheduling is reported, not overwritten.
|
||||
No test reruns after an unexpected outcome. Earlier fixture-root and missing
|
||||
provider404 findings are separate commits; product normalization is exactly
|
||||
GET /v8/management/config/api-keys/claude HTTP404 ->[], with original404 receipt.
|
||||
Every other endpoint/method/status still refuses, malformed200 null/object still
|
||||
refuses proxy-down; no new refusal code, assertion weakening or timeout change.
|
||||
|
||||
## Security focus
|
||||
|
||||
- Owner human-only broker gate before generic user/tester handling; all8verbs.
|
||||
- Native /proxy and /api/proxy routes through fixed Console caller allowlist;
|
||||
writes require signed-in owner, named host, --send, own Origin and Fetch
|
||||
Metadata. Granted/tester/loopback callers refuse. No webui management client.
|
||||
- API-key input is write-only, promptly cleared, no browser storage; management
|
||||
header only numeric loopback. OAuth pending-state ownership/expiry/finish
|
||||
guards, no wait-to-success shortcut. Unknown errors are sanitized.
|
||||
- Accounts project exactly5public fields. Stable generated display names avoid
|
||||
arbitrary upstream credential strings; ids derive from proxy identifiers,
|
||||
never token/key values. Client keys return ids and management labels only.
|
||||
- Canary checks HTTP responses, structured logs/receipts, captured process output
|
||||
and bus files; fake receives first key once. No real host proxy/credential access.
|
||||
|
||||
## Explicit outstanding work / review limits
|
||||
|
||||
P2 has not yet been rebased/joined into this branch. Runtime key-status projection
|
||||
is provisional until the actual P2 metadata contract lands. Broad package suites
|
||||
(model-proxy/bus/webui, webui requiring editor build), collateral import-derived
|
||||
files and Sage's global landing gate still need authorized execution after that
|
||||
integration. The prior broader inventory is not a current post-P2 manifest.
|
||||
Independent Q246 first/landing verdicts are not claimed. Do not treat this packet
|
||||
or the scoped checks as approval to land, push refactor, deploy or cut over.
|
||||
@@ -0,0 +1,157 @@
|
||||
// Offline upgrade contract: real bus verbs, pinned proxy, no real credentials.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import net from 'node:net';
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { getPriority } from 'node:os';
|
||||
import { initialConfig } from '../../../../packages/model-proxy/src/settings.mjs';
|
||||
import { PIN } from '../../../../packages/model-proxy/src/common.mjs';
|
||||
import { ManagementClient } from '../../../../packages/model-proxy/src/client.mjs';
|
||||
import { ProxyPage } from '../../../../packages/bus/src/proxy-page.mjs';
|
||||
import { Broker } from '../../../../packages/bus/src/broker.mjs';
|
||||
import { Store } from '../../../../packages/bus/src/store.mjs';
|
||||
const [root, binary] = process.argv.slice(2);
|
||||
if (process.env.P4_UPGRADE_ISOLATED !== 'yes' || !root?.startsWith('/mnt/storage/scratch/tmp/')) throw Error('wrapper required');
|
||||
if (createHash('sha256').update(fs.readFileSync(binary)).digest('hex') !== PIN.binary.sha256) throw Error('binary does not match PIN');
|
||||
const reserve = net.createServer(); await new Promise(r => reserve.listen(0, '127.0.0.1', r));
|
||||
const port = reserve.address().port; await new Promise(r => reserve.close(r));
|
||||
if (port === 8317) throw Error('forbidden port');
|
||||
const management = 'p4-management', canary = 'p4-upstream';
|
||||
const config = initialConfig({ dataRoot: root, port, key: management, sentinel: 'msk-fixture-sentinel-x' });
|
||||
config.management['disable-control-panel'] = true; config.observability.logs['logging-to-file'] = false;
|
||||
fs.mkdirSync(path.join(root, 'proxy/auth'), { recursive: true, mode: 0o700 });
|
||||
fs.mkdirSync(path.join(root, 'home'));
|
||||
fs.writeFileSync(path.join(root, 'config.yaml'), JSON.stringify(config), { mode: 0o600 });
|
||||
const child = spawn(binary, ['-config', path.join(root, 'config.yaml'), '-local-model'], {
|
||||
cwd: root, env: { HOME: path.join(root, 'home'), PATH: '/usr/bin:/bin' }, stdio: 'ignore',
|
||||
});
|
||||
// Diagnostic-only (sage-4432): observe the same run conditions, never change
|
||||
// priority or print exception values, fixture credentials or upstream content.
|
||||
let stage = 'binary-start', lastClientErrorCode = null;
|
||||
const clientErrorCodes = ['proxy-client-down', 'proxy-client-response', 'proxy-client-refused',
|
||||
'proxy-client-redirect', 'proxy-client-invalid'];
|
||||
const errorCodes = [...clientErrorCodes, 'ERR_ASSERTION', 'ERR_SQLITE_ERROR', 'SQLITE_ERROR',
|
||||
'proxy-down', 'not-configured', 'human-required', 'invalid-request', 'invalid-business',
|
||||
'priority-invalid', 'priority-unconfirmed', 'priority-write-refused', 'login-state-unknown'];
|
||||
function schedulingPolicy(pid) {
|
||||
try {
|
||||
// Only our Node process or the still-live child we spawned. Never print
|
||||
// raw /proc content; retain just Linux's numeric scheduling-policy field.
|
||||
const policy = fs.readFileSync(`/proc/${pid}/sched`, 'utf8').match(/^policy\s*:\s*(\d+)\s*$/m)?.[1];
|
||||
return policy === undefined ? null : Number(policy);
|
||||
} catch { return null; }
|
||||
}
|
||||
function observe() {
|
||||
let nodePriority = null, childPriority = null, childSchedulingPolicy = null;
|
||||
try { nodePriority = getPriority(); } catch {}
|
||||
if (Number.isInteger(child.pid) && child.exitCode === null && child.signalCode === null) {
|
||||
try { childPriority = getPriority(child.pid); } catch {}
|
||||
childSchedulingPolicy = schedulingPolicy(child.pid);
|
||||
}
|
||||
console.log('P4 diagnostic ' + JSON.stringify({ stage, nodePriority, childPriority, lastClientErrorCode,
|
||||
nodeSchedulingPolicy: schedulingPolicy(process.pid), childSchedulingPolicy,
|
||||
childExitCode: Number.isInteger(child.exitCode) ? child.exitCode : null,
|
||||
childSignal: /^SIG[A-Z0-9]{1,16}$/.test(child.signalCode ?? '') ? child.signalCode : null }));
|
||||
}
|
||||
child.once('exit', observe);
|
||||
let store;
|
||||
const pause = ms => new Promise(r => setTimeout(r, ms));
|
||||
const receipts = [], answers = [];
|
||||
const client = new ManagementClient({ baseUrl: `http://127.0.0.1:${port}`, key: management, receipt: r => receipts.push(r) });
|
||||
try {
|
||||
console.log('P4 offline upgrade version=' + PIN.version + ' nice=' + getPriority());
|
||||
observe();
|
||||
let ready = false;
|
||||
for (let n = 0; n < 100; n++) {
|
||||
try { await client.getCredentials(); ready = true; break; }
|
||||
catch (error) { lastClientErrorCode = clientErrorCodes.includes(error.code) ? error.code : 'unclassified'; await pause(50); }
|
||||
}
|
||||
assert.equal(ready, true, 'fixture listener starts');
|
||||
stage = 'management-ready'; observe();
|
||||
stage = 'store-open';
|
||||
store = new Store(root);
|
||||
observe();
|
||||
const page = new ProxyPage({ client, instanceTag: 'fixture' });
|
||||
const broker = new Broker({ store, proxyPage: (...args) => page.request(...args), businesses: {
|
||||
demo: { id: 'demo', human: 'owner', roles: { coder: { authority: { withinRole: [], crossRole: [] } } }, arbiters: { technical: 'coder', delivery: 'coder' } },
|
||||
} });
|
||||
const cap = broker.bindHuman({ business: 'demo', human: 'owner', via: 'cli', outsideAgent: true });
|
||||
stage = 'broker-bound'; observe();
|
||||
const call = async (verb, args = {}) => { const answer = await broker.request(cap, { verb, args }); answers.push(answer); return answer; };
|
||||
stage = 'initial-accounts'; observe();
|
||||
// Diagnostic-only (sage-4448/4452, mos-1683): one direct empty-provider
|
||||
// read, types/counts only. Never print field names, values or error text.
|
||||
const receiptCount = receipts.length;
|
||||
try {
|
||||
const groups = await client.getUpstreamGroups();
|
||||
const isArray = Array.isArray(groups);
|
||||
console.log('P4 empty-provider diagnostic ' + JSON.stringify({
|
||||
httpStatus: receipts.length === receiptCount + 1 ? receipts.at(-1).status : null,
|
||||
type: groups === null ? 'null' : typeof groups, isArray,
|
||||
count: isArray ? groups.length : groups !== null && typeof groups === 'object' ? Object.keys(groups).length : 0,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.log('P4 empty-provider diagnostic ' + JSON.stringify({
|
||||
httpStatus: receipts.length === receiptCount + 1 ? receipts.at(-1).status : null,
|
||||
code: clientErrorCodes.includes(error.code) ? error.code : 'proxy-client-error',
|
||||
}));
|
||||
}
|
||||
assert.deepEqual((await call('proxy.accounts')).accounts, []);
|
||||
console.log('proxy.accounts initial empty; GET /credentials + GET /config/api-keys/claude');
|
||||
for (const provider of ['claude', 'codex']) {
|
||||
const login = await call('proxy.login.start', { provider });
|
||||
assert.equal(typeof login.url, 'string'); assert.equal(typeof login.state, 'string');
|
||||
console.log('proxy.login.start ' + provider + ' GET /oauth/auth-url; fields=url,state only');
|
||||
const redirect = provider === 'claude' ? 'http://localhost:54545/callback' : 'http://localhost:1455/auth/callback';
|
||||
const submitted = await call('proxy.login.submit', { redirect_url: redirect + '?state=' + encodeURIComponent(login.state) + '&code=p4-fake-code' });
|
||||
assert.notEqual(submitted.status, 'ok');
|
||||
const status = await call('proxy.login.status', { state: login.state });
|
||||
assert.notEqual(status.status, 'ok');
|
||||
console.log('proxy.login.submit POST /oauth/callback; fake code never ok; GET /oauth/status=' + status.status);
|
||||
if (status.status === 'wait') {
|
||||
assert.equal((await call('proxy.login.cancel', { state: login.state })).status, 'cancelled');
|
||||
console.log('proxy.login.cancel DELETE /oauth/session confirmed');
|
||||
}
|
||||
}
|
||||
await call('proxy.apikey.add', { provider: 'z.ai', key: canary }); await pause(200);
|
||||
const accounts = (await call('proxy.accounts')).accounts;
|
||||
assert.equal(accounts.length, 1); assert.equal(accounts[0].provider, 'z.ai');
|
||||
assert.deepEqual(Object.keys(accounts[0]).sort(), ['accountId', 'displayName', 'priority', 'provider', 'status']);
|
||||
console.log('proxy.apikey.add PUT /config/api-keys/claude; account fields=accountId,provider,displayName,status,priority; id from auth_index');
|
||||
await call('proxy.priority.set', { accountId: accounts[0].accountId, priority: 7 });
|
||||
assert.equal((await call('proxy.accounts')).accounts[0].priority, 7);
|
||||
console.log('proxy.priority.set API-key PUT /config/api-keys/claude; priority read-back=7');
|
||||
const keys = await call('proxy.keys');
|
||||
assert.equal(keys.keys.length, 1); assert.equal(keys.keys[0].management, 'sentinel'); assert.match(keys.keys[0].keyId, /^[a-f0-9]{12}$/);
|
||||
console.log('proxy.keys GET /config/access/api-keys; fields=keyId,management,agent,issuedAt,state; sentinel labelled; values absent');
|
||||
// Fixture-only upload exercises the OAuth priority API relied on by the page.
|
||||
const uploaded = await fetch(`http://127.0.0.1:${port}/v8/management/credentials?name=p4-fixture.json`, {
|
||||
method: 'POST', headers: { Authorization: `Bearer ${management}`, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ type: 'claude', access_token: 'p4-access', refresh_token: 'p4-refresh', expired: '2099-01-01T00:00:00Z' }),
|
||||
signal: AbortSignal.timeout(5000), redirect: 'error',
|
||||
});
|
||||
assert.equal(uploaded.status, 200); await uploaded.body?.cancel(); await pause(200);
|
||||
const oauth = (await call('proxy.accounts')).accounts.find(a => a.provider === 'claude');
|
||||
assert.ok(oauth);
|
||||
await call('proxy.priority.set', { accountId: oauth.accountId, priority: 8 });
|
||||
assert.equal((await call('proxy.accounts')).accounts.find(a => a.accountId === oauth.accountId).priority, 8);
|
||||
console.log('proxy.priority.set OAuth PATCH /credentials/fields body={name,priority}; priority read-back=8');
|
||||
for (const value of [canary, management, 'p4-access', 'p4-refresh', 'p4-fake-code']) {
|
||||
assert.equal(JSON.stringify(receipts).includes(value), false, 'secret absent from receipts');
|
||||
assert.equal(JSON.stringify(store.all('SELECT body FROM events')).includes(value), false, 'secret absent from bus events');
|
||||
assert.equal(JSON.stringify(answers).includes(value), false, 'credential/code absent from bus replies');
|
||||
}
|
||||
console.log('PASS offline native-page bus contract and secret-free receipts');
|
||||
} catch (error) {
|
||||
// Never print assertion actual/expected values or a raw upstream exception.
|
||||
observe();
|
||||
const errorClass = ['Error', 'AssertionError', 'ProxyError', 'BusError', 'TypeError', 'RangeError'].includes(error.name) ? error.name : 'Error';
|
||||
console.error('P4 upgrade FAILED ' + JSON.stringify({ stage, errorClass,
|
||||
code: errorCodes.includes(error.code) ? error.code : 'acceptance-failed' }));
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
store?.close(); child.kill('SIGTERM');
|
||||
await Promise.race([new Promise(r => child.once('exit', r)), pause(3000).then(() => child.kill('SIGKILL'))]);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# P4 PIN upgrade acceptance; outside scripts/test-*.sh, requires Sage's slot.
|
||||
set -euo pipefail
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
BINARY=${1:?P1 verified fetched executable required}
|
||||
export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)
|
||||
trap 'rm -rf -- "$TMPDIR"' EXIT
|
||||
timeout -k 10 120 nice -n $((19 - $(nice))) unshare -rn bash -c '
|
||||
set -e
|
||||
ip link set lo up
|
||||
export P4_UPGRADE_ISOLATED=yes
|
||||
exec node "$1/acceptance.mjs" "$TMPDIR" "$2"
|
||||
' bash "$HERE" "$BINARY"
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:22Z
|
||||
01:42:22 up 11 days, 17:02, 2 users, load average: 5.98, 15.98, 16.44
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:18Z
|
||||
01:42:18 up 11 days, 17:02, 2 users, load average: 5.98, 15.98, 16.44
|
||||
@@ -0,0 +1,9 @@
|
||||
P4 offline upgrade version=8.0.42 nice=19
|
||||
P4 diagnostic {"stage":"binary-start","nodePriority":19,"childPriority":19,"lastClientErrorCode":null,"nodeSchedulingPolicy":0,"childSchedulingPolicy":0,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"management-ready","nodePriority":19,"childPriority":19,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":0,"childSchedulingPolicy":0,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"store-open","nodePriority":19,"childPriority":19,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":0,"childSchedulingPolicy":0,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"broker-bound","nodePriority":19,"childPriority":19,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":0,"childSchedulingPolicy":0,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":19,"childPriority":19,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":0,"childSchedulingPolicy":0,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":19,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":0,"childExitCode":null,"childSignal":null}
|
||||
P4 upgrade FAILED {"stage":"initial-accounts","errorClass":"Error","code":"proxy-down"}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":null,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":null,"childExitCode":0,"childSignal":null}
|
||||
@@ -0,0 +1,19 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/L2p5A3
|
||||
✔ accounts have exactly the allowlisted public fields, id from credential identifier (22.174496ms)
|
||||
✔ token-like data in every upstream field never reaches public accounts (4.314601ms)
|
||||
✔ API key account is derived from proxy auth_index, never api-key, and can set priority (9.723941ms)
|
||||
✔ OAuth priority uses top-level name/priority and confirms it through list (3.608642ms)
|
||||
✔ priority-unconfirmed: accepted API-key write with unchanged read-back refuses (3.746827ms)
|
||||
✔ priority-write-refused: OAuth PATCH status 201 (2.247959ms)
|
||||
✔ priority-write-refused: OAuth PATCH status 400 (2.965091ms)
|
||||
✔ priority-invalid: unknown account refuses before any write (1.743249ms)
|
||||
✔ proxy-down: a closed management listener refuses once (1.421441ms)
|
||||
✔ client keys return ids, sentinel/foreign labels and only scoped record metadata (2.327823ms)
|
||||
ℹ tests 10
|
||||
ℹ suites 0
|
||||
ℹ pass 10
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 143.18039
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.53, 16.42, 16.59
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
@@ -0,0 +1,26 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/TEAh1u
|
||||
✔ proxy.login.start: provider-not-allowed (27.220361ms)
|
||||
✔ proxy.apikey.add: provider-not-allowed (0.344999ms)
|
||||
✔ apikey-invalid empty (0.294884ms)
|
||||
✔ apikey-invalid too long (0.274ms)
|
||||
✔ apikey-invalid charset (0.30855ms)
|
||||
✔ callback-url-invalid not URL (1.222458ms)
|
||||
✔ callback-url-invalid oversize (0.413224ms)
|
||||
✔ callback-url-invalid repeated state (0.285131ms)
|
||||
✔ login-state-unknown callback never reaches proxy (0.345734ms)
|
||||
✔ login state is scoped to the human and business (0.467282ms)
|
||||
✔ accepted callback wait is pending; only a status ok completes and retires state (0.843249ms)
|
||||
✔ expiry reports login-timeout once with no management call and retires state (0.267807ms)
|
||||
✔ cancel calls DELETE and retires state (0.258213ms)
|
||||
✔ upstream OAuth error body never crosses bus response (0.231392ms)
|
||||
✔ priority-invalid {"accountId":"bad","priority":1} (0.201431ms)
|
||||
✔ priority-invalid {"accountId":"aaaaaaaaaaaa","priority":1.5} (0.174276ms)
|
||||
✔ priority-invalid {"accountId":"aaaaaaaaaaaa","priority":1001} (0.170986ms)
|
||||
ℹ tests 17
|
||||
ℹ suites 0
|
||||
ℹ pass 17
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 106.766018
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
@@ -0,0 +1,50 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/coQR8v
|
||||
✔ proxy.accounts: owner calls page once with business/human scope (19.314895ms)
|
||||
✔ proxy.accounts: tester human-required (14.382028ms)
|
||||
✔ proxy.accounts: agent human-required (13.366875ms)
|
||||
✔ proxy.accounts: user human-required (12.734936ms)
|
||||
✔ proxy.accounts: not-configured (13.206913ms)
|
||||
✔ proxy.login.start: owner calls page once with business/human scope (12.601593ms)
|
||||
✔ proxy.login.start: tester human-required (12.793022ms)
|
||||
✔ proxy.login.start: agent human-required (12.576573ms)
|
||||
✔ proxy.login.start: user human-required (13.04681ms)
|
||||
✔ proxy.login.start: not-configured (13.419448ms)
|
||||
✔ proxy.login.submit: owner calls page once with business/human scope (12.695382ms)
|
||||
✔ proxy.login.submit: tester human-required (13.507149ms)
|
||||
✔ proxy.login.submit: agent human-required (13.188966ms)
|
||||
✔ proxy.login.submit: user human-required (13.722535ms)
|
||||
✔ proxy.login.submit: not-configured (12.60293ms)
|
||||
✔ proxy.login.status: owner calls page once with business/human scope (12.159557ms)
|
||||
✔ proxy.login.status: tester human-required (12.884978ms)
|
||||
✔ proxy.login.status: agent human-required (13.227723ms)
|
||||
✔ proxy.login.status: user human-required (13.638448ms)
|
||||
✔ proxy.login.status: not-configured (13.288314ms)
|
||||
✔ proxy.login.cancel: owner calls page once with business/human scope (13.68684ms)
|
||||
✔ proxy.login.cancel: tester human-required (14.477894ms)
|
||||
✔ proxy.login.cancel: agent human-required (18.618454ms)
|
||||
✔ proxy.login.cancel: user human-required (15.764118ms)
|
||||
✔ proxy.login.cancel: not-configured (14.155249ms)
|
||||
✔ proxy.apikey.add: owner calls page once with business/human scope (13.346653ms)
|
||||
✔ proxy.apikey.add: tester human-required (14.275591ms)
|
||||
✔ proxy.apikey.add: agent human-required (15.041527ms)
|
||||
✔ proxy.apikey.add: user human-required (13.715987ms)
|
||||
✔ proxy.apikey.add: not-configured (13.608844ms)
|
||||
✔ proxy.priority.set: owner calls page once with business/human scope (12.499478ms)
|
||||
✔ proxy.priority.set: tester human-required (14.103153ms)
|
||||
✔ proxy.priority.set: agent human-required (14.57306ms)
|
||||
✔ proxy.priority.set: user human-required (14.02396ms)
|
||||
✔ proxy.priority.set: not-configured (13.77083ms)
|
||||
✔ proxy.keys: owner calls page once with business/human scope (11.740894ms)
|
||||
✔ proxy.keys: tester human-required (12.726783ms)
|
||||
✔ proxy.keys: agent human-required (14.12361ms)
|
||||
✔ proxy.keys: user human-required (13.283755ms)
|
||||
✔ proxy.keys: not-configured (12.67668ms)
|
||||
✔ API-key canary never enters broker events or unknown error output (13.619581ms)
|
||||
ℹ tests 41
|
||||
ℹ suites 0
|
||||
ℹ pass 41
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 696.597349
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:06Z
|
||||
01:42:06 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/RE2vwS
|
||||
✔ OAuth management calls keep URL and state out of receipts (19.749276ms)
|
||||
✔ upstream key goes only in request body and upstream refusal body is discarded (4.186524ms)
|
||||
✔ redirected management calls are not followed or retried (2.121196ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 98.033185
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:06Z
|
||||
01:42:06 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:06Z
|
||||
01:42:06 up 11 days, 17:01, 2 users, load average: 6.66, 16.61, 16.65
|
||||
@@ -0,0 +1,10 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/UVjb2T
|
||||
✔ Console production module graph cannot import the model-proxy client (70.585338ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 137.462924
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:12Z
|
||||
01:42:12 up 11 days, 17:02, 2 users, load average: 6.53, 16.42, 16.59
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:11Z
|
||||
01:42:11 up 11 days, 17:02, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/WLROJU
|
||||
✔ proxy browser clears write-only key immediately, sends once and renders data as text (603.091893ms)
|
||||
✔ proxy browser OAuth opens safely, callback wait stays pending, status ok completes (2601.033669ms)
|
||||
✔ proxy browser read-only disables account changes (550.580882ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3846.295222
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:11Z
|
||||
01:42:11 up 11 days, 17:02, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:08Z
|
||||
01:42:08 up 11 days, 17:01, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,11 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/kmRLQ1
|
||||
✔ API-key canary reaches fake once and zero responses/logs/receipts/store files (51.811486ms)
|
||||
✔ OAuth URLs, state and code stay out of broker records and management receipts (19.046885ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 240.643186
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:08Z
|
||||
01:42:08 up 11 days, 17:01, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,20 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/X0Vp78
|
||||
✔ owner proxy routes each make exactly one allowlisted call (27.305714ms)
|
||||
✔ proxy write refuses signed-out (2.96232ms)
|
||||
✔ proxy write refuses loopback (1.79702ms)
|
||||
✔ proxy write refuses cross-origin (1.340864ms)
|
||||
✔ proxy write refuses missing Fetch Metadata (1.47209ms)
|
||||
✔ all proxy writes are read-only without --send (3.481474ms)
|
||||
✔ granted user never reaches owner page or any proxy route (4.998721ms)
|
||||
✔ native page offers write-only key input and no panel script (2.07878ms)
|
||||
✔ Console caller rejects every non-proxy verb (0.837371ms)
|
||||
✔ tester socket refuses proxy page, reads and writes before any owner call (7.459873ms)
|
||||
✔ malformed API-key JSON never appears in errors or logs (1.621405ms)
|
||||
ℹ tests 11
|
||||
ℹ suites 0
|
||||
ℹ pass 11
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 221.712154
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T06:42:07Z
|
||||
01:42:07 up 11 days, 17:01, 2 users, load average: 6.53, 16.42, 16.59
|
||||
@@ -0,0 +1,6 @@
|
||||
P4 offline upgrade version=8.0.42 nice=16
|
||||
P4 diagnostic {"stage":"binary-start","nodePriority":16,"childPriority":16,"lastClientErrorCode":null,"nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"management-ready","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"store-open","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 upgrade FAILED {"stage":"store-open","errorClass":"Error","code":"acceptance-failed"}
|
||||
P4 diagnostic {"stage":"store-open","nodePriority":16,"childPriority":null,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":null,"childExitCode":0,"childSignal":null}
|
||||
@@ -0,0 +1 @@
|
||||
01:14:23 up 11 days, 16:34, 2 users, load average: 3.15, 5.41, 9.24
|
||||
@@ -0,0 +1 @@
|
||||
01:14:19 up 11 days, 16:34, 2 users, load average: 2.90, 5.40, 9.26
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:32:30Z
|
||||
02:32:30 up 11 days, 17:52, 2 users, load average: 51.91, 45.30, 29.60
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-25T07:32:25Z
|
||||
02:32:25 up 11 days, 17:52, 2 users, load average: 52.34, 45.27, 29.51
|
||||
@@ -0,0 +1,10 @@
|
||||
P4 offline upgrade version=8.0.42 nice=16
|
||||
P4 diagnostic {"stage":"binary-start","nodePriority":16,"childPriority":16,"lastClientErrorCode":null,"nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"management-ready","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"store-open","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"broker-bound","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 empty-provider diagnostic {"httpStatus":404,"code":"proxy-client-refused"}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":16,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":5,"childExitCode":null,"childSignal":null}
|
||||
P4 upgrade FAILED {"stage":"initial-accounts","errorClass":"Error","code":"proxy-down"}
|
||||
P4 diagnostic {"stage":"initial-accounts","nodePriority":16,"childPriority":null,"lastClientErrorCode":"proxy-client-down","nodeSchedulingPolicy":5,"childSchedulingPolicy":null,"childExitCode":0,"childSignal":null}
|
||||
@@ -0,0 +1,8 @@
|
||||
ed5dfcda6db77921f90f1eb587fb0044d6472fb613ecaa98445ed42bc04d5d46 packages/model-proxy/tests/empty-provider.test.mjs
|
||||
c55cc0bb47cd7716a2c31c665cd2dd426e2c54ab7b629637bdc12089dfeb40ce packages/bus/tests/proxy-page-empty-provider.test.mjs
|
||||
01c18d8d09dac72bed45ce302a88cea449ed22ffde374529cf784bad38adbd72 packages/bus/tests/proxy-page-fixture.mjs
|
||||
aa73c4cd454742f7745900e8481f7425341ed7d236b104589790ed254c8bd69f packages/model-proxy/src/client.mjs
|
||||
14acdb4d3fdaabad508eec7378740b13c175d4bdd0c6e707a673138eff82365b packages/bus/src/proxy-page.mjs
|
||||
b19292cad28bd19427fac932c6150429968e184f4a6f000d948919e76908eb05 agents/poe/work/queue-330/proposed-empty-provider.patch
|
||||
ea7584f8935251baa9c32bf675343e8c8cbe19a45c4e7dc48675e14b4409bb1c agents/poe/work/queue-330/empty-provider-prepared-imports.json
|
||||
5d227ae19d600c2fb0e375673045705c4f5bcc00311493a577108caddb38704a agents/poe/work/queue-330/empty-provider-red-run-list.txt
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"packages/model-proxy/tests/empty-provider.test.mjs": {
|
||||
"staticImports": [
|
||||
"node:test",
|
||||
"node:assert/strict",
|
||||
"node:http",
|
||||
"packages/model-proxy/src/client.mjs"
|
||||
],
|
||||
"existingParsedClosureRoots": [
|
||||
"packages/model-proxy/src/client.mjs"
|
||||
]
|
||||
},
|
||||
"packages/bus/tests/proxy-page-empty-provider.test.mjs": {
|
||||
"staticImports": [
|
||||
"node:test",
|
||||
"node:assert/strict",
|
||||
"packages/bus/tests/proxy-page-fixture.mjs"
|
||||
],
|
||||
"existingParsedClosureRoots": [
|
||||
"packages/bus/tests/proxy-page-fixture.mjs"
|
||||
]
|
||||
},
|
||||
"packages/bus/tests/proxy-page-fixture.mjs": {
|
||||
"staticImports": [
|
||||
"node:http",
|
||||
"packages/model-proxy/src/client.mjs",
|
||||
"packages/bus/src/proxy-page.mjs"
|
||||
],
|
||||
"existingParsedClosureRoots": [
|
||||
"packages/model-proxy/src/client.mjs",
|
||||
"packages/bus/src/proxy-page.mjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
packages/model-proxy/tests/empty-provider.test.mjs
|
||||
packages/bus/tests/proxy-page-empty-provider.test.mjs
|
||||
@@ -0,0 +1,2 @@
|
||||
fetch nice 19
|
||||
{"dir":"/mnt/storage/scratch/tmp/76TDtz/download/opt/cliproxyapi/8.0.42","binary":"/mnt/storage/scratch/tmp/76TDtz/download/opt/cliproxyapi/8.0.42/cli-proxy-api","panel":"/mnt/storage/scratch/tmp/76TDtz/download/opt/cliproxyapi/8.0.42/static/management.html"}
|
||||
@@ -0,0 +1 @@
|
||||
/mnt/storage/scratch/tmp/76TDtz
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
import { build } from '../../../../packages/webui/node_modules/esbuild/lib/main.js';
|
||||
import { writeFileSync } from 'node:fs';
|
||||
const entries = [
|
||||
'packages/model-proxy/tests/page-client.test.mjs',
|
||||
'packages/bus/tests/proxy-page.test.mjs',
|
||||
'packages/bus/tests/proxy-page-state.test.mjs',
|
||||
'packages/bus/tests/proxy-page-accounts.test.mjs',
|
||||
'packages/webui/tests/proxy-page.test.mjs',
|
||||
'packages/webui/tests/proxy-canary.test.mjs',
|
||||
'packages/webui/tests/proxy-browser.test.mjs',
|
||||
'packages/webui/tests/proxy-architecture.test.mjs',
|
||||
];
|
||||
const result = await build({ entryPoints: entries, bundle: true, platform: 'node', format: 'esm', splitting: true,
|
||||
packages: 'external', write: false, outdir: '/unused-p4-focused', metafile: true, logLevel: 'silent' });
|
||||
writeFileSync(new URL('./focused-import-graph.json', import.meta.url), JSON.stringify(result.metafile.inputs, null, 2) + '\n');
|
||||
writeFileSync(new URL('./focused-run-list.txt', import.meta.url), entries.join('\n') + '\n');
|
||||
console.log('Static parsed inputs=' + Object.keys(result.metafile.inputs).length + ' test entries=' + entries.length);
|
||||
@@ -0,0 +1,8 @@
|
||||
packages/model-proxy/tests/page-client.test.mjs
|
||||
packages/bus/tests/proxy-page.test.mjs
|
||||
packages/bus/tests/proxy-page-state.test.mjs
|
||||
packages/bus/tests/proxy-page-accounts.test.mjs
|
||||
packages/webui/tests/proxy-page.test.mjs
|
||||
packages/webui/tests/proxy-canary.test.mjs
|
||||
packages/webui/tests/proxy-browser.test.mjs
|
||||
packages/webui/tests/proxy-architecture.test.mjs
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"fullGraphPath": "/mnt/storage/scratch/tmp/9YZd4m/p4-import-graph.json",
|
||||
"fullGraphSha256": "f91e682e0301d88efedc2a9c61a57c8fd65ace432f50e76e37eb37d0a1c7a396",
|
||||
"parsedInputs": 677,
|
||||
"selected": 280,
|
||||
"mandatory": 199,
|
||||
"imported": 256,
|
||||
"excludedEvenIfImported": "packages/cli/tests/case1-accept.test.mjs",
|
||||
"additional": [
|
||||
"packages/business/tests/case1-business-file.test.mjs",
|
||||
"packages/cli/tests/access-boot.test.mjs",
|
||||
"packages/cli/tests/access.test.mjs",
|
||||
"packages/cli/tests/app.test.mjs",
|
||||
"packages/cli/tests/boot-status.test.mjs",
|
||||
"packages/cli/tests/case1-hostcheck.test.mjs",
|
||||
"packages/cli/tests/case2-install.test.mjs",
|
||||
"packages/cli/tests/case2-persona-import.test.mjs",
|
||||
"packages/cli/tests/case2-unit.test.mjs",
|
||||
"packages/cli/tests/channel-cli.test.mjs",
|
||||
"packages/cli/tests/channel-recovery.test.mjs",
|
||||
"packages/cli/tests/cli.test.mjs",
|
||||
"packages/cli/tests/compactions-launch.test.mjs",
|
||||
"packages/cli/tests/config.test.mjs",
|
||||
"packages/cli/tests/credentials.test.mjs",
|
||||
"packages/cli/tests/federation-acceptance.test.mjs",
|
||||
"packages/cli/tests/follow.test.mjs",
|
||||
"packages/cli/tests/git-socket.test.mjs",
|
||||
"packages/cli/tests/grants-scope.test.mjs",
|
||||
"packages/cli/tests/grants.test.mjs",
|
||||
"packages/cli/tests/host.test.mjs",
|
||||
"packages/cli/tests/hosts.test.mjs",
|
||||
"packages/cli/tests/idle-stop-race.test.mjs",
|
||||
"packages/cli/tests/instances.test.mjs",
|
||||
"packages/cli/tests/launch-account.test.mjs",
|
||||
"packages/cli/tests/launch-limit-checks.test.mjs",
|
||||
"packages/cli/tests/launch-limits.test.mjs",
|
||||
"packages/cli/tests/launch-routes.test.mjs",
|
||||
"packages/cli/tests/launcher.test.mjs",
|
||||
"packages/cli/tests/lifecycle.test.mjs",
|
||||
"packages/cli/tests/limits-tokens.test.mjs",
|
||||
"packages/cli/tests/limits.test.mjs",
|
||||
"packages/cli/tests/mark-read.test.mjs",
|
||||
"packages/cli/tests/model-proxy.test.mjs",
|
||||
"packages/cli/tests/names.test.mjs",
|
||||
"packages/cli/tests/notifier.test.mjs",
|
||||
"packages/cli/tests/optional-tracker.test.mjs",
|
||||
"packages/cli/tests/other-tree.test.mjs",
|
||||
"packages/cli/tests/processing-cli.test.mjs",
|
||||
"packages/cli/tests/project-name-repo.test.mjs",
|
||||
"packages/cli/tests/project.test.mjs",
|
||||
"packages/cli/tests/publish-socket.test.mjs",
|
||||
"packages/cli/tests/reply-limits.test.mjs",
|
||||
"packages/cli/tests/revoke-window.test.mjs",
|
||||
"packages/cli/tests/session-clear.test.mjs",
|
||||
"packages/cli/tests/session-cli.test.mjs",
|
||||
"packages/cli/tests/session-launch.test.mjs",
|
||||
"packages/cli/tests/stack-copy.test.mjs",
|
||||
"packages/cli/tests/stack-instances.test.mjs",
|
||||
"packages/cli/tests/tester-http.test.mjs",
|
||||
"packages/cli/tests/tester-reads.test.mjs",
|
||||
"packages/cli/tests/tester-threads.test.mjs",
|
||||
"packages/cli/tests/tester.test.mjs",
|
||||
"packages/cli/tests/thread-cli.test.mjs",
|
||||
"packages/cli/tests/thread-launch.test.mjs",
|
||||
"packages/cli/tests/thread-revoke.test.mjs",
|
||||
"packages/cli/tests/trackers-boot.test.mjs",
|
||||
"packages/cli/tests/transport.test.mjs",
|
||||
"packages/cli/tests/usage.test.mjs",
|
||||
"packages/cli/tests/verbs.test.mjs",
|
||||
"packages/harness/tests/citation-runner.test.mjs",
|
||||
"packages/harness/tests/claude-session.test.mjs",
|
||||
"packages/harness/tests/creative.test.mjs",
|
||||
"packages/harness/tests/pi-session.test.mjs",
|
||||
"packages/harness/tests/raise-domain.test.mjs",
|
||||
"packages/harness/tests/runner.test.mjs",
|
||||
"packages/harness/tests/skills.test.mjs",
|
||||
"packages/harness/tests/spec-tools.test.mjs",
|
||||
"packages/harness/tests/tools.test.mjs",
|
||||
"packages/processing-control/tests/task.test.mjs",
|
||||
"packages/seat/tests/sandbox.test.mjs",
|
||||
"packages/seat/tests/session.test.mjs",
|
||||
"packages/tasks/tests/adapter.test.mjs",
|
||||
"packages/tasks/tests/comment-restart.test.mjs",
|
||||
"packages/tasks/tests/fields.test.mjs",
|
||||
"packages/tasks/tests/processing.test.mjs",
|
||||
"packages/tasks/tests/shapes.test.mjs",
|
||||
"packages/tasks/tests/startup.test.mjs",
|
||||
"packages/tasks/tests/sync.test.mjs",
|
||||
"packages/tasks/tests/tree.test.mjs",
|
||||
"packages/tasks/tests/verbs.test.mjs"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,39 @@
|
||||
// Parse real static/dynamic literal imports with esbuild, never fixture-text grep.
|
||||
import { build } from '../../../../packages/webui/node_modules/esbuild/lib/main.js';
|
||||
import { readdirSync, writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
function walk(dir) { return readdirSync(dir, { withFileTypes: true }).flatMap(e => e.name === 'node_modules' ? [] : e.isDirectory() ? walk(path.join(dir, e.name)) : e.name.endsWith('.test.mjs') ? [path.join(dir, e.name)] : []); }
|
||||
const tests = walk('packages');
|
||||
const result = await build({ entryPoints: tests, bundle: true, platform: 'node', format: 'esm', splitting: true, packages: 'external',
|
||||
write: false, outdir: '/unused-p4-graph', metafile: true, logLevel: 'silent' });
|
||||
const graph = result.metafile.inputs;
|
||||
const changed = new Set(['packages/model-proxy/src/client.mjs', 'packages/bus/src/broker.mjs', 'packages/bus/src/runtime.mjs',
|
||||
'packages/bus/src/proxy-page.mjs', 'packages/webui/src/cli.mjs', 'packages/webui/src/serve.mjs', 'packages/webui/src/console-bus.mjs', 'packages/webui/src/proxy-http.mjs']);
|
||||
function reaches(file, seen = new Set()) {
|
||||
if (changed.has(file)) return true;
|
||||
if (seen.has(file)) return false; seen.add(file);
|
||||
return (graph[file]?.imports ?? []).some(i => !i.external && reaches(i.path, seen));
|
||||
}
|
||||
const forbidden = 'packages/cli/tests/case1-accept.test.mjs';
|
||||
const mandatory = tests.filter(p => /^packages\/(model-proxy|bus|webui)\/tests\//.test(p));
|
||||
const imported = tests.filter(p => reaches(p));
|
||||
const selected = [...new Set([...mandatory, ...imported])].filter(p => p !== forbidden).sort();
|
||||
const scratch = execFileSync('mktemp', ['-d', '/mnt/storage/scratch/tmp/XXXXXX'], { encoding: 'utf8' }).trim();
|
||||
const fullGraphPath = path.join(scratch, 'p4-import-graph.json'), fullGraph = JSON.stringify(graph, null, 2) + '\n';
|
||||
writeFileSync(fullGraphPath, fullGraph);
|
||||
function witness(file, seen = new Set()) {
|
||||
if (changed.has(file)) return [file];
|
||||
if (seen.has(file)) return null; seen.add(file);
|
||||
for (const i of graph[file]?.imports ?? []) {
|
||||
if (i.external) continue;
|
||||
const rest = witness(i.path, seen); if (rest) return [file, ...rest];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
writeFileSync(new URL('./gate-import-witness.json', import.meta.url), JSON.stringify(Object.fromEntries(selected.map(p => [p, witness(p) ?? ['mandatory suite']])), null, 2) + '\n');
|
||||
writeFileSync(new URL('./gate-run-list.txt', import.meta.url), selected.join('\n') + '\n');
|
||||
console.log(JSON.stringify({ fullGraphPath, fullGraphSha256: createHash('sha256').update(fullGraph).digest('hex'), parsedInputs: Object.keys(graph).length, selected: selected.length, mandatory: mandatory.length,
|
||||
imported: imported.length, excludedEvenIfImported: forbidden,
|
||||
additional: selected.filter(p => !mandatory.includes(p)) }, null, 2));
|
||||
@@ -0,0 +1,280 @@
|
||||
packages/bus/tests/access-revoke.test.mjs
|
||||
packages/bus/tests/account-migration.test.mjs
|
||||
packages/bus/tests/address.test.mjs
|
||||
packages/bus/tests/agents.test.mjs
|
||||
packages/bus/tests/approval-binding.test.mjs
|
||||
packages/bus/tests/broker.test.mjs
|
||||
packages/bus/tests/business.test.mjs
|
||||
packages/bus/tests/channel-proof.test.mjs
|
||||
packages/bus/tests/chief-of-staff.test.mjs
|
||||
packages/bus/tests/citation-availability.test.mjs
|
||||
packages/bus/tests/citation-guard.test.mjs
|
||||
packages/bus/tests/citation-runtime.test.mjs
|
||||
packages/bus/tests/comment-cursors.test.mjs
|
||||
packages/bus/tests/comment-migration.test.mjs
|
||||
packages/bus/tests/console-decide.test.mjs
|
||||
packages/bus/tests/creative.test.mjs
|
||||
packages/bus/tests/credential-claim.test.mjs
|
||||
packages/bus/tests/credential-status.test.mjs
|
||||
packages/bus/tests/credential-store.test.mjs
|
||||
packages/bus/tests/credential-verbs.test.mjs
|
||||
packages/bus/tests/credentials.test.mjs
|
||||
packages/bus/tests/csuite-messaging.test.mjs
|
||||
packages/bus/tests/daily-quota-runtime.test.mjs
|
||||
packages/bus/tests/decision-close.test.mjs
|
||||
packages/bus/tests/decision-route.test.mjs
|
||||
packages/bus/tests/decisions.test.mjs
|
||||
packages/bus/tests/end-launch.test.mjs
|
||||
packages/bus/tests/fire-revokes-grants.test.mjs
|
||||
packages/bus/tests/gitea-grant-docker.test.mjs
|
||||
packages/bus/tests/gitea-grant.test.mjs
|
||||
packages/bus/tests/gitea-migration.test.mjs
|
||||
packages/bus/tests/grant-enroll.test.mjs
|
||||
packages/bus/tests/grant-executor.test.mjs
|
||||
packages/bus/tests/grant-host.test.mjs
|
||||
packages/bus/tests/grant-migration.test.mjs
|
||||
packages/bus/tests/grant-scope-runtime.test.mjs
|
||||
packages/bus/tests/grant-scope.test.mjs
|
||||
packages/bus/tests/hard-waits.test.mjs
|
||||
packages/bus/tests/host-run.test.mjs
|
||||
packages/bus/tests/host-tools.test.mjs
|
||||
packages/bus/tests/human.test.mjs
|
||||
packages/bus/tests/instances-verb.test.mjs
|
||||
packages/bus/tests/interpreter.test.mjs
|
||||
packages/bus/tests/land.test.mjs
|
||||
packages/bus/tests/launch-notice.test.mjs
|
||||
packages/bus/tests/launcher-released.test.mjs
|
||||
packages/bus/tests/library-credential.test.mjs
|
||||
packages/bus/tests/library-lookups.test.mjs
|
||||
packages/bus/tests/library-sync.test.mjs
|
||||
packages/bus/tests/library.test.mjs
|
||||
packages/bus/tests/lifecycle-revoke.test.mjs
|
||||
packages/bus/tests/lifecycle-verbs.test.mjs
|
||||
packages/bus/tests/limit-checks.test.mjs
|
||||
packages/bus/tests/limit-stop.test.mjs
|
||||
packages/bus/tests/limits-runtime.test.mjs
|
||||
packages/bus/tests/limits.test.mjs
|
||||
packages/bus/tests/message-citation.test.mjs
|
||||
packages/bus/tests/message-filter.test.mjs
|
||||
packages/bus/tests/messages.test.mjs
|
||||
packages/bus/tests/messaging.test.mjs
|
||||
packages/bus/tests/model-proxy.test.mjs
|
||||
packages/bus/tests/name-migration.test.mjs
|
||||
packages/bus/tests/names.test.mjs
|
||||
packages/bus/tests/peer-transport.test.mjs
|
||||
packages/bus/tests/peercred.test.mjs
|
||||
packages/bus/tests/plugins-verb.test.mjs
|
||||
packages/bus/tests/principal.test.mjs
|
||||
packages/bus/tests/process.test.mjs
|
||||
packages/bus/tests/processing-runtime.test.mjs
|
||||
packages/bus/tests/processing-timeout.test.mjs
|
||||
packages/bus/tests/processing-web.test.mjs
|
||||
packages/bus/tests/processing.test.mjs
|
||||
packages/bus/tests/project-await.test.mjs
|
||||
packages/bus/tests/project-files.test.mjs
|
||||
packages/bus/tests/project-runtime.test.mjs
|
||||
packages/bus/tests/proxy-page-accounts.test.mjs
|
||||
packages/bus/tests/proxy-page-state.test.mjs
|
||||
packages/bus/tests/proxy-page.test.mjs
|
||||
packages/bus/tests/publish.test.mjs
|
||||
packages/bus/tests/reload.test.mjs
|
||||
packages/bus/tests/replies.test.mjs
|
||||
packages/bus/tests/requirements.test.mjs
|
||||
packages/bus/tests/review.test.mjs
|
||||
packages/bus/tests/revoke-window.test.mjs
|
||||
packages/bus/tests/room-check.test.mjs
|
||||
packages/bus/tests/rooms.test.mjs
|
||||
packages/bus/tests/routes-verb.test.mjs
|
||||
packages/bus/tests/schema.test.mjs
|
||||
packages/bus/tests/session-clear.test.mjs
|
||||
packages/bus/tests/session-compactions.test.mjs
|
||||
packages/bus/tests/sessions.test.mjs
|
||||
packages/bus/tests/single-use.test.mjs
|
||||
packages/bus/tests/spec-migration.test.mjs
|
||||
packages/bus/tests/spec-review.test.mjs
|
||||
packages/bus/tests/spec-socket.test.mjs
|
||||
packages/bus/tests/spec.test.mjs
|
||||
packages/bus/tests/staff-request.test.mjs
|
||||
packages/bus/tests/state.test.mjs
|
||||
packages/bus/tests/store.test.mjs
|
||||
packages/bus/tests/task-page.test.mjs
|
||||
packages/bus/tests/task-read.test.mjs
|
||||
packages/bus/tests/tasks.test.mjs
|
||||
packages/bus/tests/tester-threads.test.mjs
|
||||
packages/bus/tests/tester-writes.test.mjs
|
||||
packages/bus/tests/tester.test.mjs
|
||||
packages/bus/tests/thread-claims.test.mjs
|
||||
packages/bus/tests/thread-migration.test.mjs
|
||||
packages/bus/tests/thread-shares.test.mjs
|
||||
packages/bus/tests/threads.test.mjs
|
||||
packages/bus/tests/transport.test.mjs
|
||||
packages/bus/tests/usage-broker.test.mjs
|
||||
packages/bus/tests/usage-migration.test.mjs
|
||||
packages/bus/tests/usage-runtime.test.mjs
|
||||
packages/bus/tests/usage.test.mjs
|
||||
packages/bus/tests/user-principal.test.mjs
|
||||
packages/bus/tests/viewer.test.mjs
|
||||
packages/bus/tests/web.test.mjs
|
||||
packages/bus/tests/workflow-roles.test.mjs
|
||||
packages/business/tests/case1-business-file.test.mjs
|
||||
packages/cli/tests/access-boot.test.mjs
|
||||
packages/cli/tests/access.test.mjs
|
||||
packages/cli/tests/app.test.mjs
|
||||
packages/cli/tests/boot-status.test.mjs
|
||||
packages/cli/tests/case1-hostcheck.test.mjs
|
||||
packages/cli/tests/case2-install.test.mjs
|
||||
packages/cli/tests/case2-persona-import.test.mjs
|
||||
packages/cli/tests/case2-unit.test.mjs
|
||||
packages/cli/tests/channel-cli.test.mjs
|
||||
packages/cli/tests/channel-recovery.test.mjs
|
||||
packages/cli/tests/cli.test.mjs
|
||||
packages/cli/tests/compactions-launch.test.mjs
|
||||
packages/cli/tests/config.test.mjs
|
||||
packages/cli/tests/credentials.test.mjs
|
||||
packages/cli/tests/federation-acceptance.test.mjs
|
||||
packages/cli/tests/follow.test.mjs
|
||||
packages/cli/tests/git-socket.test.mjs
|
||||
packages/cli/tests/grants-scope.test.mjs
|
||||
packages/cli/tests/grants.test.mjs
|
||||
packages/cli/tests/host.test.mjs
|
||||
packages/cli/tests/hosts.test.mjs
|
||||
packages/cli/tests/idle-stop-race.test.mjs
|
||||
packages/cli/tests/instances.test.mjs
|
||||
packages/cli/tests/launch-account.test.mjs
|
||||
packages/cli/tests/launch-limit-checks.test.mjs
|
||||
packages/cli/tests/launch-limits.test.mjs
|
||||
packages/cli/tests/launch-routes.test.mjs
|
||||
packages/cli/tests/launcher.test.mjs
|
||||
packages/cli/tests/lifecycle.test.mjs
|
||||
packages/cli/tests/limits-tokens.test.mjs
|
||||
packages/cli/tests/limits.test.mjs
|
||||
packages/cli/tests/mark-read.test.mjs
|
||||
packages/cli/tests/model-proxy.test.mjs
|
||||
packages/cli/tests/names.test.mjs
|
||||
packages/cli/tests/notifier.test.mjs
|
||||
packages/cli/tests/optional-tracker.test.mjs
|
||||
packages/cli/tests/other-tree.test.mjs
|
||||
packages/cli/tests/processing-cli.test.mjs
|
||||
packages/cli/tests/project-name-repo.test.mjs
|
||||
packages/cli/tests/project.test.mjs
|
||||
packages/cli/tests/publish-socket.test.mjs
|
||||
packages/cli/tests/reply-limits.test.mjs
|
||||
packages/cli/tests/revoke-window.test.mjs
|
||||
packages/cli/tests/session-clear.test.mjs
|
||||
packages/cli/tests/session-cli.test.mjs
|
||||
packages/cli/tests/session-launch.test.mjs
|
||||
packages/cli/tests/stack-copy.test.mjs
|
||||
packages/cli/tests/stack-instances.test.mjs
|
||||
packages/cli/tests/tester-http.test.mjs
|
||||
packages/cli/tests/tester-reads.test.mjs
|
||||
packages/cli/tests/tester-threads.test.mjs
|
||||
packages/cli/tests/tester.test.mjs
|
||||
packages/cli/tests/thread-cli.test.mjs
|
||||
packages/cli/tests/thread-launch.test.mjs
|
||||
packages/cli/tests/thread-revoke.test.mjs
|
||||
packages/cli/tests/trackers-boot.test.mjs
|
||||
packages/cli/tests/transport.test.mjs
|
||||
packages/cli/tests/usage.test.mjs
|
||||
packages/cli/tests/verbs.test.mjs
|
||||
packages/harness/tests/citation-runner.test.mjs
|
||||
packages/harness/tests/claude-session.test.mjs
|
||||
packages/harness/tests/creative.test.mjs
|
||||
packages/harness/tests/pi-session.test.mjs
|
||||
packages/harness/tests/raise-domain.test.mjs
|
||||
packages/harness/tests/runner.test.mjs
|
||||
packages/harness/tests/skills.test.mjs
|
||||
packages/harness/tests/spec-tools.test.mjs
|
||||
packages/harness/tests/tools.test.mjs
|
||||
packages/model-proxy/tests/client.test.mjs
|
||||
packages/model-proxy/tests/config-unit.test.mjs
|
||||
packages/model-proxy/tests/fetch.test.mjs
|
||||
packages/model-proxy/tests/management-key-encoding.test.mjs
|
||||
packages/model-proxy/tests/page-client.test.mjs
|
||||
packages/model-proxy/tests/runner.test.mjs
|
||||
packages/model-proxy/tests/settings.test.mjs
|
||||
packages/processing-control/tests/task.test.mjs
|
||||
packages/seat/tests/sandbox.test.mjs
|
||||
packages/seat/tests/session.test.mjs
|
||||
packages/tasks/tests/adapter.test.mjs
|
||||
packages/tasks/tests/comment-restart.test.mjs
|
||||
packages/tasks/tests/fields.test.mjs
|
||||
packages/tasks/tests/processing.test.mjs
|
||||
packages/tasks/tests/shapes.test.mjs
|
||||
packages/tasks/tests/startup.test.mjs
|
||||
packages/tasks/tests/sync.test.mjs
|
||||
packages/tasks/tests/tree.test.mjs
|
||||
packages/tasks/tests/verbs.test.mjs
|
||||
packages/webui/tests/agents-browser.test.mjs
|
||||
packages/webui/tests/agents.test.mjs
|
||||
packages/webui/tests/assistant.test.mjs
|
||||
packages/webui/tests/board-held.test.mjs
|
||||
packages/webui/tests/browser-edge.test.mjs
|
||||
packages/webui/tests/browser.test.mjs
|
||||
packages/webui/tests/bus-browser.test.mjs
|
||||
packages/webui/tests/bus.test.mjs
|
||||
packages/webui/tests/channel-chat.test.mjs
|
||||
packages/webui/tests/chat-browser.test.mjs
|
||||
packages/webui/tests/chat-instances.test.mjs
|
||||
packages/webui/tests/chat-replies-browser.test.mjs
|
||||
packages/webui/tests/chat-sessions-access.test.mjs
|
||||
packages/webui/tests/chat.test.mjs
|
||||
packages/webui/tests/conversation.test.mjs
|
||||
packages/webui/tests/decide-browser.test.mjs
|
||||
packages/webui/tests/decide-sso.test.mjs
|
||||
packages/webui/tests/decision-http.test.mjs
|
||||
packages/webui/tests/discord.test.mjs
|
||||
packages/webui/tests/docs-browser.test.mjs
|
||||
packages/webui/tests/editor-browser.test.mjs
|
||||
packages/webui/tests/editor-provenance.test.mjs
|
||||
packages/webui/tests/editor.test.mjs
|
||||
packages/webui/tests/granted-user.test.mjs
|
||||
packages/webui/tests/history-return-flow.test.mjs
|
||||
packages/webui/tests/instances-browser.test.mjs
|
||||
packages/webui/tests/instances.test.mjs
|
||||
packages/webui/tests/library-cli.test.mjs
|
||||
packages/webui/tests/library-routes.test.mjs
|
||||
packages/webui/tests/library-status.test.mjs
|
||||
packages/webui/tests/limits-browser.test.mjs
|
||||
packages/webui/tests/limits-route.test.mjs
|
||||
packages/webui/tests/markdown-preview.test.mjs
|
||||
packages/webui/tests/messages.test.mjs
|
||||
packages/webui/tests/pinned-copy.test.mjs
|
||||
packages/webui/tests/preview.test.mjs
|
||||
packages/webui/tests/processing-browser.test.mjs
|
||||
packages/webui/tests/processing-ui.test.mjs
|
||||
packages/webui/tests/processing.test.mjs
|
||||
packages/webui/tests/project-file.test.mjs
|
||||
packages/webui/tests/project-name-repo.test.mjs
|
||||
packages/webui/tests/proof-cli.test.mjs
|
||||
packages/webui/tests/proof.test.mjs
|
||||
packages/webui/tests/proxy-architecture.test.mjs
|
||||
packages/webui/tests/proxy-browser.test.mjs
|
||||
packages/webui/tests/proxy-canary.test.mjs
|
||||
packages/webui/tests/proxy-page.test.mjs
|
||||
packages/webui/tests/reads.test.mjs
|
||||
packages/webui/tests/relaunch.test.mjs
|
||||
packages/webui/tests/return-flow.test.mjs
|
||||
packages/webui/tests/room-browser.test.mjs
|
||||
packages/webui/tests/room-http.test.mjs
|
||||
packages/webui/tests/routes-browser.test.mjs
|
||||
packages/webui/tests/routes.test.mjs
|
||||
packages/webui/tests/send-browser.test.mjs
|
||||
packages/webui/tests/send.test.mjs
|
||||
packages/webui/tests/serve.test.mjs
|
||||
packages/webui/tests/service.test.mjs
|
||||
packages/webui/tests/settings-form.test.mjs
|
||||
packages/webui/tests/settings-write.test.mjs
|
||||
packages/webui/tests/shell.test.mjs
|
||||
packages/webui/tests/sso-browser.test.mjs
|
||||
packages/webui/tests/sso.test.mjs
|
||||
packages/webui/tests/tester-browser.test.mjs
|
||||
packages/webui/tests/tester-cli.test.mjs
|
||||
packages/webui/tests/tester-listener.test.mjs
|
||||
packages/webui/tests/tester-socket.test.mjs
|
||||
packages/webui/tests/thread-claims-browser.test.mjs
|
||||
packages/webui/tests/thread-claims.test.mjs
|
||||
packages/webui/tests/thread-sharing.test.mjs
|
||||
packages/webui/tests/usage-browser.test.mjs
|
||||
packages/webui/tests/usage.test.mjs
|
||||
packages/webui/tests/user-option-swap.test.mjs
|
||||
packages/webui/tests/views.test.mjs
|
||||
@@ -0,0 +1,19 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/nmbhic
|
||||
✔ accounts have exactly the allowlisted public fields, id from credential identifier (16.726005ms)
|
||||
✔ token-like data in every upstream field never reaches public accounts (3.146433ms)
|
||||
✔ API key account is derived from proxy auth_index, never api-key, and can set priority (7.145341ms)
|
||||
✔ OAuth priority uses top-level name/priority and confirms it through list (2.559503ms)
|
||||
✔ priority-unconfirmed: accepted API-key write with unchanged read-back refuses (2.751129ms)
|
||||
✔ priority-write-refused: OAuth PATCH status 201 (1.645703ms)
|
||||
✔ priority-write-refused: OAuth PATCH status 400 (2.192791ms)
|
||||
✔ priority-invalid: unknown account refuses before any write (1.245093ms)
|
||||
✔ proxy-down: a closed management listener refuses once (1.049636ms)
|
||||
✔ client keys return ids, sentinel/foreign labels and only scoped record metadata (1.589625ms)
|
||||
ℹ tests 10
|
||||
ℹ suites 0
|
||||
ℹ pass 10
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 106.068199
|
||||
@@ -0,0 +1,10 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/zzWg8y
|
||||
✔ Console production module graph cannot import the model-proxy client (260.158583ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 332.064995
|
||||
@@ -0,0 +1,50 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/W3UTF4
|
||||
✔ proxy.accounts: owner calls page once with business/human scope (42.216353ms)
|
||||
✔ proxy.accounts: tester human-required (27.14418ms)
|
||||
✔ proxy.accounts: agent human-required (15.631107ms)
|
||||
✔ proxy.accounts: user human-required (16.036791ms)
|
||||
✔ proxy.accounts: not-configured (15.881775ms)
|
||||
✔ proxy.login.start: owner calls page once with business/human scope (15.676932ms)
|
||||
✔ proxy.login.start: tester human-required (15.957058ms)
|
||||
✔ proxy.login.start: agent human-required (16.769099ms)
|
||||
✔ proxy.login.start: user human-required (15.27276ms)
|
||||
✔ proxy.login.start: not-configured (18.544344ms)
|
||||
✔ proxy.login.submit: owner calls page once with business/human scope (14.653081ms)
|
||||
✔ proxy.login.submit: tester human-required (17.613806ms)
|
||||
✔ proxy.login.submit: agent human-required (19.653375ms)
|
||||
✔ proxy.login.submit: user human-required (21.882116ms)
|
||||
✔ proxy.login.submit: not-configured (18.504561ms)
|
||||
✔ proxy.login.status: owner calls page once with business/human scope (15.65383ms)
|
||||
✔ proxy.login.status: tester human-required (16.405308ms)
|
||||
✔ proxy.login.status: agent human-required (15.696776ms)
|
||||
✔ proxy.login.status: user human-required (18.693813ms)
|
||||
✔ proxy.login.status: not-configured (16.320036ms)
|
||||
✔ proxy.login.cancel: owner calls page once with business/human scope (13.937442ms)
|
||||
✔ proxy.login.cancel: tester human-required (17.41389ms)
|
||||
✔ proxy.login.cancel: agent human-required (17.722047ms)
|
||||
✔ proxy.login.cancel: user human-required (19.611299ms)
|
||||
✔ proxy.login.cancel: not-configured (15.608602ms)
|
||||
✔ proxy.apikey.add: owner calls page once with business/human scope (15.651765ms)
|
||||
✔ proxy.apikey.add: tester human-required (17.09525ms)
|
||||
✔ proxy.apikey.add: agent human-required (15.021593ms)
|
||||
✔ proxy.apikey.add: user human-required (15.257844ms)
|
||||
✔ proxy.apikey.add: not-configured (17.124537ms)
|
||||
✔ proxy.priority.set: owner calls page once with business/human scope (14.876553ms)
|
||||
✔ proxy.priority.set: tester human-required (21.351968ms)
|
||||
✔ proxy.priority.set: agent human-required (16.665676ms)
|
||||
✔ proxy.priority.set: user human-required (19.67346ms)
|
||||
✔ proxy.priority.set: not-configured (15.524648ms)
|
||||
✔ proxy.keys: owner calls page once with business/human scope (16.043305ms)
|
||||
✔ proxy.keys: tester human-required (16.990648ms)
|
||||
✔ proxy.keys: agent human-required (15.927183ms)
|
||||
✔ proxy.keys: user human-required (15.10558ms)
|
||||
✔ proxy.keys: not-configured (19.013178ms)
|
||||
✔ API-key canary never enters broker events or unknown error output (18.503151ms)
|
||||
ℹ tests 41
|
||||
ℹ suites 0
|
||||
ℹ pass 41
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 940.684074
|
||||
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/AwJzc2
|
||||
✔ proxy browser clears write-only key immediately, sends once and renders data as text (752.942588ms)
|
||||
✔ proxy browser OAuth opens safely, callback wait stays pending, status ok completes (2519.227325ms)
|
||||
✔ proxy browser read-only disables account changes (456.893472ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3803.115926
|
||||
@@ -0,0 +1,11 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/weXu2n
|
||||
✔ API-key canary reaches fake once and zero responses/logs/receipts/store files (38.031356ms)
|
||||
✔ OAuth URLs, state and code stay out of broker records and management receipts (17.551154ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 193.476026
|
||||
@@ -0,0 +1,12 @@
|
||||
nice=19 TMPDIR=/mnt/storage/scratch/tmp/67qzFx
|
||||
✔ OAuth management calls keep URL and state out of receipts (16.663673ms)
|
||||
✔ upstream key goes only in request body and upstream refusal body is discarded (3.261477ms)
|
||||
✔ redirected management calls are not followed or retried (1.670589ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 84.19507
|
||||
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user