gizmoduck: T3 federation design r4.5 for #414 (duckworth-0010, mos-1022): user-thread runs can't message the owner but their gated decisions reach the owner's inbox by ruling (B9, sage-1368), R3 tests it reaches the owner and nobody else, R6 tells the user, start order waits for row 193's viewer level (N20), R8 waits on host variables
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# T3 federation design: factory and hub (Vikunja #414, G3, G4, C1.1)
|
||||
|
||||
Author Gizmoduck (Opus 5.5), 2026-10-20, r4.4. Design only, no product code. Reviewer Duckworth (Opus 5.5,
|
||||
Author Gizmoduck (Opus 5.5), 2026-10-20, r4.5. Design only, no product code. Reviewer Duckworth (Opus 5.5,
|
||||
mos-0978), r1 verdict changes B1-B5 (duckworth-0001, REVIEW.md at b402197ff), r2 verdict changes B6 (duckworth-0002,
|
||||
REVIEW-r2.md at 03a663cd7), r3 verdict changes B7 (duckworth-0003, REVIEW-r3.md at 45cbe5896), r4 verdict changes B8 (duckworth-0005, REVIEW-r4.md at
|
||||
4966136d4), r4.1 verdict changes, B8's issuer host (duckworth-0007, REVIEW-r4.1.md at b14f11136). R1, R2 and R5 go to Sage's
|
||||
4966136d4), r4.1 verdict changes, B8's issuer host (duckworth-0007, REVIEW-r4.1.md at b14f11136), r4.4 verdict changes B9 (duckworth-0010, REVIEW-r4.4.md at 4cf3ce232). R1, R2 and R5 go to Sage's
|
||||
queue once Duckworth approves, and Sage assigns owners. Code lines are at origin/refactor 9ae8c73ac. The
|
||||
Q175 SSO design is cited at Sage's local commit 817e8f51b (agents/sage/work/q175-sso/DESIGN.md), which was
|
||||
not on origin when this was written.
|
||||
@@ -40,6 +40,11 @@ PLAN.md T3 (all in jason-master-restart).
|
||||
#419, section 8). N11, `#waiting` returns null for `to` `user` (3.6 item 7, R3).
|
||||
N12, R3 assumes nothing about how shares end (section 9 Q2). Row 196 (#1694, Gander) owns the shared
|
||||
principal verifier before R3 (section 5).
|
||||
- r4.5 (duckworth-0010): B9, a user-thread run can't message the owner, but its decisions do reach the
|
||||
owner's inbox, as Sage ruled for row 193 (sage-1368, morgana-0008)
|
||||
and Mos kept (mos-1022). 3.6 item 7 says so, R6 tells
|
||||
the user, and R3 tests both sides. N20, the start-order line waits for row 193's viewer level. R8
|
||||
can't start while any host variable is unresolved.
|
||||
- r4.4 (mos-1020, mos-1019): N17, R4 is chained after row 193's viewer level and keeps the level-loss
|
||||
test, which has to kill its mutant before R4 is done.
|
||||
- r4.3 (duckworth-0007): B8, an issuer that isn't on the hub host must pass the host-access, hypervisor
|
||||
@@ -343,8 +348,13 @@ The broker changes, each with an R3 test:
|
||||
|
||||
This is the tester rule at :1852-1857, narrowed to one user and one thread. A `#userThread(s)` beside
|
||||
`#testerThread` (:631) finds a run of a user's thread. Such a run can't send `to: 'human'`
|
||||
(`user-thread-human`), as :1864-1866 does for the tester, so a collaborator can't reach the owner's
|
||||
inbox through the instance. `#checkMessaging` reads `user` as `human` for that run, as :1867 does for
|
||||
(`user-thread-human`), as :1864-1866 does for the tester, so a collaborator can't message the owner.
|
||||
Decisions are different (B9). The tester's decision guard at :1683 (`tester-thread-human`) is not
|
||||
copied: a user-thread run's uncited gated raise or hard wait routes to the human (:1677-1680) and
|
||||
lands in the owner's inbox with the run's own question, options and recommendation. Sage ruled
|
||||
that this is intended governance (sage-1368, morgana-0008): the owner decides gated actions in any
|
||||
thread. Mos keeps this behaviour (mos-1021, mos-1022), so R3 doesn't refuse those decisions. R6 tells
|
||||
the user that a run in their thread can raise a decision to the instance owner. `#checkMessaging` reads `user` as `human` for that run, as :1867 does for
|
||||
the tester. A human or tester session sending `to: 'user'` fails `unknown-role`. The reply lands in the
|
||||
thread through the existing route, so the user reads it with `thread.messages`.
|
||||
`#waiting` (broker.mjs:2556) returns null at once for `to` `user`, as it does for `human` and `tester`.
|
||||
@@ -501,13 +511,13 @@ gate. Chains follow Q177: a dependent row builds on its parent's candidate.
|
||||
| R1 | `packages/business/src/access.mjs`: access.json loader and validator. Providers, peers and businesses come in as arguments. One read plus the file checks | Codex Sol | none | business tests: every rule in 3.5 refuses with the key path named, symlink, non-regular file, other owner, group or other write, expiry, `*`, disjoint from the given subjects, `human`/`tester` ids, a level other than `collaborator`, a project entry that isn't a UUID. `covers`: `*` and listed ids match, an unlisted project, instance or business doesn't, an expired grant doesn't, a level filter rejects another level |
|
||||
| R2 | `federation` key in scripts/mosaic-config.mjs | Haiku 5.5 high | Sage #412 phase 1 candidate | test-config.sh: valid key loads, unknown sub-key and bad origin refuse, config without it loads |
|
||||
| R5 | `mosaic access list, grant, revoke`: human proof, atomic write, revision bump, receipt line. Help text states the `instances` limit and the provider-removal order | Haiku 5.5 high | R1 | cli tests: tester and agent callers refused, concurrent write refuses on revision conflict, receipt appended, bad grant refused before write |
|
||||
| R3 | Broker user principal: consumes row 196's `verifyPrincipal` (`user` kind, `user-cli.mjs`), `bindUser`, the 3.6 changes, the collaborator verb list and its filters, per-request grant read, audit lines for every `s.human`/`s.tester` branch | Opus 5.5 | R1, row 196 | bus tests: each listed verb allowed, every other verb refused, a leak test per listed read (other projects, other instances, other users' threads), a reply-chain `message.send` into another user's thread refused, `thread.send` writes no `human.input`, the run's reply to a user's message reaches that user's `thread.messages`, a reply `to: 'user'` without a matching `in_reply_to` refused, a user-thread run sending `to: 'human'` refused, a reply to a user returns `waiting` null and posts no notice message, revoked and expired grant refused on next request, refusals recorded. Mutants (the agent-parent, non-Console-parent and kind-confusion mutants are row 196's): the grant read skipped, a read filter dropped, `#threadFor`'s user refusal removed, the reply-to check dropped (a run answers another user's message or one outside its thread), a user named like the human accepted |
|
||||
| R3 | Broker user principal: consumes row 196's `verifyPrincipal` (`user` kind, `user-cli.mjs`), `bindUser`, the 3.6 changes, the collaborator verb list and its filters, per-request grant read, audit lines for every `s.human`/`s.tester` branch | Opus 5.5 | R1, row 196 | bus tests: each listed verb allowed, every other verb refused, a leak test per listed read (other projects, other instances, other users' threads), a reply-chain `message.send` into another user's thread refused, `thread.send` writes no `human.input`, the run's reply to a user's message reaches that user's `thread.messages`, a reply `to: 'user'` without a matching `in_reply_to` refused, a user-thread run sending `to: 'human'` refused, a reply to a user returns `waiting` null and posts no notice message, a user-thread run's uncited gated raise reaches the owner's inbox and no other inbox or role, while its `message.send` `to: 'human'` is still refused (B9, mos-1022), revoked and expired grant refused on next request, refusals recorded. Mutants (the agent-parent, non-Console-parent and kind-confusion mutants are row 196's): the grant read skipped, a read filter dropped, `#threadFor`'s user refusal removed, the reply-to check dropped (a run answers another user's message or one outside its thread), a user named like the human accepted |
|
||||
| R4 | Revocation: 30 s timer, `access.reload` verb, claim end on loss, `threadsState` `revoked`, launcher skip and SIGTERM, launch check refusal, R5 calls `access.reload` | Codex Sol | R3, R5, row 193's viewer level (mos-1019) | bus and launcher tests, each with a mutant: a hand edit removing the grant ends the run within one timer period, an expiry with no request ends it, a pending message followed by a launcher restart starts no run, a running revoked thread gets SIGTERM, a grant downgraded to viewer counts as a loss, another user's runs stay. R4 starts after row 193's viewer level lands, because before it R1 refuses any other level, the whole file fails, and the downgrade test can't kill its mutant (N17, mos-1020) |
|
||||
| R6 | Console: granted-user sign-in, `user-cli` transport per session, per-request grant re-read, owner-only controls hidden, peer links | Opus 5.5 | Sage #412 phase 1, R1, R2, R3 | webui tests on Sage's fake OP: granted user signs in, ungranted `sub` refused, revoked mid-session gets 401 on next call, a granted user's owner-verb call refused by the bus even with the UI check removed (mutant) |
|
||||
| R6 | Console: granted-user sign-in, `user-cli` transport per session, per-request grant re-read, owner-only controls hidden, a notice on the user's thread page that a run there can raise a decision to the instance owner (B9), peer links | Opus 5.5 | Sage #412 phase 1, R1, R2, R3 | webui tests on Sage's fake OP: granted user signs in, ungranted `sub` refused, revoked mid-session gets 401 on next call, a granted user's owner-verb call refused by the bus even with the UI check removed (mutant) |
|
||||
| R7 | Scratch two-instance acceptance: two configs, two data roots, two Consoles, one fake OP. The Consoles start outside agent ancestry (a transient user unit), as the user proof requires. They never use the live stack's units or ports | Haiku 5.5 high | R1-R6, row 190 (several instances on one uid) | script test: Jason granted collaborator on one hub project signs in to the hub, makes a thread, sends to the granted instance, gets the instance's reply in the thread, is refused on another project and on board reads, is revoked and gets refused with the run ended. The factory shows nothing of the hub |
|
||||
| R8 | Live window, not a code row: hub provider client, hub proxy route, access.json written by Jason, re-pins | Jason, Mos | the hub host live (Vikunja #418), Jason's #419 answer, #412 phase 2, R7 | Entry gate, host form only for 10-27 (mos-0996): the hub runs on its own host (#418), not on dragon-lin or Melanie's server. R8 refuses to start unless every check passes. The privilege checks run on every factory host in section 8's list (the dragon-lin dev uid, `<case1-host>` and its agents, W-jarvis), as each factory uid there under `env -i`, and each passes only when the call fails: `sudo -n true`, `doas -n true`, and `ssh -o BatchMode=yes <hub-host> true` as the hub user, as Jason's user and as root, each with and without the session's `SSH_AUTH_SOCK`. If `sudo -n` succeeds, as it does for jwoltje on dragon-lin (mos-0996 point 1), the factory counts as root on its own host. Then, as root through `sudo -n`: fingerprint every private key under every home and `/root`, and every key loaded in any agent socket found on the host (`ssh-add -l` per socket). Compare them with `ssh-keygen -lf` over every `authorized_keys` on the hub host, root's included, and no fingerprint matches. Run the ssh checks as root, once with no agent socket and once with each socket found, and each fails (N13). Every other privilege path is a named item in section 8's table (mos-1009): hypervisor, backups, sign-in provider, TLS front end, certificate authority, DNS and network. R8 refuses to start until each one is either closed, with its check passing, or accepted by Jason in writing with the table's wording, which also goes to Melanie (#419). The hub host's bus socket, data root and config dir don't exist on any factory host, no factory host mounts anything of the hub's, and the hub host runs no other instance or agent. Also: the two provider client ids differ, each origin answers over HTTPS from the other host, each Console reaches its issuer's discovery URL, and both clocks are synced. Jason's attestation confirms no factory agent holds a login or key for the hub host, but the failed calls are the gate. The uid form (row 190b) is a later option, outside this gate. Then Jason signs in to the hub from his account and sends one message, Melanie revokes, and his next request is refused |
|
||||
| R8 | Live window, not a code row: hub provider client, hub proxy route, access.json written by Jason, re-pins | Jason, Mos | the hub host live (Vikunja #418), Jason's #419 answer, #412 phase 2, R7 | Entry gate, host form only for 10-27 (mos-0996): the hub runs on its own host (#418), not on dragon-lin or Melanie's server. R8 refuses to start unless every check passes, and while any host variable (`<case1-host>`, `<case2-host>`, `<wjarvis-host>`) is unresolved (duckworth-0010). The privilege checks run on every factory host in section 8's list (the dragon-lin dev uid, `<case1-host>` and its agents, W-jarvis), as each factory uid there under `env -i`, and each passes only when the call fails: `sudo -n true`, `doas -n true`, and `ssh -o BatchMode=yes <hub-host> true` as the hub user, as Jason's user and as root, each with and without the session's `SSH_AUTH_SOCK`. If `sudo -n` succeeds, as it does for jwoltje on dragon-lin (mos-0996 point 1), the factory counts as root on its own host. Then, as root through `sudo -n`: fingerprint every private key under every home and `/root`, and every key loaded in any agent socket found on the host (`ssh-add -l` per socket). Compare them with `ssh-keygen -lf` over every `authorized_keys` on the hub host, root's included, and no fingerprint matches. Run the ssh checks as root, once with no agent socket and once with each socket found, and each fails (N13). Every other privilege path is a named item in section 8's table (mos-1009): hypervisor, backups, sign-in provider, TLS front end, certificate authority, DNS and network. R8 refuses to start until each one is either closed, with its check passing, or accepted by Jason in writing with the table's wording, which also goes to Melanie (#419). The hub host's bus socket, data root and config dir don't exist on any factory host, no factory host mounts anything of the hub's, and the hub host runs no other instance or agent. Also: the two provider client ids differ, each origin answers over HTTPS from the other host, each Console reaches its issuer's discovery URL, and both clocks are synced. Jason's attestation confirms no factory agent holds a login or key for the hub host, but the failed calls are the gate. The uid form (row 190b) is a later option, outside this gate. Then Jason signs in to the hub from his account and sends one message, Melanie revokes, and his next request is refused |
|
||||
|
||||
R1, R2 and R5 can start now and run in parallel. R3 starts when R1 has a candidate, R4 when R3 and R5 do.
|
||||
R1, R2 and R5 can start now and run in parallel. R3 starts when R1 has a candidate, R4 when R3, R5 and row 193's viewer level do (mos-1019).
|
||||
R6 waits on Sage's phase 1 candidate.
|
||||
|
||||
## 8. Risk to 10-27
|
||||
|
||||
@@ -1368,3 +1368,4 @@ are never rewritten or removed; corrections are new entries.
|
||||
| 2026-10-20T19:16:08Z | Gizmoduck | T3 federation design r4.2 for #414 (mos-1013, morgana-0007) | factory means every principal outside the hub instance, hosts as variables, Q180 settles Q1 |
|
||||
| 2026-10-20T19:16:56Z | Gizmoduck | T3 federation design r4.3 for #414 (duckworth-0007) | B8 issuer host passes host-access, hypervisor and backups rows, N16-N19 |
|
||||
| 2026-10-20T19:17:11Z | Gizmoduck | T3 federation design r4.4 for #414 (mos-1020) | R4 chained after row 193's viewer level, keeps the level-loss test (N17) |
|
||||
| 2026-10-20T19:20:55Z | Gizmoduck | T3 federation design r4.5 for #414 (duckworth-0010, mos-1022) | B9 user-thread decisions reach the owner's inbox by ruling, R3 tests it, N20 start order, R8 waits on host variables |
|
||||
|
||||
Reference in New Issue
Block a user