~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
101 lines · 5.7 KB Raw History

Acceptance 0005: record association, guest requester R2, cascade release (plan 0036)

Proves an agent that cannot do OAuth can still be a market requester the owner vouches for: alice's VM starts a second requester R2 (own did:plc + PDS in the VM), alice writes a contract-scoped market.association naming R2, bidders allow R2's RFP via that record, R2 rents a nested VM, and releasing alice's contract cascades: R2 releases first, every token in the chain is revoked, the association record is deleted.

  • Plan 0036 item V1 (A4-A8)
  • Last pass: rc128 cascade 2026-10-30 20:34Z (atirc-dev confirmed server-side); rc127 V1 all 3 tokens revoked
flowchart LR
  alice[alice requester<br/>CLI or page] -- RFP / accept --> b1[bidder]
  b1 --> vm1[alice VM<br/>guest agent]
  vm1 -- guest.requesterDid --> alice
  alice -- market.association<br/>contract-scoped, expiresAt --> repoA[(alice repo)]
  vm1 --> r2[R2 requester<br/>own did:plc + PDS]
  r2 -- RFP allowed via association --> b2[bidder]
  b2 --> vm2[R2 nested VM]
  alice -- mint r2-RKEY.alice --> atirc((atirc))
  r2 -- mint vm-RKEY.r2-RKEY.alice --> atirc

Preconditions

ItemNeed
Accountsalice (authority) signed in by QR; R2 is created inside the VM, nothing to prepare
Vouchesbob + john vouch alice; alice vouches bob + john (R2 inherits trust only through the association)
Versionsall bidders on the same rc (>= rc128): a bidder that ignores guestTools can win and the guest has no socialweb-computer
Scopesrequester guest.secretsUpdated c/u, market.associationRequest c/u/d; bidder guest.requesterDid c (re-sign-in after these were added)
atircchild mint below the r2 token (any depth), lease server
Watchershost watchers (both VMs may land on different hosts), atirc-dev for revocations

Steps

SWC=socialweb-computer; R=~/.tmp/acc36; mkdir -p "$R"; chmod 700 "$R"
NAME=cli-v1-$(date +%s)
# 1. outer VM: agent + socialweb-computer in the guest + guest association slot
$SWC run request --state-dir "$R" --name "$NAME" --hold --guest-tools socialweb-computer \
  --associate guest --agent -- --atproto-oauth-qr > "$R/$NAME-request.log" 2>&1 &
# sign alice in (pendingAction url), wait Held + requester_atirc_minted
# 2. zero-command R2: guest.r2Start -> socialweb-r2.service in the guest
$SWC run request r2 "$NAME" --state-dir "$R" --r2-name r2-v1 --agent --hold
# 3. optional: VM agent for the outer VM
$SWC run request agent "$NAME" --state-dir "$R"
# 4. wait: R2 Held on its nested VM, grandchild nick vm-<rkey>.r2-<rkey>.aliceoa joins
# 5. release ONLY the outer contract: cascade must release R2 first
$SWC run request release "$NAME" --state-dir "$R"

Page: Request VM with Associate DID / R2 box (guestTools, fromGuest, secretsRef), then Start R2. Manual fallback in the guest (debug only): socialweb-computer run request --guest-requester ... as the agent user (never root).

Expected observations

Outer requester (alice) log:

msgmeaning
requester_guest_requester_reported / requester_guest_requesterR2 DID arrived via guest.requesterDid
proof at://<R2>/...market.associationRequest/request verifiedpossession proof of the R2 repo
requester_association_writtenat://<alice>/com.publicdomainrelay.temp.market.association/requester_associate-<hash>-<rkey>
requester_atirc_minted subhandle r2-<accept rkey>.aliceoa.bsky.socialchild token into /etc/socialweb-computer/r2-atirc.env
requester_secrets_updated_sentguest.secretsUpdated delivered
requester_guest_requester_release ... r2Releasedcascade on outer release
requester_atirc_revoked x2 (r2 + VM agent), release_revoke left=0grandchild covered by suffix

Guest / R2: socialweb-r2.service: active (started by guest.r2Start), association ref arrived, identity Ready, R2 RFP, Held, in-guest socialweb-r2-release ok.

Timings (V1 rc125): R2 DID reported 17:15:48 -> Verified 17:15:52 -> hat-7 bid on R2 RFP 17:16:09 -> R2 joined channel 17:16:38. Cascade revoke ~32 s after outer release.

Pass criteria

  • Bidder policy allows R2's RFP only via the association (forged self-claim by R2 denied; a DID not named by alice denied)
  • R2 rents a nested VM, grandchild agent joins (3 levels: alice -> r2 -> vm)
  • Outer-only release: R2 contract Released first, outer Released, exit 0
  • revocations.ndjson holds r2, VM agent (grandchild by suffix) before / at termination
  • Association record gone after release (getRecord -> RecordNotFound, checked 3 times)
  • Both hosts empty (watchers)

Cleanup

  • If the cascade failed: release R2 contract first (from the guest or bidder-side delete), then outer
  • Orphan R2 VM: 0007 on its host; token dies by lease
  • Unrevoked token: atirc-dev server-side revoke by subhandle (suffix covers children)
  • R2 runs no atirc sub (owner rule); kill any test sub

Known pitfalls

SymptomCause / fix
guest has no socialweb-computer-- --guest-tools went to spec.options (bare --agent swallowed the next arg, pre rc126); or a pre-rc125 bidder won
R2 run request refuses root, bundle root-onlyrc126 mirrors into /run/socialweb-r2, runs as the agent user
run request r2 rejected by CLImissing from the capabilities registry (rc127); fixed rc128
outer release leaves R2 VM runningno cascade before A8 (rc127)
R2 token alive after releaserevoke ran in a goroutine after Released and the CLI stopped kcp (rc126); fixed rc127
cross-contract clobber / impersonation via authority vouchreview HIGHs fixed: contract-scoped rkey, nonce proof record in the R2 repo
denied accept, no receipt, guest staysbounded denied-accept release (A8)