1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
plan: "0039"
title: "A long-lived requester from the CLI: run requester in the foreground with a ready file, install requester as a systemd unit, the gateway composed into it, atirc agents from it, and a mixed-prod acceptance against a user-configured bidder"
status: doing
source: >
2026-11-01 atirc, owner (verbatim): "split quickstart into quickstart-local.md and quickstart-prod.md, then write
a plan for how we are going to address all the Not Supported Today items so that we could bring up a
mixed-prod.yaml assume the user has access to a bidder they will configure (via
examples/infra/vouch-bidder-operator.yaml and examples/infra/vouch.yaml and etc.). There is no reason we cannot
bring up a kcp and apply resources to make a long lived requester from our CLI, we want this requester to be
able to be run in the foreground and report random port bound to written out to file so we can wait for bound
port and kubectl file to access it. We also want to support and document how we would kubectl apply
HostProcess or whatever to have it installed as a systemd unit in slice in sepcified user created if asked for
new user - similar to how we do bidder installs. There's also no reason we couldn't compose the gateway into
that requester so that it can create k8s clusters and run the k3s control plane for them. also for the atirc
agent we have ingress options and should show how to configure them for a local PDS so that that works or
leverage a remote PDS for an existing account. write plan then make it happen and validate, including
acceptance test(s) as needed." Lead: linux-kcp. Plan author: linux-kcp-plan39. The Not Supported items are
the tables at the end of docs/quickstart-local.md and docs/quickstart-prod.md.
principle: >
A long-lived requester is one more application instance: a kcp of its own, the MarketRequester root applied
into it, every listener on port 0, state as kcp objects under one state dir. It is the root that
`run request` composes today, kept up instead of torn down per contract, so `kubectl apply` of Infra,
EphemeralInfra and ComputeContract against it runs the same RFP flow against prod bidders. The default
policies always run (requester-tangled-vouch; bidder-tangled-vouch on the bidders); nothing here sets
`policy: open`. No user record is ever deleted by these paths. A user is created only by the one explicit
root step (install); everything else runs as USER without root.
grounding: >
Read from the code at main 0ac9a0c3.
(1) `run request` (cmd/socialweb-computer/request.go, requestRun.start, standalone branch): bringUp with
profile quick and role devinstance.RoleRequester into requests/<name>, then request.OpenKcp(...).ApplyRoot of a
MarketRequesterSpec, signIn, WaitReady, WriteInvocation; one ComputeContract per invocation; the owner lock
requests/<name>/owner.lock. Standalone defaults (request.go:528-537): network.plcDirectoryUrl
https://plc.directory, network.ingressProxyHost xrpc.fedproxy.com, spec.ingress compose.DefaultIngressConfig().
(2) `run bidder` (cmd/socialweb-computer/bidder.go) is the long-lived model: standalone() bringUp + serve()
(ApplyRoot with retry, signIn through appcli.IdentityRenderer without a deadline under --fg, WaitReady, a JSON
summary on stdout, then block until Ctrl-C and downInstance keeping state); installedCore() is the
--host-backend systemd-user path that bidder.RunCore (internal/stack/bidder/core.go) runs under the unit.
(3) `install bidder` (internal/install/install.go Install): steps account (useradd --system, --adopt-account
for an existing one, install-<account>.json record), subids, kvm, delegation, apparmor, nodePassword,
stateLayout, guestImage, unitFiles (templates/socialweb-computer-bidder@.service: Type=notify, Slice=
socialweb.slice, CONFIG_PATH_SOCIALWEB_COMPUTER=<state>/config.json, ExecStart run bidder --handle %i --fg),
linger, enable, bringUp, checkHost; config() writes config.json (host-backend systemd-user, state-dir,
profile prod-qr, bidder-policy default); roles come from internal/hostcontract (gateway, bidder only) and
internal/bootstrap/table (Gateway(), Bidder()); upgrade.go infers gateway or bidder.
(4) HostProcess (api/v1alpha1/types_hostprocess.go) has unit, exec/argv, env, slice, readiness, placement
supervisor|bootstrap and no account field: it runs in the user manager of the core that places it. HostInstall
(internal/controllers/hostinstall) only observes the install record.
(5) pkg/embedded (plan 0038) runs the requester in process on memory storage and vaultmemory, comms Firehose,
no ingress, a snapshot the app must store encrypted; it is the iOS and wasm shape, not a Linux host shape.
(6) The Gateway root (internal/app/gateway/gateway.go) composes its own Pds, Requester, GatewayAPI and the
cluster provider (ClusterStateDir <state>/clusterprovider, k3s and kine HostProcesses), with
GatewayNodeMarket{HomeWorkspace, ContractWorkspace} (gateway.go:231-240): the NodeClaim controller
(internal/controllers/nodeclaim) writes the cluster's Infra nodes-<hex> into the contract workspace. Profiles
prod and market-network only. The core registers the Infra and NodeClaim controllers for every root
(internal/stack/componentcore/controllers.go:146-152).
(7) atirc mint (internal/market/requester/atirc.go serviceAtircIssuer): fails "the requester identity signs no
service auth" when q.serviceAuth is nil; requester.go:643-650 sets it only for IdentityModeOAuth
(oauthServiceAuth) or, when not LocalSigner, from the owner MarketIdentity (pdsAccount and oauthSessionProxy;
the link marketrequester.go:407). localRepo sets LocalSigner (requester.go:377), so a local-PDS requester never
signs, although the Pds component provides pds.CapabilityServiceAuth (internal/controllers/pds/provide.go:77) and
the bidder already links its guest to it (internal/app/marketbidder/marketbidder.go:634). A localRepo handle is
compose.PdsHandle: <workspace>.requester.local (internal/app/compose/pds.go:56), which nobody resolves.
(8) RequesterOAuthScope (internal/atproto/oauth/scope.go:69) has no repo:sh.tangled.graph.vouch, so vouch.yaml
through an OAuth requester repo is refused by the PDS; OptionalScopes holds only rpc:chat.atirc.revoke.
(9) NetworkApp has no computeKinds (MarketBidderSpec has, types_application.go:669); marketnetwork
children.go:630 builds the MarketBidderSpec from NetworkApp; dev up has no --compute-kinds.
(10) The vouch examples use deletionPolicy Delete (AtprotoRecord default); vouch-bidder-operator.yaml needs the
network's operator repo link (link-records-operator), which an installed bidder does not compose.
(11) Schema revisions now: MarketRequester 37 (38 burned on prod by a reverted build), Requester 29, Gateway 38,
MarketNetwork 31.
decisions:
name_and_base: >
The verb is `socialweb-computer run requester` (beside `run bidder`), on the bringUp path of `run request`
standalone (HostProcess kcp, real OpenBao, ingress), not pkg/embedded: a Linux host has sockets, an ingress
gives getSecrets without the sealed fallback, and OpenBao keeps secrets out of a snapshot file. Owner decision
D1 below.
ready_file: >
`run requester --ready-file <path>` (default <state-dir>/requesters/<name>/ready.json) is written once the
MarketRequester root is Ready, by write to <path>.tmp then rename, mode 0600, and rewritten on every restart
and on a change of any field. JSON {schema: 1, name, pid, startedAt, kcpUrl (the bound front address),
kubeconfig (path of the admin kubeconfig), workspace, did, handle, identityMode, pdsUrl, ingress {pds,
requester} (public URLs from the Exposure status), gateway {workspace} when composed}. No token, key or
password, only paths. Removed on a clean stop. A script waits with `until [ -s ready.json ]`.
install_vs_hostprocess: >
`install requester` is the path that creates a user (the one explicit root step, as install bidder); a
HostProcess manifest is the documented same-account path (no root, no new user) into a core the account
already runs. Both run the same process: `socialweb-computer run requester --fg`. HostProcess has no account
field and cannot create a user, so it cannot be the new-user path. Owner decision D2.
gateway_compose: >
`run requester --with-gateway` applies a Gateway root with a new profile `requester` into the same kcp. The
Gateway gains spec.requesterRef {workspace, name}: with it the root composes no Pds, Requester or identity
of its own, sets GatewayNodeMarket.ContractWorkspace to the MarketRequester's workspace, and the
MarketRequester's Requester rents the nodes under the user's own DID and policy. GatewayAPI is not composed
in profile requester; a Cluster is a kubectl apply. Owner decision D3.
agent_identity: >
OAuth (QR or browser): mints today. Remote PDS existing account (pdsAccount, app password): mints through
the owner MarketIdentity service auth; prove it live. Local PDS (localRepo): mints only when the DID is on
plc.directory, the PDS is public through an ingress, the handle is one the user controls and resolves, and
the Requester links service auth to its own Pds component; a .local handle is refused before the RFP of an
agent contract. Owner decision D4.
state_rules:
- "Read AGENTS.md first. status is one of todo, doing, done, blocked. The lead flips statuses."
- "No Go tests (AGENTS.md, plan 0027). Proof is the build gates and the live runs in done_when; the policy spec tests stay green."
- "Schema changes bump the Revision in api/v1alpha1/resources.go past every burned revision (MarketRequester 38 is burned) and run go generate; generated output is never hand-merged; only one track holds api/ at a time."
- "Live runs against prod bidders and irc.fedfork.com are the lead's (acceptance README rules); task agents prove on their own market network, with a local bidder and a requester of their own."
- "Default policies only. No deletion of user records: every AtprotoRecord in these paths is deletionPolicy Retain."
- "A decision that closes moves into docs/ARCHITECTURE.md (section 14 when the item opens, 15 when it closes)."
open_decisions:
- id: D1
question: "Base the long-lived requester on the run request bringUp (HostProcess kcp, OpenBao, ingress) or on pkg/embedded (in process, memory storage, Firehose only)?"
recommended: "bringUp, verb `run requester`. pkg/embedded stays the app shape (iOS, wasm)."
- id: D2
question: "Systemd install: `install requester` (root, creates the account) or a kubectl-applied HostProcess?"
recommended: "install requester for a new user, mirroring install bidder (default account swc-requester, --account to name one, --adopt-account for an existing one); HostProcess documented for the same account with no root."
- id: D3
question: "Gateway in the requester: a Gateway root that reuses the MarketRequester's Requester (spec.requesterRef), or a second root with its own Requester on the same account through oauthSessionProxy?"
recommended: "spec.requesterRef: one identity, one OAuth session (plan 0030), one policy engine, one ingress; no second sign-in."
- id: D4
question: "Local PDS atirc agent: what must irc.fedfork.com resolve? (ask atirc-dev: does chat.atirc.mint check only the service-auth JWT against the DID document from plc.directory, or also the handle bidirectionally?)"
recommended: "require did:plc on plc.directory, the PDS public through the ingress, and --atproto-handle on a domain the user controls (DNS TXT _atproto or https /.well-known/atproto-did on the PDS ingress public host); refuse a .local handle for spec.agent before the RFP."
- id: D5
question: "vouch-bidder-operator.yaml on a user-configured (installed) bidder: how does the bidder kcp write the operator's repo?"
recommended: "an optional operator sign-in in the bidder kcp: MarketIdentity operator (oauthQr, scope repo:sh.tangled.graph.vouch only) and the AtprotoRepo operator with the same link label as the market network (apps.socialweb.computer/record-repo: operator), so the example applies unchanged; until then the operator writes the record with goat."
- id: D6
question: "The agent bundle of a kubectl-applied contract: who writes the ATIRC_TOKEN placeholder entry?"
recommended: "the Requester itself, when a spec.agent contract's spec.secretsRef path holds no bundle (one entry at agent.envFile, mode 0600), so the page, the iOS app and the CLI need no bao step; the quickstart's bao commands stay valid."
- id: D7
question: "The vouch record through an OAuth requester repo needs repo:sh.tangled.graph.vouch. Add it to RequesterOAuthScope?"
recommended: "yes, as an optional scope (oauth.OptionalScopes), so no identity is forced to sign in again on rollout (status.pendingAction signInAgain ScopeUpgrade instead); typescript-helpers mirrors it; goat stays the fallback."
- id: D8
question: "deletionPolicy of the vouch examples?"
recommended: "Retain everywhere (local examples too): kubectl delete never deletes a user's vouch record."
- id: D9
question: "Sign-in of an installed requester (no TTY)?"
recommended: "oauthQr as the default profile (the pendingAction url from the MarketIdentity status, scripts/oauth-signin.ts), as a prod-qr bidder; an app password goes once from stdin into the requester's root OpenBao (a SecretRef), never into config.json, a flag or a HostProcess argument."
parallel_tracks:
- track: T1-cli-requester
items: [A-RUN-REQUESTER]
owns: "cmd/socialweb-computer/requester.go (new), cmd/socialweb-computer/main.go (verb table rows), internal/stack/request (shared passthrough, ready file, core)"
- track: T2-install
items: [B-INSTALL-REQUESTER, B2-HOSTPROCESS-PATH]
owns: "internal/install, internal/hostcontract, internal/bootstrap/table, internal/checkhostmain, cmd/socialweb-computer/install.go"
note: "starts at once against the fixed verb name; its live proof waits for A-RUN-REQUESTER"
- track: T3-gateway
items: [C-GATEWAY-COMPOSE]
owns: "internal/app/gateway, internal/controllers/nodeclaim, api Gateway type, cmd/socialweb-computer/requestergateway.go (new)"
- track: T4-identity
items: [D-AGENT-IDENTITY, F2-VOUCH-SCOPE]
owns: "internal/market/requester (requester.go service-auth selection, atirc.go, secrets.go), internal/app/marketrequester, internal/atproto/oauth/scope.go"
- track: T5-dev-up
items: [E-DEV-COMPUTE-KINDS]
owns: "api NetworkApp, internal/app/marketnetwork, cmd/socialweb-computer/dev.go"
- track: T6-bidder-operator
items: [F3-BIDDER-OPERATOR-REPO]
owns: "internal/app/marketbidder, internal/app/compose/operator.go"
- track: T7-docs-acceptance
items: [D-AGENT-INGRESS, F-MIXED-PROD]
owns: "docs/quickstart-local.md, docs/quickstart-prod.md, docs/acceptance/0013-mixed-prod.md (new), examples/infra"
- track: api-serialization
note: "T3 (Gateway) and T5 (NetworkApp) both change api/v1alpha1/types_application.go and resources.go: land T5 first (smaller), then T3 rebases and runs scripts/regen-after-rebase.sh; T4 has no schema change unless D6 needs one."
items:
- id: A-RUN-REQUESTER
title: "run requester: a long-lived foreground MarketRequester root in a kcp of its own, a ready file, restart survival, every identity mode"
track: T1-cli-requester
depends_on: []
files:
- cmd/socialweb-computer/requester.go
- cmd/socialweb-computer/main.go
- cmd/socialweb-computer/request.go
- internal/stack/request/request.go
- internal/stack/request/ready.go
- internal/stack/request/ssh.go
- deploy/apps/option-map.yaml
- AGENTS.md
- docs/quickstart-prod.md
work: >
New verb `run requester` with options name, state-dir, ready-file, fg, ready-timeout, network-timeout,
host-backend, the kcp/kine/bao/dumbpipe paths, log-level, and the same passthrough after `--` as `run
request` (identity: --atproto-oauth-qr, --atproto-oauth, --atproto-identity-mode pdsAccount
--atproto-handle --atproto-pds-url with the password from the environment, localRepo with --identity
<name>; comms; ingress; secrets-fallback). Move the MarketRequesterSpec part of requestRun.passthrough into a
function both verbs call (no copy). State dir <state-dir>/requesters/<name> (never requests/, which holds
invocations), one owner flock. Bring up with bringUp (profile quick, role requester), ApplyRoot, sign in
through appcli.IdentityRenderer (no deadline under --fg), WaitReady, write the ready file (decisions
ready_file), print the JSON summary, then block until Ctrl-C and stop the instance keeping state, as
run bidder --fg. A second start on the same state dir adopts the units and reuses the DID, the vault and
every contract. Helpers on the running instance: `run requester env <name>` (KUBECONFIG and the
workspace, like dev env), `run requester ssh <name> --contract <cc> [--transport webrtc]` (the run
request ssh code over any Held contract). No Infra-specific verb: kubectl apply.
done_when: >
As USER, `run requester --name lr1 --state-dir S -- --atproto-oauth-qr` signs in from the pendingAction url
and writes S/requesters/lr1/ready.json; `until [ -s ready.json ]` returns; `kubectl --kubeconfig
$(jq -r .kubeconfig ready.json) get marketrequesters.socialweb.computer app` prints Ready and the DID; the
kcpUrl port is not a fixed port and equals the bound front; `kubectl apply -f examples/infra/vm.yaml` (bidder
pinned) reaches Deployed on a prod bidder and `run requester ssh lr1 --contract infra-quickstart-vm-vm1`
prints quickstart-vm-ok; Ctrl-C, start again: the same DID, the ready file rewritten with the new kcpUrl,
the contract still Held, ssh again works; the same with a pdsAccount identity (app password) and with a
localRepo --identity; no token or password in the ready file, the summary or any log.
risks:
- "bringUp of profile quick is tuned for short invocations (ready timeouts, die-with); a long-lived instance must survive the CLI's terminal closing only under --fg semantics, and nothing must be torn down on a transient kcp error."
- "Two verbs sharing one passthrough: a refactor of request.go is shared with live run request users; keep run request output identical."
- "Held contracts across a restart: the requester adopts by status; prove a release after a restart revokes atirc tokens first (cloudinit.RequestRevokeWait)."
status: done
- id: B-INSTALL-REQUESTER
title: "install requester: account (created when asked), slice, user unit, config.json, linger, upgrade and uninstall, as install bidder"
track: T2-install
depends_on: [A-RUN-REQUESTER]
files:
- internal/hostcontract/hostcontract.go
- internal/checkhostmain
- internal/bootstrap/table/table.go
- internal/install/install.go
- internal/install/bootstrap.go
- internal/install/upgrade.go
- internal/install/uninstall.go
- internal/install/templates/socialweb-computer-requester@.service
- internal/stack/request/core.go
- cmd/socialweb-computer/install.go
- cmd/socialweb-computer/main.go
- cmd/socialweb-computer/requester.go
- docs/quickstart-prod.md
work: >
hostcontract.RoleRequester ("requester"), check-host -role requester (no kvm; subids and delegation only
with --with-gateway). install.DefaultAccount requester -> swc-requester; `--account <name>` creates that
account when no record and no user exists, `--adopt-account` takes an existing one (ErrForeignAccount
otherwise), as today. `--name` (repeatable, like --handle) names each requester instance; unit
socialweb-computer-requester@<name>.service: Type=notify, Slice=socialweb.slice, WorkingDirectory=<state>,
CONFIG_PATH_SOCIALWEB_COMPUTER=<state>/config.json, ExecStart <state>/bin/current/socialweb-computer run
requester --name %i --fg, Restart=always, Delegate=yes. Steps: account, subids and delegation (gateway
only), nodePassword (gateway only), stateLayout, unitFiles, config (host-backend systemd-user, state-dir,
identity mode, handle, ingress, comms, with-gateway; never a password), linger, enable, bringUp (wait for
the ready file), checkHost. `run requester --host-backend systemd-user` runs request.RunCore under the unit
(the bidder.RunCore pattern) and sends READY=1 after the ready file. upgrade and rollback learn the role;
uninstall removes the units, keeps the state unless --purge. Sign-in per D9.
done_when: >
On a test host as root: `install requester --account swc-req-t1 --name me --dry-run` prints the step list;
without --dry-run the account is created (install-swc-req-t1.json records Created), the unit is active in
socialweb.slice of that user, <state>/requesters/me/ready.json appears after the scripted QR sign-in, and
`sudo -u swc-req-t1 kubectl --kubeconfig <ready.kubeconfig> get marketrequesters.socialweb.computer app`
is Ready; a second install is all FOUND; `upgrade --binary <new> --sha256 <sum>` restarts the unit and the
DID and Held contracts survive; reboot keeps it (linger); `uninstall requester --account swc-req-t1`
removes the units and leaves the state.
risks:
- "install is prod-shaped code shared with install bidder and install gateway; a regression there breaks prod upgrades. Prove install bidder --dry-run output is unchanged."
- "upgrade.go infers the role from the record; a requester record must never be read as a bidder."
- "systemd user manager Type=notify through the run requester process: READY=1 must come only after the ready file, or the unit start times out on a slow OAuth sign-in (TimeoutStartSec, as BidderStartGrace)."
status: done
- id: B2-HOSTPROCESS-PATH
title: "Same-account install through a kubectl-applied HostProcess, documented and proven"
track: T2-install
depends_on: [A-RUN-REQUESTER]
files:
- examples/infra/requester-hostprocess.yaml
- docs/quickstart-prod.md
work: >
An example HostProcess (placement supervisor, unit swc-requester-<name>, exec socialweb-computer, argv run
requester --name <name> --state-dir <dir> --fg, slice socialweb.slice, restartPolicy Always, readiness exec
test -s <dir>/requesters/<name>/ready.json) applied into a core the account already runs (an installed
requester or a dev host instance). Prove that no root prunes a HostProcess outside its set (compose.SetWorkloads
renders only set members) and that the core adopts it across its own restart (unit name and InvocationID).
done_when: >
kubectl apply of the example into a running core of the same account places the unit in socialweb.slice,
HostProcess status Running with the ready-file readiness, the ready file names a working kubeconfig; dev
restart of that core leaves the unit adopted unchanged; kubectl delete stops it and keeps its state dir.
risks:
- "If a root prunes foreign HostProcesses, this path needs a label or a set entry; read compose.CheckWorkloads first."
status: done
- id: C-GATEWAY-COMPOSE
title: "The gateway in the requester's kcp: profile requester, spec.requesterRef, Cluster by kubectl, k3s control plane and nodes rented by the same requester"
track: T3-gateway
depends_on: [A-RUN-REQUESTER]
files:
- api/v1alpha1/types_application.go
- api/v1alpha1/resources.go
- internal/app/gateway/gateway.go
- internal/app/gateway/vault.go
- internal/controllers/nodeclaim/controller.go
- internal/controllers/nodeclaim/infra.go
- cmd/socialweb-computer/requestergateway.go
- deploy/sets
- docs/quickstart-prod.md
work: >
GatewaySpec.RequesterRef *commonv1.ObjectRef (Gateway Revision 39) and ProfileGatewayRequester
"requester". With requesterRef the root skips PlanAccount, the Pds, the Requester and GatewayAPI, composes
the cluster provider (ClusterStateDir under the requester's state dir) and GatewayNodeMarket with
HomeWorkspace = the gateway workspace and ContractWorkspace = the requester workspace; the NodeClaim
controller writes nodes-<hex> Infra there, whose ComputeContracts the MarketRequester's Requester runs
with the user's DID and the default policy; vault links for the node join secrets go to the requester's
vault (contracts/*). `run requester --with-gateway` applies the Gateway root into workspace gateway of the
same kcp and adds gateway.workspace to the ready file; `run requester kubeconfig <name> --cluster <c>`
prints the tenant admin kubeconfig path.
done_when: >
run requester --with-gateway (OAuth identity): kubectl apply of a Cluster {account: me, running: true} in the
gateway workspace goes Ready with the k3s control plane as HostProcesses of the requester's core;
`kubectl --kubeconfig $(run requester kubeconfig lr1 --cluster c1) apply -f examples/infra/k8s-cluster.yaml`
makes a NodeClaim, Infra nodes-<hex> appears in the requester workspace, a node rented from the
user-configured prod bidder joins Ready, the pod logs quickstart-k8s-ok; deleting the Deployment scales the
node to zero (about 2 min idle) and the contract is Released; a requester restart keeps the cluster.
risks:
- "k3s in a user namespace needs subids and cgroup delegation on the host: run requester --with-gateway must preflight what install gateway checks (check-host -role gateway subset)."
- "The nodes-<hex> Infra record mirror writes computer.socialweb.infra to the owner repo: needs the repo link from the gateway root to the requester's repo; Retain only."
- "Two roots in one kcp share one core: CheckWorkloads per root; profile requester must not render the gateway set's web app or association-watch."
- "Gateway Revision 39 must not collide with a burned revision on prod; read deploy/apiresourceschema-revisions.txt first."
status: done
- id: D-AGENT-IDENTITY
title: "atirc agents from the long-lived requester: OAuth and remote-PDS identities proven, and a local-PDS identity that signs service auth"
track: T4-identity
depends_on: [A-RUN-REQUESTER]
files:
- internal/market/requester/requester.go
- internal/market/requester/atirc.go
- internal/market/requester/secrets.go
- internal/app/marketrequester/marketrequester.go
- internal/services/requester/wiring.go
- docs/quickstart-prod.md
work: >
OAuth: no code; prove from run requester. pdsAccount: prove the owner MarketIdentity service auth mints
(an app password may call getServiceAuth for chat.atirc.mint) and that revoke works by the token itself.
localRepo: when the identity's DID is on the public PLC (spec.network.plcDirectoryUrl
https://plc.directory) and its Pds component is public (ingress Exposure Ready), the MarketRequester local
path adds compose.ServiceAuthLink(requester, the Pds component, did) and the Requester selects that service
auth (requester.go:643-650: a third case, local Pds service auth, logged requester_service_auth signing
local-pds); otherwise it stays nil. A spec.agent contract whose requester handle ends in .local, or whose
handle does not resolve to its DID, fails before the RFP with one message naming --atproto-handle. Per
D6, a spec.agent contract whose secretsRef path holds no bundle gets the placeholder bundle from the
Requester.
done_when: >
From run requester: OAuth QR identity, kubectl apply of agent-vm.yaml with no bao step logs
requester_atirc_minted, agent-start.yaml makes <label>-<rkey>.<handle> JOIN the owner channel, release
revokes before vm.terminated; the same with a pdsAccount identity on bsky.social; a localRepo identity with
--atproto-handle on a test domain and the PDS behind xrpc.fedproxy.com mints and joins; a localRepo identity
with the default .local handle fails the agent contract before the RFP with the --atproto-handle message,
and a plain VM contract of it still works.
risks:
- "Unknown atirc checks (D4): ask atirc-dev through the lead before coding the local-PDS case."
- "A service-auth path for localRepo also changes what bidders see (submitRfp/submitAccept callbacks may start sending service auth); keep the market callbacks unchanged and use the new source only for the atirc issuer, or prove the bidders accept both."
- "The guest requester R2 and spec.atircParentRef paths share atirc.go; they must keep their parent-bearer issuer."
status: doing
progress: "pdsAccount proven on prod (rc161 CLI, hat.dplc.rip: owner-identity signing, JOIN, revoke before release); OAuth proven (0013 A); localRepo on plc.directory with a public handle built, refused pre-RFP for .local, not yet proven against irc.fedfork.com"
- id: D-AGENT-INGRESS
title: "Document and prove the ingress options a local-PDS requester needs for the agent, and what a remote-PDS one needs"
track: T7-docs-acceptance
depends_on: [A-RUN-REQUESTER, D-AGENT-IDENTITY]
files:
- docs/quickstart-prod.md
- cmd/socialweb-computer/ingressopts.go
- deploy/apps/option-map.yaml
work: >
Two ingresses, two jobs. PDS ingress (spec.ingress; local PDS only): makes the DID document's atproto_pds
endpoint, the handle's /.well-known/atproto-did, the relay crawl and the bidders' record reads reach the
local PDS: `--ingress-xrpc-host xrpc.fedproxy.com` (the did:key ingress relay, the standalone default),
`--ingress-provider opentunnel --ingress-opentunnel-*`, or `--ingress-public-host <host>` behind the user's
own edge. Requester ingress (spec.requesterIngress; every identity): submitBid, submitEvent and getSecrets
(the agent's ATIRC_TOKEN reaches the guest through getSecrets): `--requester-ingress-provider
webrtc|xrpc-subscription|opentunnel|iroh`, `--requester-ingress-webrtc-stun`; comms Firehose has none and
then needs `--secrets-fallback Sealed`. A remote-PDS identity needs only the requester ingress. Add any
option-map rows the requester lacks for these flags; the ready file reports both public URLs.
done_when: >
quickstart-prod.md has a table of the options per identity; a localRepo run requester with the default
xrpc-subscription PDS ingress and one with opentunnel each answer `curl https://<pds public>/xrpc/_health`
and resolve the handle to the DID, and the agent joins (D-AGENT-IDENTITY); a pdsAccount run requester with
requester ingress webrtc gets its bundle by getSecrets (SecretsApplied reason GetSecrets).
risks:
- "xrpc.fedproxy.com is prod infrastructure shared by every bidder; a new load pattern goes to the lead first."
status: done
- id: E-DEV-COMPUTE-KINDS
title: "dev up --compute-kinds vm,deno for the network bidders"
track: T5-dev-up
depends_on: []
files:
- api/v1alpha1/types_application.go
- api/v1alpha1/resources.go
- internal/app/marketnetwork/devup.go
- internal/app/marketnetwork/children.go
- cmd/socialweb-computer/dev.go
- cmd/socialweb-computer/main.go
- cmd/socialweb-computer/bidder.go
- docs/quickstart-local.md
- docs/acceptance/0012-infra-quickstart.md
work: >
NetworkApp.ComputeKinds []string (listType=set, values vm|deno; MarketNetwork Revision 32); BidderVMDefaults
gains ComputeKinds and fill copies it to every bidder app; children.go passes app.ComputeKinds into the
MarketBidderSpec; `dev up --compute-kinds` with the validation of run bidder (one shared parse function, no
copy); docs drop the manual MarketBidder apply.
done_when: >
dev up --bidders 1 --compute-kinds vm,deno: `k root:bidder-1 get marketbidders.socialweb.computer app -o
jsonpath='{.spec.computeKinds}'` prints [vm,deno] with no manual apply and deno-sandbox.yaml reaches
Deployed with quickstart-deno-ok; without the flag it prints [vm] or nothing and a deno contract gets no bid.
risks:
- "A MarketNetwork schema change: every dev network must be brought down and up again after the bump (not a dev restart)."
status: done
- id: F2-VOUCH-SCOPE
title: "vouch.yaml through an OAuth or pdsAccount requester repo: the optional vouch scope and Retain"
track: T4-identity
depends_on: []
files:
- internal/atproto/oauth/scope.go
- typescript-helpers lib/oauth-scope/mod.ts
- examples/infra/vouch.yaml
- examples/infra/vouch-bidder-operator.yaml
work: >
repo:sh.tangled.graph.vouch?action=create&action=update in RequesterOAuthScope and in OptionalScopes (D7), so
a session granted without it stays Ready with pendingAction signInAgain ScopeUpgrade; both vouch examples
get deletionPolicy Retain (D8).
done_when: >
run requester with a fresh OAuth sign-in: vouch.yaml applies and the AtprotoRecord is Applied with
at://<did>/sh.tangled.graph.vouch/<bidder did>; an identity signed in before the change stays Ready with
pendingAction ScopeUpgrade and is not expired on rollout; kubectl delete of the AtprotoRecord leaves the
record in the repo.
risks:
- "An OAuth scope change reaches every OAuth identity on rollout (memory: scope add forces re-sign-in for oauthQr identities): it must land only as optional, announced to the lead before an rc."
status: done
- id: F3-BIDDER-OPERATOR-REPO
title: "vouch-bidder-operator.yaml on an installed bidder: an optional operator sign-in that composes the operator repo"
track: T6-bidder-operator
depends_on: [F2-VOUCH-SCOPE]
files:
- internal/app/marketbidder/marketbidder.go
- internal/app/compose/operator.go
- internal/atproto/oauth/scope.go
- docs/bidder.md
work: >
Per D5: a MarketBidder option (operator-signin) composes a MarketIdentity operator (oauthQr, a scope of
repo:sh.tangled.graph.vouch only), the AtprotoRepo operator and the ComponentLink link-records-operator for
AtprotoRecords labelled apps.socialweb.computer/record-repo: operator, the same names the market network
uses; nothing changes for a bidder without the option.
done_when: >
On a test bidder installed with the option, after the operator's scripted QR sign-in, kubectl apply of
vouch-bidder-operator.yaml in the bidder kcp is Applied with at://<operator did>/sh.tangled.graph.vouch/<requester
did>; the bidder then bids for that requester under bidder-tangled-vouch; a bidder without the option is
unchanged (0006 compat smoke).
risks:
- "A second OAuth session in a prod bidder kcp; the bidder's own oauthQr identity must be untouched (plan 0030)."
status: done
- id: F-MIXED-PROD
title: "examples/infra/mixed-prod.yaml and acceptance 0013: bring it up against a user-configured bidder with the vouch examples, default policy only"
track: T7-docs-acceptance
depends_on: [A-RUN-REQUESTER, D-AGENT-IDENTITY, F2-VOUCH-SCOPE]
files:
- examples/infra/mixed-prod.yaml
- docs/acceptance/0013-mixed-prod.md
- docs/acceptance/README.md
- docs/quickstart-prod.md
work: >
mixed-prod.yaml: EphemeralInfra mixedprod, ttlSeconds 3600, endWhen Complete; vm1 (your key, a cloud-init
marker, bidderDids [REPLACE-BIDDER-DID], tunnel ssh:22 webrtc), sbx1 (computeKind deno, the same bidder pin;
the operator must serve deno), agent1 (agent, secrets entry for the bundle path); no policy field anywhere.
0013: preconditions (a bidder the user configured: install bidder with bidder-tangled-vouch and, for sbx1,
--compute-kinds vm,deno; the requester account; both vouches; an atirc token to watch), steps (run
requester with ready file; wait; vouch.yaml in the requester kcp; the operator vouch by F3 or goat; apply
mixed-prod.yaml; ssh vm1; read sbx1 stdout; agent-start.yaml and the JOIN; with --with-gateway a Cluster and
k8s-cluster.yaml; Ctrl-C and restart mid-run; delete; the same with install requester), expected
observations, pass criteria, cleanup, pitfalls; a row in docs/acceptance/README.md.
done_when: >
`kubectl apply --dry-run=server -f examples/infra/mixed-prod.yaml` passes against a run requester; 0013 is
written in the format of 0012 and linked from the acceptance README and quickstart-prod.md.
risks:
- "The deno contract needs a bidder that opted in; without one sbx1 stays Degraded (no bids); the runbook says so."
status: done
- id: ACC39-MIXED-PROD
title: "Acceptance 0013 passes live against a user-configured prod bidder, default policy only"
track: T7-docs-acceptance
depends_on: [A-RUN-REQUESTER, B-INSTALL-REQUESTER, C-GATEWAY-COMPOSE, D-AGENT-IDENTITY, D-AGENT-INGRESS, E-DEV-COMPUTE-KINDS, F-MIXED-PROD, F2-VOUCH-SCOPE, F3-BIDDER-OPERATOR-REPO]
done_when: >
The lead runs 0013: from run requester (OAuth QR) and from install requester, mixed-prod.yaml reaches Deployed
on the user's bidder with requester-tangled-vouch and bidder-tangled-vouch both run and allowing; vm1 ssh
prints the marker; sbx1 stdout is read; agent1's nick joins and answers; a tenant cluster node joins and the
pod runs; a restart mid-run keeps every Held contract; delete releases everything, tokens revoked before
vm.terminated, the bidder host empty, the vouch records still in both repos; no secret on any terminal or log;
the Not Supported tables of both quickstarts are empty.
status: done
progress: "2026-11-01 prod: A and C on rc159, B on the rc160 CLI (acceptance 0013 Prod Readings)"