gizmoduck: T3 federation design r3.1 for #414: R8 isolation gate accepts a separate host or a separate uid, one-host audit (mos-0991), Q1 answer recorded, Q2 settled by row 193 (morgana-0002)

This commit is contained in:
jason.woltje committed 2026-10-20 14:01:43 -05:00
1 parent 940e0e816c
commit a2840a6beb
2 files changed
+53 -23

No files matched your search

+52 -23
View File
@@ -28,7 +28,8 @@ PLAN.md T3 (all in jason-master-restart).
(3.5), provider removal needs a Console restart (3.7), Q1 is with Jason (9), client ids in R8 (7).
- r3 (duckworth-0002): B6, the reply path to a user (3.6 item 7, runner header, R3 test and mutant, R7
asserts a reply). C1, the walk doesn't stop an unsandboxed same-uid agent (3.6). N10, `names` gates
through `#agent` (3.6 item 4).
through `#agent` (3.6 item 4). mos-0991: R8's gate accepts a separate host or a separate uid, and 3.8
checks the federation path for one-host assumptions. Q1 holds Jason's answer. morgana-0002 settles Q2.
- r2.1 (mos-0988): the separate uid is row 190b. R8's gate points at 190b, R7 at row 190 (one uid,
scratch). P19 333 is on the 10-27 path (section 8).
- Mos's rulings in mos-0978 and mos-0984 replace the r1 questions (section 9). The push credential note is
@@ -223,8 +224,8 @@ say so.
Writes go through `mosaic access grant` and `revoke` (row R5), atomically with a revision bump and one
receipt line in `<dataRoot>/state/access-log.jsonl` (append-only, as `activation-log.jsonl` is). The
CLI checks its caller with the human proof, but a direct file write by the instance's uid skips both the
CLI and the receipt. Only the separate uid (R8 gate) keeps other instances' agents from making that
write. Jason may also edit the file by hand.
CLI and the receipt. Only the separate uid or separate host (R8 gate) keeps other instances' agents
from making that write. Jason may also edit the file by hand.
### 3.6 The user principal in the broker
@@ -255,8 +256,8 @@ shell Jason opens for example, can start its own copy of the Console's tree from
any granted user. So can anything that can ptrace or write files as that uid. The walk refuses an agent
in its normal process tree, but an unsandboxed agent on the same uid can get past it by starting the
Console through `systemd-run --user` or by daemonizing so its parent chain ends at pid 1. That is the same
hole the owner proof has (P19 333), not a new one. Other instances can't, because the uid check refuses
them (R8 gate). Federation adds no weaker path than the owner's.
hole the owner proof has (P19 333), not a new one. Other instances can't: on one host the uid check
refuses them, and on separate hosts the hub's bus isn't on their host at all (R8 gate). Federation adds no weaker path than the owner's.
The broker changes, each with an R3 test:
@@ -331,6 +332,27 @@ Removing a provider from `console.sso` is different. config.json is read at star
only after a Console restart. Removing the users from access.json acts at once, so the R5 help text says to
do that first.
### 3.8 One host or two (mos-0991)
Melanie's hub may run on a separate server instead of under a separate uid. The federation path assumes
nothing about sharing a host:
- Nothing crosses between the instances' buses or filesystems. Each Console talks only to its own bus
through `user-cli` or `human-cli`, spawned on its own host. access.json lives in the receiving
instance's config dir. `federation.peers` holds URLs and labels, never a path or a socket. No row reads
a peer's file or socket.
- In the 10-27 slice the two Consoles never call each other. The user's browser reaches each origin, and a
peer link is a plain link. Section 6 does need each Console's origin reachable from the other host, so
R8 checks that now and the later rows add no infra.
- Each instance has its own OIDC client with its own redirect URI on its own origin (3.3). Each Console
must reach its provider's issuer and JWKS from its own host. If the hub uses the factory's Authentik
across hosts, that means over Authentik's public URL.
- Q175 checks token expiry, and section 6 has a 60 s window on signed requests, so both hosts need a synced
clock.
The only one-host pieces are R7, which is scratch on one host by design (row 190), and the uid checks in
R8's gate, which now have a separate-host form.
## 4. Fit with the current code
- The bus stays a local socket. Federation reaches it only through the receiving instance's Console, which
@@ -364,15 +386,16 @@ do that first.
(human.mjs:67). Inside one instance the proofs stay same-uid cooperative (3.6, P19 333).
If row 190b can't deliver separate uids, Mos takes a new decision to Jason then. The design doesn't plan a
single-uid live run.
- T1.1 live also needs two Consoles, two public origins, two Traefik routes and two provider clients,
which are Jason-gated infra.
- Or a separate host (mos-0991). If Jason runs the hub on its own server, that meets R8's isolation gate
instead of 190b, and 190b isn't needed for R8 (3.8).
- T1.1 live also needs two Consoles, two public origins, two reverse-proxy routes and two provider
clients, which are Jason-gated infra.
- T1.2, #412 SSO. Hard dependency. Row R6 builds on Sage's phase 1 candidate (Q177 merge-chaining) and
goes live only after the phase 2 window.
- T1.3, human users and RBAC. Mos's reading (mos-0978 Q2) is one user and grant model for local and
federated humans. Row 193 (Morgana, with Sage) decides the store. This design proposes access.json as
that shared shape: T1.3 adds shared threads and more levels, federation adds `home`. If row 193 picks
another store, R1 becomes an adapter or is dropped, and R3 and R5 rebase onto it. The broker check in
3.6 and the revocation in 3.7 don't depend on where the grants are stored.
- T1.3, human users and RBAC. One user and grant model for local and federated humans (mos-0978 Q2).
Row 193 (Morgana) confirmed access.json as that store (morgana-0002, section 9 Q2). T1.3 adds `local`
users, the viewer level and `thread_shares`, and federation adds `home`. R1 is the shared loader, so row
193 builds on R1's candidate.
## 6. After 10-27: instance-to-instance
@@ -405,7 +428,7 @@ gate. Chains follow Q177: a dependent row builds on its parent's candidate.
| 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 | 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, another user's runs stay |
| 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) |
| 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, Traefik route, access.json written by Jason, re-pins | Jason, Mos | row 190b live (hub under its own Linux user), #412 phase 2, R7 | Entry gate, R8 refuses to start unless all hold: the factory and hub buses run under different uids, a factory-uid human-cli and user-cli call to the hub socket is refused (human.mjs:67), the factory uid can't read the hub data root or config dir (both 0700), and the two provider client ids differ. 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 | row 190b live (hub under its own Linux user) or the hub on its own host, #412 phase 2, R7 | Entry gate, R8 refuses to start unless one isolation form holds. Separate uid: the factory and hub buses run under different uids, a factory-uid human-cli and user-cli call to the hub socket is refused (human.mjs:67), and the factory uid can't read the hub data root or config dir (both 0700). Separate host: the hub's bus socket, data root and config dir don't exist on the factory host, the factory host mounts nothing of the hub's, and Jason confirms no factory agent holds a login or key for the hub host. Both forms also check: 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. 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.
R6 waits on Sage's phase 1 candidate.
@@ -414,19 +437,25 @@ R6 waits on Sage's phase 1 candidate.
The slice lands only if three things hold. #412 phase 1 has a reviewed candidate by about 10-23. P19 333,
the path-bound human proof R3 depends on, is settled in time for R3 (Mos told Sage, mos-0988). Row 190b
is live by about 10-25. R6 and R7 chain on #412 phase 1. R8 needs all three, plus two Jason-gated windows
(#412 phase 2 and the federation re-pin), plus infra work only Jason does: a second public origin, a
Traefik route and a provider client. If row 190b can't deliver separate uids, R8 doesn't run and Mos takes it to Jason.
or a separate hub host is live by about 10-25. R6 and R7 chain on #412 phase 1. R8 needs all three, plus
two Jason-gated windows (#412 phase 2 and the federation re-pin), plus infra work only Jason does: a
second public origin, a proxy route and a provider client, and on a separate host that host itself. If
neither isolation form is ready, R8 doesn't run and Mos takes it to Jason.
## 9. Rulings (mos-0978, mos-0984)
## 9. Rulings (mos-0978, mos-0984, mos-0991)
1. Q1: human cross-access is the 10-27 slice (mos-0978). Duckworth's N8 says moving agent-to-agent past
10-27 narrows Jason's "Federation is configurable", so Mos is putting it to Jason (mos-0984 point 3).
r2 doesn't wait for that answer. If he puts it inside 10-27, rows follow.
2. Q2, open: one user and grant model for local and federated humans is Mos's reading. Row 193 (Morgana,
T1.3) settles the store with Sage.
1. Q1: human cross-access is the 10-27 slice (mos-0978). Duckworth's N8 put agent-to-agent's timing to
Jason through Mos. Jason answered "P2P federation is acceptable for 10-27" (mos-0991). Mos is checking
that P2P means person-to-person, the human-access slice. No agent-to-agent rows unless Mos says so.
2. Q2, settled by row 193 (morgana-0002, to Sage too): access.json is the one user and grant store for
humans. Row 193 adds `home: "local"` users, uses the grant level as the human role, and keeps viewer and
collaborator. The owner stays outside the file. Thread shares go in a new append-only bus table,
`thread_shares`, not in access.json, and a share counts only while the named user's live grant covers
the thread's project and instance. The 10-27 slice ships collaborator only (W2), so when row 193 adds
viewer, R1's level check widens with it.
3. Q3 and B5: a separate uid per instance is a hard precondition of R8 (mos-0984 point 2). Mos won't ask
Jason to accept a single uid, and takes a new decision to him if row 190b can't deliver.
Jason to accept a single uid, and takes a new decision to him if row 190b can't deliver. A separate
host meets the same gate (mos-0991, 3.8).
## 10. Notes
+1
View File
@@ -1361,3 +1361,4 @@ are never rewritten or removed; corrections are new entries.
2026-10-20T18:56:54Z | Gizmoduck (Opus 5.5) | T3 federation design r2 for Vikunja #414, answering Duckworth's B1-B5 and N1-N9 (duckworth-0001) and Mos's mos-0978 and mos-0984 rulings, agents/gizmoduck/work/federation/DESIGN.md | r2 committed and pushed on gizmoduck/federation-design; sent to Duckworth and Mos for re-review; R1, R2, R5 wait for Duckworth's approval before Sage's queue
2026-10-20T18:58:23Z | Gizmoduck (Opus 5.5) | T3 federation design r2.1 for Vikunja #414, mos-0988: R8 uid gate on row 190b, R7 on row 190, P19 333 in the risk | committed and pushed on gizmoduck/federation-design; Duckworth's r2 verdict pending
2026-10-20T19:00:18Z | Gizmoduck (Opus 5.5) | T3 federation design r3 for Vikunja #414, answering duckworth-0002 B6 (user reply path), C1 and N10 | committed and pushed on gizmoduck/federation-design; sent to Duckworth for approval on the diff
2026-10-20T19:01:43Z | Gizmoduck (Opus 5.5) | T3 federation design r3.1 for Vikunja #414, mos-0991 (R8 gate accepts a separate host or uid, one-host audit 3.8, Q1 answer) and morgana-0002 (Q2 settled, access.json is the human store) | committed and pushed on gizmoduck/federation-design; sent to Duckworth with r3