catalog: integration-catalog plan: 0026-abc-control-plane W0-TESTS purpose: > The behavior that the Go integration suite (test/integration, build tag integration), the soak program (test/soak) and the wire-level unit tests asserted before plan 0026 W0-TESTS deleted every Go test. Plan 0027 writes specification tests from this file. Each entry names actors, inputs, the observable outcome and the cleanup expectation, not how the code did it. The deleted sources are in git history at the parent of the W0-TESTS deletion commit. kinds: integration: a Go test function in test/integration (its own or a shared market network, or in-process fakes) fixture: a shared fake or check in test/integration with no Test function of its own soak: a verb or lane of the soak program in test/soak wire: a deleted unit test that asserted an externally observable wire rule; terse acceptance: a row of the plan 0026 acceptance matrix, proven live on the named hosts policyspec: > a policy engine specification test (owner-authorized 2026-10-30, ahead of plan 0027): go test in process, the real RunReconciler, engine and bundled gha-lite actions in the pinned deno, the real trust resolver over a fake PDS, no network; every case asserts the selected policy ran for the decision and that its verdict and the decision are right harness: network: > A market network is brought up with `socialweb-computer dev up --instance i<6 hex> --profile market-network --bidders N [--with-gateway] --guest-image-source --vm-host-backend systemd-user --app-values .bob.test> --ready-timeout 90s --network-timeout 4m|5m|10m --state-dir `. dev up must exit 0 and print a report whose MarketNetwork status.phase is Ready. Every listener binds port 0. A tenant cluster front socket path must fit in 107 bytes, so the run root is short (default /var/tmp/swcit). Each network takes a host live slot first (SOCIALWEB_IT_LIVE_SLOT; up to 30m wait, heartbeat every 10m). Shared networks: one 1-bidder network without gateway (policy tests) and one 2-bidder network with gateway (scale-to-zero and subscribeClusters tests), each brought up once per package run. fail_fast: > While a network is up, `dev status` is read every 3s and every watched command is stopped at once when: devsup becomes unreachable after it was reachable; the MarketNetwork phase is Failed; VerifierPassed=False with reason Probe lasts 3m or more; any child application is Failed; or any ComputeVM is Failed. teardown: > Always, also on failure. On failure only, first save the `dev status` JSON as status-on-failure.json and copy core logs, guest console logs and VMM logs into the test log dir. Then `socialweb-computer dev down --instance --state-dir ` (6m limit). Its report must show no remaining processes, no VMs left and removedState=true. Leak check, any hit is a failure: the state dir still exists; a VM work dir exists under handles/*/vms/*; a process names the state dir in its cmdline or environ; a systemd --user unit or the slice of a bidder handle is still listed; a socialweb-b.slice cgroup of a bidder handle exists. Then the per-network fixture dir is removed and the live slot released. Artifacts are kept. account: > Every command runs as the VM account (SOCIALWEB_IT_ACCOUNT, default test-0001; via `sudo -n -u ` when the caller differs) with HOME, XDG_RUNTIME_DIR=/run/user/, the user DBUS bus, PATH=/usr/local/bin:/usr/bin:/bin, TMPDIR=/tmp, LANG=C.UTF-8 and SOCIALWEB_CLUSTERPROVIDER_SCALE_HOLD=10s. pod_probe: > The tenant probe pod used by several tests: busybox 1.36.1 pinned by digest, sleep 3600, requests 10m CPU and 16Mi, termination grace 0. entries: - id: TestMain kind: integration source: test/integration/main_test.go actors: [test operator, VM account, build script, socialweb-computer CLI, live-slot broker] setup: > Suite preconditions. Offline mode (no host preconditions, no build) when SOCIALWEB_IT_OFFLINE is non-empty or every '|' part of -test.run starts with TestMarket. Otherwise, in order, and exit 1 if any fails: SOCIALWEB_IT_ACCOUNT is a local user and `sudo -n -u true` works when the caller differs; SOCIALWEB_IT_ARTIFACTS (default ~/.tmp/swc-it/-<4 hex>) is created; SOCIALWEB_IT_GUEST_IMAGE (default file:///var/lib/socialweb-computer/guest-image) holds vmlinux, rootfs.ext4 and image.json for a file:// source; unless SOCIALWEB_IT_SKIP_BUILD, `scripts/build-socialweb-computer.sh --dev --publish >` (15m, build.log) leaves /bin/socialweb-computer; SOCIALWEB_IT_RUN_ROOT (default /var/tmp/swcit) exists with mode 1777 and / does not exist yet and is created as the VM account with tmp/. asserts: > The exit code is the suite result, forced to 1 when harness setup fails, a shared network fails its teardown, or the run root cannot be removed. The artifact path is printed at the end. cleanup: > After all tests every shared network runs the standard teardown; a failure there fails the run. Then / is removed as the VM account. Artifacts are kept. - id: TestHarnessEmptyNetworkUpAndDown kind: integration source: test/integration/harness_test.go actors: [kcp, devsup, bidder, pds, plc] setup: > Two parallel subtests, each its own 1-bidder network without gateway (harness.network). asserts: > dev up exits 0 within 4m30s with MarketNetwork phase Ready. The fresh network lists 0 ComputeVM components in every application status. Two simultaneous instances on one host do not collide. cleanup: Standard teardown per subtest (harness.teardown). cases: - "a: one empty 1-bidder network up and down" - "b: a second empty 1-bidder network up and down at the same time" - id: TestAcceptOfAnEndedContractStartsNoVM kind: integration source: test/integration/accept_test.go actors: [tenant cluster, gateway, requester, bidder, kcp, pds, relay] setup: > Own network: --bidders 1 --with-gateway, network timeout 5m. Tenant cluster "tenant" answers /readyz within 3m; the default ServiceAccount exists within 30s. Pod default/it-accept-probe (harness.pod_probe) drives a NodeClaim, an RFP, a Bid, an accept and a ComputeVM on bidder-1. The pod is then deleted with grace 0. Then the bidder's dev-core process group gets SIGKILL. asserts: > A tenant Node is Ready within 4m; no NodeClaim is Failed or Lost at any time; no member keeps logging "failed after placement". Within 100s after the pod delete (10s scale hold + 90s) bidder-1 lists 0 ComputeVMs and no guest remains (no VM work dir, no VM unit). There is at least one Won Bid and every Won Bid has status.accept.uri; within 90s every Won Bid has status.terminated and the Bid count is unchanged. After the SIGKILL a new bidder pid appears within 2m and bidder-1 is Ready with a DID and a ready Bidder/bidder component within 2m. During a 15s settle window and at its end no ComputeVM appears and no guest starts for the ended contract; Bid and Won counts equal the counts from before the restart; every Won Bid still has status.terminated. cleanup: Standard teardown. The pod is already deleted. - id: TestAcceptSurvivesABidderRestart kind: integration source: test/integration/accept_test.go actors: [requester, bidder, kcp, pds, relay] setup: > Own network: --bidders 1, no gateway. `socialweb-computer run request --network --name rb<6 hex> --exec true --contract-timeout 4m --network-timeout 2m --state-dir ` (5m). Bids in root:bidder-1 are read every 50ms. When the first Bid is Published or Won the bidder dev-core process group gets SIGSTOP; once the requester retries the accept it gets SIGKILL. asserts: > The bidder publishes a Bid within 3m and run request has not exited before it. The stop lands while the Bid is Published, not Won (the restart falls between bid and accept). Within 45s the requester core log has submitAccept_retry. A new bidder pid appears within 2m. run request exits 0; its last JSON summary has phase Released, a non-empty contract receipt URI and a non-empty VM address. Exactly 1 Bid is Won. No guest remains within 1m. cleanup: > SIGCONT and SIGKILL to the original bidder pid, then standard teardown. A still running run request is killed. - id: TestOneAcceptMakesOneComputeVM kind: integration source: test/integration/accept_test.go actors: [requester, bidder, kcp, pds, relay] setup: > Own network: --bidders 1, no gateway. `run request --network --name ao<6 hex> --exec true --contract-timeout 4m --network-timeout 2m -- --accept-copies 10`: the requester sends 10 concurrent copies of one accept. ComputeVMs on bidder-1 are read every 200ms during the run. asserts: > At most 1 distinct ComputeVM is ever seen (a second one fails the test at once). run request exits 0 with phase Released, a receipt URI and a VM address. Every receipt on any ComputeVM is the requester's receipt. The requester log has submitAccept_copies with copies=10 and receipts equal to exactly that receipt. Exactly 1 Won Bid with a non-empty status.claimedBy. No guest remains within 1m. cleanup: Standard teardown. - id: TestARentedVMSurvivesABidderKillAndRestart kind: integration source: test/integration/bidder_restart_test.go actors: [requester, bidder, kcp, pds, relay, VM host systemd user units] setup: > Own network: --bidders 1, no gateway, parallel. `run request --network --name br<6 hex> --hold --exec 'echo IT-RESTART-HELD; read -r _' --contract-timeout 4m --network-timeout 2m` (6m), waiting for the marker. Records the VM identity: ComputeVM name and phase, runtime pid and start time, market address, receipt URI, and per VM user unit ActiveState, InvocationID, MainPID and process start ticks; and the issuerUrl values in the bidder core log. Two disruptions in sequence, then "done" on the guest stdin, then `run request release --contract-timeout 2m --network-timeout 2m` (3m). asserts: > Before: exactly 1 ComputeVM, phase Running, non-zero pid, an address, a receipt, at least 1 unit; the bidder log names exactly 1 issuer URL. After each disruption, within 2m the VM identity is unchanged (same units and invocation ids, same pid and start time) and so is the issuer URL. The held run request exits 0 with phase Held and the original receipt. The release exits 0 with phase Released and contract releaseStatus 200 (the vm.delete answer). No ComputeVM, VM unit or VM work dir remains within 1m. cleanup: Standard teardown. cases: - "kill -9 of the bidder dev-core process group; a new pid within 2m" - "socialweb-computer dev restart --instance bidder-1/bidder within 2m" - id: TestBidderAnswersEachRFPOnceAcrossARestart kind: integration source: test/integration/bids_test.go actors: [tenant cluster, gateway, bidder, kcp, pds, relay] setup: > Own network: --bidders 1 --with-gateway, 5m. Tenant "tenant" /readyz within 3m, default ServiceAccount within 30s. Pod default/it-bid-probe (harness.pod_probe) scales out. Then SIGKILL to the bidder-1 dev-core process group. asserts: > At least 1 tenant Node Ready within 4m; no NodeClaim Failed or Lost. Before the restart: no two Bids share spec.rfp.uri; at least 1 ComputeVM; exactly 1 Won Bid per ComputeVM (Bid status.computeVM names it). After the kill a new pid within 2m and bidder-1 Ready with a ready Bidder component within 2m. For a 15s settle window the Bid count does not change and the network stays healthy. After: same Bid, RFP, Won and ComputeVM counts, still 1 Bid per RFP, every ComputeVM still has a Won Bid. cleanup: Standard teardown (it also removes the pod, guests and tenant cluster). - id: TestComponentLinkRegistryEnforcesWorkspaceAccess kind: integration source: test/integration/componentlink_test.go actors: [component handler registry, bidder caller, pds handler, system caller] setup: > In process, no network (suite preconditions still apply unless offline). Handlers: root:beta Pds/pds HTTP handler "http" answering 204 and a stream handler "http"; root:alpha Pds/pds an HTTP handler. Calls GET http:/// and opens streams to root:beta Pds/pds "http" as several callers under several ComponentLink sets. asserts: > Denials are the link-denied error. With no links: alpha Bidder/bidder is denied; beta Bidder/bidder (same workspace) gets 204; the system caller gets 204. An exact link alpha Bidder/bidder -> beta Pds/pds gives 204; restricted to handler "other" denied, to "http" 204. A workspace-only link alpha -> beta gives 204; with a mismatched fromName denied. Streams: no link denied, the exact link allowed, a link for handler "other" denied. Clearing the links closes an open stream within 2s. After clearing, cross-workspace HTTP and streams are denied. cleanup: The registry is closed. Nothing on disk. - id: TestComponentLinksAreReconciledOnALiveMarketNetwork kind: integration source: test/integration/componentlink_test.go actors: [kcp, bidder, pds, policy engine] setup: > Own network: --bidders 1, no gateway, 5m. Reads every application component workspace from dev status and lists exposure.socialweb.computer/v1alpha1 ComponentLink objects in each. Deletes the reconciled Bidder -> Pds link in the MarketBidder workspace. Creates a manual ComponentLink manual-cross-workspace-<8 hex> in a Pds workspace outside the bidder workspace from Bidder/ to that Pds, then deletes it. asserts: > The MarketNetwork names at least 1 component workspace. In every workspace within 3m every ComponentLink has from and to in that workspace, observedGeneration == generation and status.ready=true with phase Ready. The bidder workspace links Bidder/ to a Pds and has no link to PolicyEngine. The deleted Bidder -> Pds link returns with the same name and a new UID, Ready within 3m. The manual cross-workspace link is Ready within 3m and never Failed, and is gone within 2m after its delete. The network stays healthy. cleanup: The manual ComponentLink is deleted (30s, NotFound ignored), then standard teardown. - id: TestAStoppedBidderIsReplacedAndReleasesItsOrphan kind: integration source: test/integration/failover_test.go actors: [tenant cluster, gateway, requester, bidder-1, bidder-2, kcp, pds, relay] setup: > Own network: --bidders 2 --with-gateway, 5m, gateway option vm-ready-timeout-sec=90, parallel. Warm-up: two concurrent `run request --name fo<6 hex> --exec true --contract-timeout 4m --network-timeout 2m -- --deny-bidder-dids ` (5m each). Then pod default/it-failover in tenant "tenant". Before the winner's node joins, the winner is stopped by stopped=true on its entry in MarketNetwork "network" spec.apps (root). Later the flag is removed. Finally the pod is deleted. asserts: > Warm-up: each request exits Released with a receipt, won by the bidder not denied; no guests within 2m. Within 4m a NodeClaim has status.contract.acceptUri and a winner DID while not yet Joining or Running; its receipt within 2m, still before any join; within 30s the winner has a ComputeVM carrying that receipt. After the stop the application message starts with "stopped:" within 2m and no claim for the old RFP is Joining or Running; within 270s that claim ends (Failed, Lost or Released) or is gone. Within 6m a replacement claim for a new RFP is Running, won by the other bidder, never Failed or Lost, and the pod is Ready on its node. After the restore the stopped bidder is Ready within 3m and within 3m has no ComputeVMs and no VM units; any ComputeVM it shows meanwhile carries only the old orphan receipt, never the replacement's. The pod stays Ready on the same node. After the pod delete no guest remains within 100s. cleanup: The pod is deleted with grace 0, then standard teardown for both bidder handles. - id: TestOpenTunnelProviderPublication kind: integration source: test/integration/ingress_provider_test.go actors: [opentunnel ingress provider, pds handler, fake opentunnel process] setup: > In process, no network. A fake opentunnel executable prints the URL held in a file (first https://pds-one.opentunnel.xyz) and sleeps. Handler root Pds/pds answers "ok". The opentunnel provider runs with route pds-test and local host 127.0.0.1. Ingress is registered (workspace root, exposure pds-ingress, service pds, protocol HTTP, handler http, ingress ref "ingress"). Then the file changes to https://pds-two.opentunnel.xyz and the same spec is registered again. asserts: > The provider is Ready within 10s. The first URL starts with https://pds-one.opentunnel.xyz. The second registration is refused with the stable-host-mismatch error. cleanup: Registration closed, provider cancelled and awaited, temp dir removed. - id: TestKubeconfigClientCertificateWithoutCredentialHelper kind: integration source: test/integration/kubeconfig_client_cert_test.go actors: [owner pds account, pds, gateway, tenant cluster, kcp, kubectl] setup: > Own network: --bidders 1 --with-gateway, 5m, parallel. Tenant "tenant" ready within 3m and ClusterRoleBinding socialweb-owner within 3m. Gateway XRPC address from Exposure svc-gatewayapi-gateway-api-local status.endpoint; owner from the gateway owner-sessions.json (exactly 1) via com.atproto.server.createSession over the network CA. Service-auth tokens from com.atproto.server.getServiceAuth with aud = the GatewayAPI gateway-api audience and a per method lxm. Calls com.publicdomainrelay.temp.cluster.getKubeconfig, computer.socialweb.k8s.kubectlApplyServerSide and com.publicdomainrelay.temp.cluster.revokeIdentity. Uses the kubeconfig through client-go and /usr/local/bin/kubectl with a PATH that lacks socialweb-credential. asserts: > getKubeconfig {"name":"tenant","ttlSeconds":3600} is 200 with Cache-Control no-store. The kubeconfig has exactly 1 user with a plain client cert and key (no exec plugin, no token); cert CN = owner DID, O = [socialweb.computer:cluster-owners], lifetime 1h to 1h05m. ttlSeconds 59 and 86401 are 400 InvalidRequest. A token for lxm listClusters is 401 on getKubeconfig. `kubectl get nodes -o name` exits 0 and a client-go node list works. kubectlApplyServerSide of ConfigMap default/it-owner-credential (fieldManager kubectl) is 200 and the object exists. revokeIdentity {"name":"tenant"} is 200, and within 2s the cert is refused (401/403; kubectl non-zero). The private key (base64 of the PEM, and PEM body lines of 40+ chars) appears in no file under the gateway logs or the gateway run dir. cleanup: Fixture dir removed; standard teardown. - id: k8s_xrpc_test.go kind: fixture source: test/integration/k8s_xrpc_test.go actors: [owner pds account, gateway, tenant cluster] setup: > The shared "Job through XRPC" check of the scale-to-zero test (cluster "tenant", a test namespace, a pinned node), calling the gateway computer.socialweb.k8s.* methods with owner service-auth tokens (aud = gateway audience, lxm = full method NSID). asserts: > kubectlApplyServerSide of ConfigMap it-app-config (environment=production, log_level=info, fieldManager kubectl) is 200 with a uid and kind ConfigMap, and the object exists. log_level=debug with fieldManager other and no force is 409 ApplyConflict; with force=true 200 and the cluster shows debug. Kind example.invalid/v1 Nope is 400 KindNotFound. Job it-xrpc-job (batch/v1, backoffLimit 0, nodeName pinned, restartPolicy Never, busybox printing a marker and uname -m) is 200 kind Job. getJob (tailLines 20) reaches Succeeded within 90s with exactly 1 pod, 1 container, Terminated, exit 0, the marker in its log. getJob it-no-such-job is 404 ObjectNotFound. cleanup: Job it-xrpc-job deleted with background propagation; a failed delete is an error. - id: market_fixtures_test.go kind: fixture source: test/integration/market_fixtures_test.go actors: [pds, relay, jetstream, oauth token server, oauth QR transfer service, event stream controller, market inbox] setup: > In-process fakes for the offline TestMarket* tests. PDS: a local full-mode PDS (sqlite repo and account stores, random did:plc, handle market<8 hex>.test, k256 repo key), or a real PDS from SOCIALWEB_IT_MARKET_PDS_URL/_DID/_HANDLE/_PASSWORD. Account modes: managed-associated (association RequireApproval) and managed-unassociated (Skip) use a bearer session; existing-pds-account a RemotePDS market-remote with secret refs market/access (accessJwt) and market/dpop (P-256 DPoP key); existing-oauth-browser and existing-oauth-delegated OAuth sessions refreshed by a fake token server; existing-oauth-qr a QR transfer at /xrpc/com.fedfork.atprotoOauthQR. Relay fake: com.atproto.sync.subscribeRepos websocket in cbor or json frames. Jetstream fake: commit frames with time_us. EventStream objects (source relay or jetstream, collections, optional dids, handler sink) on a fake API with 20ms retry. Records: com.publicdomainrelay.temp.market.rfp, .bid, .accept, .receipt; association claims as com.publicdomainrelay.temp.badgeBlueKeys. asserts: > Relay and jetstream record each requested cursor and replay the backlog above it (resume is observable); the relay can close after N events to force a reconnect. The inbox dedupes on uri#cid and counts every apply. The token server in revoked mode answers 400 invalid_grant. The QR service needs a Bearer token (401 without), answers 400 without a nonce, and serves a nonce once (404 after). cleanup: Every fake server closed, local PDS stores closed and removed, controllers cancelled. - id: TestMarketAccountAndTransportMatrix kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds, jetstream, event stream controller, oauth token server, qr transfer service] setup: > Offline (market_fixtures_test.go). Each account subtest has its own PDS account and a com.atproto.server.createSession session; the OAuth modes use a DPoP-bound session as client fixture-browser, fixture-delegated or (via the one-shot QR transfer with a Bearer service-auth token) fixture-qr. Each transport subtest writes one com.publicdomainrelay.temp.market.bid with com.atproto.repo.createRecord and delivers it into an inbox keyed by at-uri#cid. XRPC: by com.atproto.repo.getRecord. subscribeRepos: com.atproto.sync.subscribeRepos opened before the write. JetStream: an EventStream (jetstream, collections [bid], dids [account]). Both: subscribeRepos plus XRPC. asserts: > XRPC: the first delivery is new, 1 object. subscribeRepos: within 10s an event with seq >= 0 and ops[0].path /, 1 object. JetStream: the handler gets the commit within 15s, new, 1 object. Both: within 15s an event with repo = account DID and an op path; XRPC delivery is new, the firehose copy (same uri and cid) is not; 1 object after 2+ applies. cleanup: Fakes closed, temp stores removed, controller stopped. cases: - "accounts: managed-associated, managed-unassociated, existing-pds-account, existing-oauth-browser, existing-oauth-qr, existing-oauth-delegated" - "transports: XRPC, Firehose subscribeRepos, Firehose JetStream, Both" - "6 accounts x 4 transports = 24 subtests" - id: TestMarketDuplicateDeliveryCreatesOneObject kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds, relay, event stream controller] setup: > Offline. A relay EventStream (relayUrls [fake relay], collections [com.publicdomainrelay.temp.market.bid], dids [account]). One bid record; the relay publishes its commit at seq 1 and again at seq 2. asserts: > Both firehose events reach the handler within 15s. Both copies plus the getRecord copy give exactly 1 object after exactly 3 applies. cleanup: Fakes closed, controller stopped. - id: TestMarketRestartResumesCursorsAndDedupe kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds, relay, event stream controller] setup: > Offline. A JSON relay that drops the websocket after every event. Relay EventStream on the bid collection and the account DID. Two bids published at seq 1 and 2. asserts: > Both events arrive (15s each). Event one, then event two twice, give exactly 2 objects. The relay saw a reconnect with cursor=1. cleanup: Fakes closed, controller stopped. - id: TestMarketOutOfOrderAcceptConverges kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds] setup: > Offline. One record each of com.publicdomainrelay.temp.market.bid, .receipt and .accept, delivered by getRecord in the order accept, bid, receipt, accept. asserts: Exactly 3 objects after exactly 4 applies; the early accept does not block. cleanup: PDS closed, stores removed. - id: TestMarketFirehoseFailsClosedWithoutIngress kind: integration source: test/integration/market_matrix_test.go actors: [event stream controller] setup: An EventStream with source relay, an empty relay URL, the bid collection, no DID filter. asserts: Within 15s status.phase is Failed and status.cursor stays 0. cleanup: Controller stopped. - id: TestMarketRemotePDSMissingAuthFailsClosed kind: integration source: test/integration/market_matrix_test.go actors: [pds, remote pds binding] setup: > Offline. A RemotePDS market-remote with spec.url and spec.did, resolved first without auth, then with spec.auth accessTokenRef market/access and dPoPKeyRef market/dpop (P-256 multibase). asserts: > Without auth resolving fails with the authentication-required error. With auth it resolves and com.atproto.server.describeServer returns the PDS DID. cleanup: PDS closed, stores removed. - id: TestMarketRelayReconnectResumesCursor kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds, relay, event stream controller] setup: Offline. A JSON relay that drops after every event; two bids published at seq 10 and 11. asserts: Both events reach the handler (15s each); the relay saw a reconnect with cursor=10. cleanup: Fakes closed, controller stopped. - id: TestMarketAssociationRevocationRemovesApproval kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds] setup: > Offline. com.atproto.repo.putRecord of a com.publicdomainrelay.temp.badgeBlueKeys claim (rkey fixture-claim-, keyId a random did:plc, challenge the account DID, service, nonce, createdAt), then com.atproto.repo.deleteRecord. asserts: > Before the delete getRecord finds it; after, getRecord reports it absent (RecordNotFound, RepoNotFound or 404). cleanup: PDS closed, stores removed. - id: TestMarketExpiredSessionReportsSignInAgain kind: integration source: test/integration/market_matrix_test.go actors: [oauth client, oauth token server] setup: > Offline. An OAuth session whose access JWT expired 1h ago (P-256 DPoP key, scope atproto, refresh token fixture-refresh) against a token endpoint that issues new tokens; a second session (fixture-revoked) against one that answers 400 invalid_grant. asserts: > The first session refreshes and returns a new access token. The revoked one fails with the session-expired (sign in again) error. cleanup: Fake token servers closed. - id: TestMarketTransportFixtureFormats kind: integration source: test/integration/market_matrix_test.go actors: [account holder, pds, relay, event stream controller] setup: > Offline. One bid record. json: a relay EventStream consumes text frames. cbor: a subscribeRepos client consumes binary frames (header op 1 t #commit; body with a CID tag 42 and an op path). Commit published at seq 1. asserts: In both formats the decoded event arrives within 15s with repo = account DID. cleanup: Fakes closed. cases: [json, cbor] - id: TestMarketPDSFixturePointableAtRealPDS kind: integration source: test/integration/market_matrix_test.go actors: [pds] setup: > Skipped unless SOCIALWEB_IT_MARKET_PDS_URL is set; uses that PDS with _DID (must parse), _HANDLE and _PASSWORD and a createSession session. asserts: An authenticated com.atproto.server.describeServer succeeds. cleanup: Nothing created. - id: TestPolicyWorkflowRunsBundledMatrix kind: integration source: test/integration/policy_matrix_test.go actors: [bidder, requester, policy engine, kcp] setup: > Shared 1-bidder network without gateway. PolicyEngine policy-engine in root:bidder-1 has status.ready=true within 2m. Per case a PolicyWorkflowRun: engineRef policy-engine, workflowRef the bundled workflow of the case; perspective, selfDid, subjectDid, counterpartyDid, rootRequesterDid from the case; args {matrixCase: }; inputs mode=full, self-did, perspective, subject-did, counterparty-did, root-requester-did and strong-ref record JSON for com.publicdomainrelay.temp.market.rfp, .bid and .accept; activeDeadlineSeconds 120; ttlSecondsAfterFinished 600. asserts: > Within 2m each run is Succeeded with exitStatus success and condition Complete=True; outputs.evaluate.allow is "true" or "false" and violations a JSON array; consoleOutput is non-empty and contains "policy":""; status.cache has a key policy//... holding result.json. cleanup: Runs expire by TTL or with the network; shared network standard teardown at package end. cases: - "bidder-mutuals (bidder)" - "bidder-only-me (bidder)" - "bidder-tangled-vouch (bidder)" - "bid-payload (requester)" - "open (bidder)" - "requester-mutuals (requester)" - "requester-only-me (requester)" - "requester-tangled-vouch (requester)" - "under-4-cpus (bidder)" - id: TestPolicyWorkflowRunCacheRoundTrip kind: integration source: test/integration/policy_matrix_test.go actors: [bidder, policy engine, kcp] setup: > Shared 1-bidder network; PolicyEngine ready within 2m. A first PolicyWorkflowRun of the bundled open workflow (bidder perspective, args {cacheRoundTrip: }, full inputs); a second with the same spec and spec.cache seeded from the first run's status.cache. asserts: > Both Succeeded within 2m with the success shape. The first persists a policy/open/ cache key. The second run's console contains "policy cache hit", and every policy/open/ key of the first is in the second's status.cache with byte-identical result.json. cleanup: Runs expire by TTL; shared network teardown at package end. - id: TestPolicyWorkflowRunInputsArgsPerspective kind: integration source: test/integration/policy_matrix_test.go actors: [requester, policy engine, kcp] setup: > Shared 1-bidder network. A PolicyWorkflow (engineRef policy-engine) with one job echo whose outputs args, perspective and self come from inputs.policy-args, inputs.perspective and inputs.self-did, visible within 30s. A PolicyWorkflowRun of it: perspective requester, selfDid did:plc:itechobidder000000000000, args {alpha: one, beta: two}, activeDeadlineSeconds 60. asserts: > Succeeded within 2m; outputs.echo.args is exactly {"alpha":"one","beta":"two"}, perspective requester, self the selfDid. cleanup: Objects go with the shared network. - id: TestPolicyWorkflowRunCancellationAndDeadline kind: integration source: test/integration/policy_matrix_test.go actors: [bidder, policy engine, kcp] setup: > Shared 1-bidder network. Each case notes the network's deno policy processes (deno --no-prompt --no-config --allow-net under the network dev-core) and creates a PolicyWorkflowRun with an inline spec.workflow of 800 blocking tangy/policy-open@v1 steps. asserts: > spec-cancel (deadline 600; Running and a new deno process within 60s, then spec.cancel=true): Cancelled with condition Cancelled=True within 2m. deadline (activeDeadlineSeconds 1): Failed with Failed=True reason DeadlineExceeded within 30s. deletion (deadline 600; Running, then the object is deleted): gone within 30s. In all three every deno process the run started exits within 60s. cleanup: Runs expire by TTL; shared network teardown. cases: [spec-cancel, deadline, deletion] - id: TestPolicyWorkflowRunFailsClosedOnRecordTypes kind: integration source: test/integration/policy_matrix_test.go actors: [bidder, policy engine, kcp] setup: > Shared 1-bidder network. Per case a PolicyWorkflow with spec.recordRef {uri at://did:example:matrix//, cid bafyfailclosed} and a PolicyWorkflowRun of it whose spec.cache entry rec// holds value.json {"$type":""}. asserts: > Within 30s the run is Failed with "typescript policy records are disabled" or "denoWorker policy records are disabled"; consoleOutput is empty; no new deno policy process after 10s. cleanup: The PolicyWorkflow is deleted; the run expires by TTL. cases: - "typescript (computer.socialweb.temp.policy.typescript)" - "deno-worker (com.publicdomainrelay.temp.market.policies.denoWorker)" - id: TestPolicyWorkflowRunTTLDeletesFinishedRun kind: integration source: test/integration/policy_matrix_test.go actors: [bidder, policy engine, kcp] setup: > Shared 1-bidder network. A PolicyWorkflowRun of the bundled open workflow with ttlSecondsAfterFinished 2. asserts: Succeeded within 2m with the success shape, then deleted by its TTL within 60s. cleanup: The TTL deletes the run. - id: TestRequestSecretsReachTheGuestThroughTheRFPFlow kind: integration source: test/integration/request_secrets_test.go actors: [requester, bidder, pds, kcp, guest VM] setup: > Own network: --bidders 1, no gateway, parallel. A secrets bundle [{path: /root/secrets/it/value, value: it-secret-<32 hex>}] (mode 0600). `run request --network --name rq<6 hex> --secrets --hold --contract-timeout 2m --network-timeout 2m --exec "systemctl start setup-secrets.service; cat /root/secrets/it/value; echo; echo IT-SECRET-READ; read -r _"` (5m). Then "done" and `run request release ` (3m). asserts: > The guest prints the exact secret before IT-SECRET-READ. While held the secret (plain, and std and raw-url base64 at 3 byte offsets) appears nowhere outside the guest: files under the state dir and requests/ (vms excluded), process cmdlines and environments, the VM account's user journal since the start, test logs other than run request stdout; the scan covered at least 1 kine.db and at least 2 repos.db. The summary has phase Held, contract.receiptUri, winnerDid, secretsFetchedAt and at least one contract.hostKeys entry; stderr has StrictHostKeyChecking=yes. The release is Released with releaseStatus 200. Within 1m no ComputeVM, VM work dir or VM unit is left. The scans are clean again after the release. cleanup: Standard teardown. - id: TestTenantClusterScalesFromZeroOnMarketVMsAndBack kind: integration source: test/integration/scale_to_zero_test.go actors: [tenant owner, gateway, bidder, kcp, tenant cluster, guest VM] setup: > Shared 2-bidder network with gateway, parallel with TestClusterEventsStreamFromSubscribeClusters. Tenant "tenant" /readyz within 3m. Namespaces it-srv, it-cli-allowed (label it-allow=yes), it-cli-other. busybox httpd pods it-a and it-b in it-srv with required hostname anti-affinity, Service it-b:8080; later pod it-c pinned to B's node. The k8s_xrpc_test.go check on node A. NetworkPolicies applied and removed one at a time: deny-ingress, deny-egress, egress-one-ipblock (B /32), allow-labelled-namespaces, default-deny-dns-allow (UDP and TCP 53). Then delete it-b and it-c, then the three namespaces. asserts: > Idle 0 Nodes and 0 NodeClaims. Within 4m two nodes join from market RFP VMs and it-a and it-b are Ready on different nodes; exactly 2 NodeClaims Running with status.contract.receiptUri (within 2m); the bidders run one ComputeVM per receipt. The XRPC fixture passes. Network probes from node A (30s settle each): without policy B by IP, the CoreDNS name it-b.it-srv.svc.cluster.local and the service name all reach; deny ingress and deny egress block and removal restores; ipBlock allows B and blocks C; namespaceSelector allows only the labelled namespace; default deny with DNS allowed resolves but blocks B. Scale in: 1 Node and 1 NodeClaim within 100s, never more than one node released at once, node A kept; then 0 and 0. Within 1m no ComputeVM carries this test's receipts. The gateway core log has 2+ node_claim_released lines, all confirmed=true. /readyz answers at 0 nodes. cleanup: The test scales to zero itself; shared network standard teardown at package end. - id: TestSshGatewayExposesItsServerAndReusesTheGuestAcrossRestart kind: integration source: test/integration/sshgateway_test.go actors: [owner, ssh gateway, ssh server, pds, bidder, kcp, guest VM] setup: > Needs a dumbpipe executable (SOCIALWEB_IT_DUMBPIPE, /usr/local/bin/dumbpipe or PATH). Own network: --bidders 1, network timeout 10m. An owner ssh-<8 hex>.alice.test on the alice PDS via com.atproto.server.createAccount, written to owner-sessions.json (0600). `socialweb-computer run gateway ssh --network --host-backend proc --name sshgw --network-timeout 5m -- --test-owner-sessions-file `; its report names a local ssh exposure and the local svc-sshserver HTTP exposure. POST /keys with HTTP basic auth registers one ed25519 key. ssh as user with LC_SESSION=it-<8 hex> runs hostname. Then `socialweb-computer dev restart sshgw/ssh-server --instance ` and the same session again. asserts: > Key registration is 200 with an rkey. An unregistered key is refused. The first hostname is non-empty and the bidders hold exactly 1 ComputeVM. dev restart succeeds within 4m. The second session returns the same hostname and still exactly 1 ComputeVM (the guest is reused). cleanup: Standard teardown. - id: TestClusterEventsStreamFromSubscribeClusters kind: integration source: test/integration/subscribe_clusters_test.go actors: [tenant owner, fresh account, gateway, pds, bidder, kcp, tenant cluster] setup: > Shared 2-bidder network with gateway. Gateway endpoint from Exposure svc-gatewayapi-gateway-api-local, audience from GatewayAPI gateway-api; owner from owner-sessions.json; tokens with lxm com.publicdomainrelay.temp.cluster.. Websocket /xrpc/com.publicdomainrelay.temp.cluster.subscribeClusters (binary CBOR frames) for the owner and for a fresh createAccount account. POST com.publicdomainrelay.temp.cluster.createCluster {name: demo}; a subscription with ?name=nope. Then namespace it-srv and pod it-stream in demo, later deleted. asserts: > Within 30s the owner snapshot ends with #info SnapshotComplete, has 2+ events and starts with #cluster tenant snapshot=true. The fresh account's snapshot has no #cluster and no error frames. createCluster is 200. ?name=nope yields an error frame ClusterNotFound within 15s. #cluster demo reaches Ready within 3m with Initializing before Ready and at least one #member event; if tenant was not Ready at snapshot time, demo's first #member precedes tenant Ready. After the pod is created a #nodeClaim reaches Running within 4m after Bidding and Provisioning, never Failed or Lost; the pod is Ready within 90s. After the pod delete a #nodeClaim deleted=true frame arrives within 100s. cleanup: Websockets closed; demo goes with the shared network teardown. - id: TestOutOfBandComputeVMDeletionPublishesTermination kind: integration source: test/integration/termination_test.go actors: [requester, bidder, kcp, pds, guest VM] setup: > Own network: --bidders 1, parallel. `run request --network --name term<6 hex> --hold --exec "echo ; read -r _" --contract-timeout 4m --network-timeout 2m` (6m). After the marker the single ComputeVM in root:bidder-1 is deleted directly in kcp. asserts: > Exactly 1 ComputeVM with a UID while held. It is gone within 3m. Within 2m StateSync termination- in root:bidder-1 has status.phase Acked and status.acked.ref (one termination published). The held run request exits within 90s. The network stays healthy. cleanup: Standard teardown. - id: TestWebAppSignInOverLoopbackOriginIssuesAClientCertificate kind: integration source: test/integration/web_app_signin_test.go actors: [browser user, web app, gateway, pds, kcp, tenant cluster] setup: > A loopback origin http://127.0.0.1:. Own network: --bidders 1 --with-gateway, 5m, gateway option public-origin=; a TCP proxy from the origin to the gateway API Exposure endpoint. Tenant "tenant" /readyz and ClusterRoleBinding socialweb-owner within 3m. A cookie-jar client that does not follow redirects, with Origin=: GET /oauth-client-metadata.json; POST /oauth/start {handle: owner DID}; POST /oauth/authorize on the PDS (identifier, password, request_uri, client_id); GET the /oauth/callback it returns; GET /session twice; POST /api/clusters/list; POST /api/clusters/kubeconfig {name: tenant}. asserts: > Metadata (retried up to 2m) is 200 with client_id /oauth-client-metadata.json and redirect_uris containing /oauth/callback. /oauth/start is 200 with an authorizationUrl at /oauth/authorize on the PDS carrying request_uri and that client_id (a PAR was pushed). The PDS authorize is 200 with a code and a redirect_uri at /oauth/callback with state and iss. The callback is 302 to / and sets swc_session HttpOnly, SameSite=Strict, Path=/, not Secure. /session is 200 with did = owner DID on both calls. clusters/list is 200 naming tenant, which is Ready within 5m. kubeconfig is 200 (429 retried) with Cache-Control no-store, exactly 1 user with a plain client cert and key, CN = owner DID, O [socialweb.computer:cluster-owners]; a client-go node list on the tenant works with it. cleanup: Listener and proxy closed; standard teardown. - id: soak kind: soak source: test/soak/main.go actors: [market network, requester soakreq, bidder, gateway, tenant cluster, pds, openbao, kcp front, operator host] setup: > A Linux-only program `soak [flags]` with verbs load, sample, hammer, tidy, inspect, orphans and release (anything else: usage, exit 2). No script or CI invoked it; it was run by hand as systemd user units on a DigitalOcean droplet. Target: an existing `socialweb-computer dev up --profile market-network --bidders 1 --with-gateway` instance (optionally --vm-host-backend systemd-user), read from --instance (default soak) and --state-dir (default /var/tmp/swcit/soak); children under /apps/dev/-. --binary (default /var/tmp/soak/bin/socialweb-computer) runs `dev status` and `status`. A typical run: a long load (RFP, VM and tenant lanes), a sample process, tidy every 10 minutes, and chaos run apart (dev restart of DenoPods, kill -9 of cores and bidder pods); recorded rates 1.1 RFP/s, a VM contract every 40s on 3 lanes held 20s, 2 to 6 tenant lanes, contract TTL 40m, requester retention 900s, 3h or more. asserts: > No threshold in the program; verbs exit 1 only on setup errors. load prints a JSON counter line every minute (ts, elapsedSec, rfpCreated, rfpFailed, vmStarted, vmHeld, vmReleased, vmFailed, refused, tenantCycles, tenantOut, tenantIn, tenantFailed, clustersCreated, clustersDeleted), one at stop and one after in-flight lanes drain (up to 15m). Pass, judged outside: failed counters 0 or explained by chaos; each application reports 0 orphans and 0 stuck terminations; `soak orphans` reports unclaimed 0; every series in samples.jsonl plateaus: after warm-up a least-squares fit is flat when |slope x window| <= max(2 x residual sd, 5% of the mean). cleanup: > Contracts expire by their socialweb.computer/ttl-after-finished annotation; open VM contracts are closed by soak release; tenant lanes delete their Cluster and Workspace. The soakreq child stays until dev down. Afterwards only live and retained objects and records: no orphan compute.vm records, no unowned objects, no stuck finalizers. - id: soak/load-requester-bootstrap kind: soak source: test/soak/main.go actors: [market network, requester soakreq] setup: > First phase of soak load. --requester (default soakreq), --contract-quota 20000, --max-concurrent 64. Without a NetworkApp of that name it adds a MarketRequester application with the network requester profile, a ComputeContracts quota and options max-concurrent and per-account-max; an existing one is reused. No record retention exists: nothing deletes the requester's records by age. asserts: > The NetworkApp reaches phase Ready with an instance name and kcp URL, without the stopped message, within 10m; listing ComputeContract in the child workspace works within 3m; prints {"event":"requester ready","instance":""}. Failure exits 1. cleanup: None; the requester child stays. - id: soak/load-rfp kind: soak source: test/soak/main.go actors: [requester soakreq, bidder, pds] setup: > On when --rfp-rate > 0 (default 0.5/s). RFP-only ComputeContracts rfp-- (spec.vmName soak-rfp, annotation socialweb.computer/rfp-only=true, ttl-after-finished --contract-ttl default 50m) at a fixed pace, at most --rfp-inflight (48) in flight, for --duration or until a signal. asserts: > rfpCreated per accepted create; Forbidden or Invalid with "quota" counts refused (back-pressure); other errors rfpFailed. Healthy: rfpFailed 0; object counts, market.rfp repo records and process memory plateau. cleanup: Contracts expire by TTL, market.rfp records by requester retention. - id: soak/load-vm kind: soak source: test/soak/main.go actors: [requester soakreq, bidder, vm host, pds] setup: > On when --vm-every > 0 (default 45s). A full ComputeContract vm-- every --vm-every, at most --vm-lanes (3) open, through RFP, Bids, accept and VM boot. asserts: > vmStarted per create; Held within 10m counts vmHeld, else (Failed, Released, timeout) vmFailed. After --vm-hold (20s) spec.release=true and Released within 5m counts vmReleased, else vmFailed. Pass: vmFailed 0 or only chaos-explained (no_bids around a bidder kill). cleanup: Every opened contract gets spec.release=true; nothing left behind. - id: soak/load-tenant-cycle kind: soak source: test/soak/tenant.go actors: [gateway, tenant cluster, bidder, vm host] setup: > On when --tenant-every > 0 (default off). An existing tenant cluster --tenant-name (default tenant) on the -gateway child, opened through the gateway state dir (clusterprovider/pdr-*-: front.json, server CA, admin client cert). One cycle per --tenant-every, pod hold --tenant-hold (20s). asserts: > default ServiceAccount within 1m; pod soak-- (pinned busybox, 10m CPU, 16Mi, grace 0) Running within 6m (tenantOut); after the hold and delete, 0 nodes within 5m (tenantIn); any failure counts tenantFailed with a JSON event. cleanup: The probe pod is always deleted; the cluster stays; no nodes, VMs or contracts remain. - id: soak/load-tenant-lanes kind: soak source: test/soak/lanes.go actors: [gateway, tenant cluster, bidder, vm host, kcp workspace] setup: > On when --tenant-lanes N > 0 (recorded 2, 4, 6). Needs -gateway with a bootstrap account DID. Lanes --lane-base+1 .. --lane-base+N, lane i starts after i x 20s. Each lane loops: create Cluster lc in the bootstrap account home workspace (root::) through the gateway members.kubeconfig, run the scale cycle, delete the cluster. asserts: > clustersCreated; tenant /readyz within 5m; pod Running within 8m (tenantOut); nodes 0 within 6m after delete (tenantIn); errors count tenantFailed with backoff 1m per failure up to 5m; Cluster NotFound within 10m after delete. Pass: tenantFailed 0 apart from stop-cut cycles, clustersDeleted == clustersCreated, gateway kcp memory and OpenBao storage per cycle plateau. cleanup: > After every round the Cluster is deleted and awaited, then the tenancy.kcp.io Workspace of the same name; no cluster, workspace, node or VM left. - id: soak/sample kind: soak source: test/soak/sample.go actors: [operator host, market network, requester, bidder, gateway, pds, kcp, kine, openbao] setup: > `soak sample` --every (default 5m; 1m used), --out (default /var/tmp/soak/samples.jsonl), --once. One JSON line per tick: host memory, swap, oom_kill count, load, /var/tmp use, journal bytes; per app and role process count, RSS and sockets (kcp, kine, bao, firecracker, dumbpipe, caddy, postgres, deno:, core:); bytes per state subdir and sqlite file; `status` contracts per phase; kv.sqlite3 and pds repo.sqlite3 bytes, wal and rows; per PDS listener com.atproto.repo.describeRepo and paged com.atproto.repo.listRecords counts per DID and collection; kine live keys per resource and logical clusters; sampleSec. asserts: > Collection only; the evidence for the soak verdict (plateau rule; OOM kills flat; record counts follow retention). It closes idle connections after each sample so it does not leak itself. cleanup: Writes only the samples file. - id: soak/hammer kind: soak source: test/soak/hammer.go actors: [kcp front, gateway, tenant cluster mount] setup: > `soak hammer` --workers (64), --duration (5m), --every (0). Targets the network kcp default workspace, a child kcp (--app), or a tenant cluster's mounted workspace under the gateway account (--mount, root:::). Each worker lists core v1 namespaces (limit 1) through the kcp front, no client rate limit, 10s timeout. asserts: > Prints "requests failed ". Pass: failed 0 and kcp RSS flat (reproduces mount proxy transport growth and audit-log growth). cleanup: None; read-only. - id: soak/tidy kind: soak source: test/soak/tidy.go actors: [market network, requester, bidder, gateway, openbao, tenant vault] setup: > `soak tidy` --interval (15m), once or every 10 minutes. For the network kcp and every child, every OpenBaoServer gets a merge patch (field manager soak-tidy) setting spec.tidy.interval and, when present, each spec.declared.pkiTidy interval. asserts: > Prints one line per server; OpenBaoServer status lastRunAt advances every interval and OpenBao storage stays flat in the samples. cleanup: The shortened intervals stay until teardown. - id: soak/inspect kind: soak source: test/soak/inspect.go actors: [bidder, requester, gateway] setup: > `soak inspect` --app (soak-bidder-1), --group (socialweb.computer), --resource (computevms, v1alpha1), --path (root), --unowned (true), --name. Lists the resource in that child's kcp. asserts: > One JSON line per object (name, phase, ageSec, owners, finalizers, deleting, annotations); unowned only by default. Pass: no unowned object older than its TTL, none stuck deleting with finalizers. cleanup: None; read-only. - id: soak/orphans kind: soak source: test/soak/orphans.go actors: [requester soakreq, pds] setup: > `soak orphans` --app (soakreq), --older-than (1h). Finds the app DID and repo listener from `dev status`, lists every ComputeContract (status.contract.payload.uri, status.contract.rfp.uri), pages com.atproto.repo.listRecords for com.publicdomainrelay.temp.compute.vm and com.publicdomainrelay.temp.market.rfp. asserts: > A compute.vm record older than --older-than whose URI and rfp URI no live ComputeContract claims is an orphan; one JSON line each, then a summary {contracts, vms, rfps, older, unclaimed, olderThan, repo}. Pass: unclaimed 0 (ACC13-NO-ORPHANS). cleanup: None; reports only. - id: soak/release kind: soak source: test/soak/release.go actors: [requester soakreq, bidder, vm host] setup: > `soak release` --app (soakreq), after load stops or crashes. Lists the ComputeContracts of the requester child. asserts: > Every vm-* contract not Released or Failed gets spec.release=true; each then reaches Released and its VM stops (seen with status or soak sample). RFP-only contracts are skipped. cleanup: The cleanup of the VM lane; no vm-* contract stays Held or pending. - id: internal/xrpcserver/Test* kind: wire source: internal/xrpcserver/xrpcserver_test.go asserts: > XRPC at /xrpc/: missing, expired or badly signed token, wrong or missing lxm, or wrong aud (did:web:host or did:web:host#socialweb_computer) is 401 {"error":"Unauthorized"}; a body that fails the lexicon is 400 InvalidRequest; Initializing is 429 ClusterInitializing with Retry-After; Draining is 409 ClusterDraining without it; unmapped errors 500 InternalError with no detail; wrong method 405; revokeIdentity of another DID 403 Forbidden, its revoked[] starts with {name:"",state:RevocationPending}, no writer 503 ClusterUnavailable. - id: internal/atprotoauth/Test* kind: wire source: internal/atprotoauth/atprotoauth_test.go asserts: > Service-auth JWT: lifetime above the maximum refused (exactly the maximum accepted); exp, lxm (present and equal) and aud checked; did:key issuers refused without lookup; did:plc and did:web resolved; an unresolvable issuer is no-key, not bad-signature. - id: internal/gateway/Test* kind: wire source: internal/gateway/gateway_test.go asserts: > A cluster the caller does not own is 404 ClusterNotFound (not 403); revoked identity is IdentityRevoked and creates nothing; a kcp refusal is ClusterUnavailable carrying the cause; an unreadable deny fails closed; createCluster sets running=true and returns the current phase. - id: internal/revocation/Test* kind: wire source: internal/revocation/revocation_test.go asserts: > revokeIdentity reply: account entry {name:"",state:RevocationPending}, then one per cluster (ColdDeferred when cold, else RevocationPending); no clusters gives a single Revoked entry. Revoke on DID 404 or a document without keys, never on a fetch error. - id: internal/plcresolver/Test* kind: wire source: internal/plcresolver/plcresolver_test.go asserts: > did:plc via GET /: 404 is gone; 500, 429, transport error or non-JSON is an error. Keys are verificationMethod ids (did#atproto). did:web via https://host/.well-known/did.json: 404 revokes, NXDOMAIN or timeout does not. did:key and non-DIDs make no request. - id: internal/didkey/Test* kind: wire source: internal/didkey/didkey_test.go asserts: > did:key ed25519 spec vector 3b6a27bc... <-> did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp; secp256k1 did:key refused; base58 keeps leading zeros; the iroh/dumbpipe EndpointID is the hex ed25519 public key of IROH_SECRET (vector f8555129... -> cb668665...). - id: internal/kcpclient/Test* kind: wire source: internal/kcpclient/*_test.go asserts: > ServiceAccount name is "did-" + lowercase unpadded base32 of the DID (ADR 0020 vector did:plc:testaccount0000000000000000 -> did-mruwiotqnrrtu5dfon2gcy3dn52w45bqgaydambqgaydambqgaydambq). Tree root:::clusters; account and home workspaces carry their spec.type, the mounted child none and spec.mount.ref {socialweb.computer/v1alpha1, Cluster, name}; running off is PATCH spec.running=false; deny is an atprotoidentities object {did, scope Account, revoked}; Node and ClusterLink status via /status; MarketIdentity approve is a JSON patch testing resourceVersion and appending spec.approvedAssociations; context system:admin is the shard admin. - id: internal/kcpapply/Test* kind: wire source: internal/kcpapply/kcpapply_test.go asserts: > A workspace is addressed as /clusters/; an object whose managedFields name kubectl, kubectl-client-side-apply or kubectl-edit is refused, never adopted. - id: cmd/socialweb-computer/TestAChangedAPIResourceSchemaIsRefusedNotPatched kind: wire source: cmd/socialweb-computer/apply_live_linux_test.go asserts: > Live kcp: `socialweb-computer apply` of an APIResourceSchema with a changed spec under the same name is refused; the live object is unchanged. - id: cmd/socialweb-computer/TestTheKcpFrontFlipsWithZeroFailedReadyzPolls kind: wire source: cmd/socialweb-computer/roll_live_linux_test.go asserts: > Live: during a kcp slot roll, a front restart and a slot kill -9, GET /readyz through the front never fails or is refused; the front port does not change; watches reconnect. - id: cmd/socialweb-computer/Test* kind: wire source: cmd/socialweb-computer/main_test.go, dev_verbs_test.go, install_test.go, release_test.go asserts: > Exit 2 for a verb with no backend on the OS (names plan and GOOS), for unset or unknown SOCIALWEB_HOST_BACKEND, and for install with proc or as non-root. version --json gives {name,goos,go,release,assets[name,version,sha256,embedded]}. dev env prints export lines (KUBECONFIG, SOCIALWEB_DEV_PORT_KCP_A); --json includes SOCIALWEB_KCP_HOST. dev ls prints JSON ([] when empty); an instance not up exits 1 "dev up first". A release binary missing assets exits 2 and names them. - id: internal/assets/TestEveryEmbeddedAssetExtractsAndVerifies kind: wire source: internal/assets/release_test.go asserts: > Release build: every embedded asset of the host platform extracts, every pinned executable is an executable file in a tree whose digest equals the pin. Now the release-only verb `socialweb-computer dev verify-assets`, run by deploy/check.sh --local and --full. - id: api/v1alpha1/Test* kind: wire source: api/v1alpha1/types_test.go, types_marketidentity_test.go asserts: > status.nodeExpiry absent = unknown, 0 = unlimited, else a unix second; the Ready phase is the literal "Ready"; Cluster, Node, NodeClaim, AtprotoIdentity, MarketIdentity and their lists are registered under socialweb.computer. - id: internal/apigen/Test* kind: wire source: internal/apigen/apigen_test.go asserts: > CRD required fields follow omitempty/optional (NodeClaimSpec requires cluster, nodeName, ownerDid, apiserverPort, advertiseAddress, tokenFile, nodePasswordFile, serverCAFile; RecordRef uri and cid). APIResourceSchema name v1alpha1-N..socialweb.computer; a changed spec under an old N is refused (bump Revision). A workspace bound to v1alpha1-1 keeps new fields after the export is re-pointed to v1alpha1-2 (live kcp; the v1alpha1-1 NodeClaim schema was kept in testdata/apiresourceschema/). - id: internal/clicfg/Test* kind: wire source: internal/clicfg/clicfg_test.go asserts: > Precedence flag > SOCIALWEB_ env > config.json (path from CONFIG_PATH_SOCIALWEB_COMPUTER) > default; an empty env value still wins; booleans are bare flags; numbers are checked. - id: internal/ccmmain/TestLoad* kind: wire source: internal/ccmmain/config_test.go asserts: > Env SOCIALWEB_CLOUDCONTROLLERMANAGER_{TENANT_KUBECONFIG,CLUSTER,INSTANCES_FILE} required, {MAX_NODES,PODS_PER_NODE,RESYNC} positive; the k3s admin kubeconfig and static bearer tokens are refused. - id: internal/tenantcredentialmain/Test* kind: wire source: internal/tenantcredentialmain/config_test.go, main_test.go asserts: > Env SOCIALWEB_TENANTCREDENTIAL_{STATE_DIR,CLUSTER,SERVER (https only),COMMON_NAME,ORGANIZATIONS, OUT_DIR}, optional VALID_FOR; writes ca.crt, tls.crt, tls.key mode 0600, never overwrites; leaf CN and O as requested; stdout one summary line without key material. - id: internal/devinstance/Test* kind: wire source: internal/devinstance/devinstance_test.go asserts: > State root: option, else $TASK_STATE_DIR/socialweb-computer, else $XDG_STATE_HOME/socialweb-computer, else $HOME/.local/state/socialweb-computer; instance ids prefixed socialweb-comput-; socket paths under 108 bytes; dev refuses uid 0. - id: internal/guestseed/Test* kind: wire source: internal/guestseed/guestseed_test.go asserts: > cloud-init NoCloud seed: FAT16 labelled "cidata" with user-data, meta-data and optional network-config; readable by fsck.fat, blkid (TYPE=vfat) and mtype; byte-identical for one input. - id: internal/guestrootfs/Test* kind: wire source: internal/guestrootfs/guestrootfs_test.go asserts: > Guest rootfs has no ssh host keys, cloud-init state or machine-id; cloud.cfg drop-in growpart auto ["/"], resize_rootfs true, ssh_deletekeys true; tar byte-identical across builds with mtimes clamped to SOURCE_DATE_EPOCH. - id: internal/tenantnet/Test*+internal/providermain/TestTheTenantAddOnsApply* kind: wire source: internal/tenantnet/tenantnet_test.go, internal/providermain/podnetwork_test.go asserts: > Per-node DNS DaemonSet (hostNetwork, dnsPolicy Default, node label selector), Corefile on 169.254.20.10 (health :8080, ready :8181, kubernetes cluster.local, forward . /etc/resolv.conf). Add-ons are applied as application/apply-patch+yaml under one field manager, node-reader RBAC first, then the DNS objects in kube-system. - id: internal/eso/Test* kind: wire source: internal/eso/eso_test.go, beside_test.go, secret_body_test.go asserts: > ClusterSecretStore spec.provider.vault {server, caBundle, auth.cert} names the client secret; ExternalSecret reads k3s/eso-demo; webhook clientConfig.url https://127.0.0.1:/validate-external-secrets-io-v1-{clustersecretstore,externalsecret,secretstore}, no service, caBundle the minted CA, failurePolicy Ignore; the webhook cert verifies for that URL. - id: internal/vaultcert/Test*+internal/rootvault/Test* kind: wire source: internal/vaultcert/*_test.go, internal/rootvault/rootvault_test.go asserts: > Vault TLS CA stable across runs; server SANs cover every requested host and rotate when one is missing; server and client EKUs not interchangeable; files 0600, dirs 0700. OpenBao listener requires and verifies client certs (tls_require_and_verify_client_cert true). - id: internal/vaultlayout/Test* kind: wire source: internal/vaultlayout/*_test.go asserts: > OpenBao names shared with guests and the web app: socialweb-nodes/{data,metadata}/, policy node-, role , mount jwt-<12 hex of the issuer> (https://bidder.example.com -> jwt-5860b7149f69); secret/data/k3s/node/, role node-, policy node-secret-; ESO policy is exactly lookup-self read, secret/data/k3s/eso-demo read, revoke-self update; door path secret/data/socialweb-door/claims//secrets-ingress. - id: internal/kcplibs/impl/openbaoclient/Test* kind: wire source: internal/kcplibs/impl/openbaoclient/openbaoclient_test.go asserts: > X-Vault-Token on every request; namespace header only outside root; POST /v1/sys/mounts/pki {"type":"pki"}; PUT /v1/sys/namespaces/ only when absent; 404/403 unwrapped; ip_sans sent apart from alt_names; 429 standby is healthy; BAO_/VAULT_ env cannot change TLS, address or proxy. - id: internal/kcplibs/impl/policyclient/Test* kind: wire source: internal/kcplibs/impl/policyclient/policyclient_test.go asserts: > Policy engine POST /request/create returns {"status":"ok","detail":{"id"}}; in_progress is running; completed with exit_status success/failure maps outcome; verdict read from policy//result.json; an unreachable engine counts as running. - id: internal/kcplibs/impl/kcpstore/Test* kind: wire source: internal/kcplibs/impl/kcpstore/*_test.go asserts: > /clusters//apis/policy.socialweb.computer/v1alpha1/[namespaces//]policyworkflowruns, status by server-side apply on /status under the caller's field manager; token mint POST .../serviceaccounts//token; workspace path from the kcp.io/path annotation of the LogicalCluster "cluster". - id: internal/kcplibs/common/kcp/Test*+internal/kcplibs/common/ref/Test* kind: wire source: internal/kcplibs/common/kcp/kcp_test.go, internal/kcplibs/common/ref/ref_test.go asserts: > Service name ...svc.kcp.local (root:acme:prod -> prod.acme); cluster URL /clusters/. - id: internal/kcplibs/common/listeners/Test*+internal/kcplibs/factory/servicenames/Test* kind: wire source: internal/kcplibs/common/listeners/listeners_test.go, internal/kcplibs/factory/servicenames/*_test.go asserts: > Listener report {"listeners":[{name,kind tcp|unix|relay,address}]}; wildcard binds become 127.0.0.1; port 0, relative unix paths and duplicates dropped. Workload env SERVICE_ARGS, SERVICE_ENV, KCP_DNS_TABLE, KCP_DNS_TABLE_FILE (/runs/.kcpdns/table.json), KCP_TOKENS, KCP_SERVICE_DOMAIN, KCP_NAMESPACE, KCP_SHIM; a port-0 manifest advertises nothing until bound. - id: internal/kcplibs/common/denospec/Test* kind: wire source: internal/kcplibs/common/denospec/denospec_test.go asserts: > Deno argv --allow-*/--deny-* bare or =a,b, --ignore-env=, --allow-scripts=; commas or empty values inside an entry refused. - id: internal/kcplibs/impl/metrics/Test* kind: wire source: internal/kcplibs/impl/metrics/metrics_test.go asserts: > GET /metrics Prometheus text with kcp_reconciles_total counter, kcp_queue_depth gauge, kcp_reconcile_seconds summary, each family described once. - id: internal/kcpserver/Test*+internal/kcpdatastore/Test* kind: wire source: internal/kcpserver/*_test.go, internal/kcpdatastore/kcpstore_test.go asserts: > kcp argv: start, leader election, the WorkspaceMounts gate, --secure-port=0, --experimental-bind-free-port, --etcd-servers=http://127.0.0.1:, --bind-address=127.0.0.1, --shard-base-url=; readiness GET /readyz with the shard-admin bearer on the slot port; kcp-slots.json persisted before the front flips. kine on sqlite (or postgres for the market profile) listening only on a unix socket, --metrics-bind-address=0; store backed up (with -wal/-shm) before an asset-changing restart. - id: internal/cloudprovider/Test* kind: wire source: internal/cloudprovider/*_test.go asserts: > Node spec.providerID socialweb:///; labels node.socialweb.computer/cluster and /instance-type; unknown or draining instance deletes the Node; nodes.json is a JSON array of {nodeName,cluster,addresses,instanceType,draining}, empty "[]", absent or malformed deletes nothing. - id: internal/clusterprovider/Test*+internal/identitybundle/Test* kind: wire source: internal/clusterprovider/*_test.go, internal/identitybundle/identitybundle_test.go asserts: > The k3s identity bundle (13 files) lands under /server/tls byte- and mtime-exact and persists per cluster; kube-scheduler config 0600 only for market clusters; apiserver probe 200 and 401 reachable, 500 and 503 not. - id: internal/subnetallocator/Test*+internal/instance/Test* kind: wire source: internal/subnetallocator/*_test.go, internal/instance/writefile_test.go asserts: > networks.json {"version":1,"pool","leases":[{cluster,block,allocatedAt,appInstance?}]}, 0600 in a 0700 dir; another pool, duplicates or unparseable refused. The k3s token file holds exactly the raw token, mode 0600 at open. - id: internal/install/Test*+internal/foldmain/Test* kind: wire source: internal/install/*_test.go, internal/foldmain/foldmain_test.go asserts: > /etc/subuid and /etc/subgid ranges above existing entries; user@.service.d socialweb-delegate.conf; apparmor socialweb-pasta; config.json {"host-backend":"systemd-user", "state-dir"}; the unit sets CONFIG_PATH_SOCIALWEB_COMPUTER and ExecStart /bin/current/socialweb-computer serve gateway kcp; bin/current and bin/previous point at bin/; members re-exec as " internal ". - id: internal/hostsvc/Test* kind: wire source: internal/hostsvc/systemd_linux_test.go asserts: > Transient units: one ExecStart, CollectMode inactive, KillMode control-group, MemoryMax with MemorySwapMax=0, OOMPolicy stop, TasksMax; fd-store units FileDescriptorStoreMax=1, NotifyAccess=main; names prefixed socialweb-; systemd older than 253 refused. - id: internal/sweepkcp/Test* kind: wire source: internal/sweepkcp/sweepkcp_test.go asserts: > The sweep worklist is the atprotoidentities with spec.scope Account; the label socialweb.computer/sweep=exempt excludes one. - id: internal/nodemarket/Test* kind: wire source: internal/nodemarket/*_test.go asserts: > NodeClaim contract with the web app: the provider writes spec only, never status.phase; Joined is Joining or Running with secrets fetched; Failed with rfpUri and no acceptUri is a market decline; Lost is an end, not adoptable; status.message passes through; node policy and secret are written before the claim, which waits for both ClusterLinks to listen. - id: internal/appcli/TestTheRenderer* kind: wire source: internal/appcli/identity_test.go asserts: > CLI rendering of MarketIdentity status: AwaitingScan shows pendingAction url once and a QR; Ready prints the handle; AwaitingApproval y/n approves or rejects through spec; SessionExpired says sign in again; Failed returns status.message. - id: plan-0026-A1 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A1 actors: [prod gateway, hat-7] asserts: > Tenant k3s cluster scales from zero to N and back to zero through the prod gateway with the hat-7 oauthQr bidder; firehose and relay events observed live. - id: plan-0026-A2 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A2 actors: [this host, hat-9, hat-7, prod gateway] asserts: > Pod anti-affinity places one VM per host; pausing each winner moves the next VM to the last unpaused bidder; paused winners still release their VMs at scale to zero. - id: plan-0026-A3 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A3 actors: [prod gateway] asserts: > Gateway AtprotoRecord association is Ready and its CID matches the PDS record; removing its ComponentLink makes it fail with LinkDenied. - id: plan-0026-A4 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A4 actors: [hat-7, hat-9, this host] asserts: > A bidder restart keeps its event sources and the root stays Ready. - id: plan-0026-A5 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A5 actors: [prod gateway] asserts: > A gateway restart keeps the requester association; requester bids are not denied. - id: plan-0026-A6 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A6 actors: [this host] asserts: > A component created in a root: workspace after core start is reconciled within one watch event; no reconcile waits for a resync. - id: plan-0026-A7 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A7 actors: [this host, prod gateway] asserts: > socialweb-computer dev links prints the full link graph; the bus deny counter stays zero across A1 to A6. - id: plan-0026-A8 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A8 actors: [this host] asserts: > A rotated OpenBao CA and leaf certificate are used without a restart. - id: plan-0026-A9 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A9 actors: [this host, prod gateway] asserts: > Tenant clusters work around the kcp mount on kcp main: a ServiceAccount-token kubeconfig and a client-certificate kubeconfig both get and watch through /clusters/root:::clusters; a revoked certificate gets 401 and a revoked DID bearer 403; caddy validate accepts the @tenant matcher; a stopped or deleted cluster answers 503 within one gate change and cuts open watches; shut and reap follow the Cluster's own state, never a 502; a Ready cluster with its apiserver down is not reaped; revocation deletes the did- ServiceAccount through the direct path and defers a stopped one; kcp mount-proxy logs show no tenant traffic. - id: plan-0026-A10 kind: acceptance source: docs/plans/0026-abc-control-plane.yaml acceptance_matrix A10 actors: [this host, hat-7, prod gateway] asserts: > W1 wire changes hold against the live counterparties: every XRPC call sends Accept application/json; a bidder whose service-auth re-mint fails returns the first 401/403 without an unsigned retry; custody DPoP calls omit an empty DPoP header and fail when custody returns no access token; requester market and vaultaccess reject an oversized 2xx body; requestCrawl bodies are accepted by the relay; an expired ingress nonce is refused; OpenBao logins are shared by concurrent callers and renew before expiry. - id: TestABidderPolicyDefaultTangledVouch kind: integration source: owner order 2026-10-30 (bidders default to bidder-tangled-vouch; open is opt-in only) actors: [bidder, requester, policy engine, kcp] setup: > Own network: --bidders 1, no gateway, the default bidder policy (the market network profile names none, so bidder-tangled-vouch). One run request --network whose network vouches are not Applied yet (a requester outside the bidder operator's vouch graph), then the same with dev up --bidder-policy open. asserts: > With spec.policy unset the Bidder component's spec.policy is bidder-tangled-vouch, its status.guestNetwork.hostUDPReason is "policy not only-me", and the bidder publishes no bid for the unvouched requester (the scope policy denies it). With spec.policy open the same request gets a bid. open never runs unless a spec or flag names it. cleanup: Release the contract; dev down the network. - id: prod-bidder-tangled-vouch kind: acceptance source: docs/acceptance/0001-auth-modes.md "Bidder policy"; docs/plans/0034-webrtc-guest-transport.yaml ACC34-UNCHANGED actors: [hat-7, hat-9, this host, alice, bob, john] asserts: > Every prod bidder's Bidder reports spec.policy bidder-tangled-vouch and its config.json names bidder-policy bidder-tangled-vouch. The acceptance requester alice (did:plc:lpfuqerea3deuoyrn7ojser4) is vouched by the bidder operators bob (did:plc:ocnuqjlzgohypj3zoznermms) and john (did:plc:5svqtrhheairglgiiyvutzik) with an sh.tangled.graph.vouch record whose rkey is alice's DID, and alice vouches both; every acceptance run request from alice gets a bid under that policy. - id: TestADeletedContractReleasesItsGuest kind: integration source: docs/plans/0028-computecontract-delete-releases.yaml ACC28-DIRECT-DELETE, ACC28-DELETE-BEFORE-ACCEPT, ACC28-DELETE-WHILE-STOPPED, ACC28-BOUND, ACC28-FORCED-REMOVAL actors: [requester, bidder, kcp, pds, relay, VM host systemd user units] setup: > Own network: --bidders 1, no gateway. `run request --network --name dd<6 hex> --hold --exec 'sleep 3000'` until the contract in root: is Held (the requester keeps running while the ssh session lasts). Each case then deletes the ComputeContract with `kubectl delete --wait=false` and reads the contract, the requester core log, the requester repo (com.atproto.repo.listRecords of com.publicdomainrelay.temp.compute.events.vm.delete), the bidder's Bid, ComputeVM and FirecrackerVM, the VM user unit and the VM work dir. asserts: > Every ComputeContract the requester runs carries the finalizer computecontract.socialweb.computer/release; no Bid or ComputeVM does. cleanup: Interrupt the run request; standard teardown; no guest, VM unit or VM work dir remains. cases: - "direct delete of a Held contract: it stays Terminating with the finalizer, goes Releasing, then Released; the repo holds exactly one vm.delete record for it; within 3m the ComputeVM, FirecrackerVM, VM unit and VM work dir are gone and the Bid is Terminated; then the contract object is gone" - "delete while the contract collects bids (spec.denyBidderDids names every bidder): the object is gone within one watch event of the delete; no accept and no vm.delete record exists for it; no ComputeVM appears on any bidder in the next 60s" - "delete while the requester is down (spec.apps[].stopped of the MarketNetwork): the contract stays Terminating with the finalizer; once the requester runs again it sends exactly one vm.delete, the guest is gone within 3m, then the object is gone" - "release bound: the winning bidder is stopped and spec.ttlSecondsAfterFinished puts the release deadline a few minutes ahead; the deleted contract retries the vm.delete (Releasing with a growing attempt count), ends Failed with \"vm.delete was not acknowledged by (the contract TTL): \" at the deadline, then the object is gone" - "forced removal: a Held contract is deleted and its finalizer removed by a JSON merge patch before the release ends; the requester logs requester_contract_gone_unreleased once and no further line for the contract" - id: TestADeletedRequesterRootReleasesItsGuests kind: integration source: docs/plans/0028-computecontract-delete-releases.yaml ACC28-ROOT-DELETE actors: [requester, bidder, kcp, pds, relay, VM host systemd user units] setup: > Own network: --bidders 1, no gateway. `run request --network --name rd<6 hex> --hold --exec 'sleep 3000'` until Held, then a second ComputeContract with the same spec is created in root: and held. Then `kubectl delete marketrequester app --wait=false` in root:. asserts: > The root's status reads Deleting "2 ComputeContract(s) releasing", then Deleting " component(s) stopping"; both contracts are Released and both guests (ComputeVM, FirecrackerVM, VM unit, VM work dir) are gone on the bidder before the Requester component is deleted; the requester repo holds exactly one vm.delete record per contract; the root object (by uid) is gone; no guest is left. cleanup: Interrupt the run request; standard teardown. - id: TestAGuestHttpsTunnel kind: integration source: docs/plans/0032-guest-shell-tunnels.yaml ACC32-TAGGED-EVENT, ACC32-STATUS-NETWORKS, ACC32-HELD-UNCHANGED, ACC32-RELEASE (https only after plan 0034 W0) actors: [requester, bidder, kcp, pds, relay, VM host systemd user units, the opentunnel server] setup: > Own network: --bidders 1, no gateway; the guest has outbound HTTPS. `run request --network --name ht<6 hex> --hold --tunnel web:8080 --exec ''` until Held, then until status.contract.networks names web. asserts: > The contract is Held with an iroh vmAddress before any tunnel is up. On the guest setup-guest-binaries and guest-tunnel-web are active; the journal of setup-guest-binaries names opentunnel installed with the sha256 of the bidder's socialweb-guest-downloads sidecar, fetched from http://192.0.2.1:/v1/downloads, and the guest made no request to github.com or ghcr.io. The bidder's repo holds a signed market.event whose vm.onNetwork payload has tag web, kind https and an https address; the contract's status.contract.networks holds {tag: web, kind: https, address, at, event}. Over HTTPS the address answers GET / with 200 and the nonce body. cases: - "a --tunnel entry other than :[:][:shell[=]] or ::, or a transport other than opentunnel|webrtc: run request fails before any RFP" - "a contract without spec.tunnels: Held as before, no networks, no tunnel unit and no setup-guest-binaries in its user_data" cleanup: > run request release ; the ComputeVM, FirecrackerVM and VM unit are gone and the address no longer answers; standard teardown. - id: TestAGuestShellTunnel kind: integration source: docs/plans/0032-guest-shell-tunnels.yaml ACC32-SHELL, ACC32-TAGGED-EVENT, ACC32-STATUS-NETWORKS, ACC32-HTTPS-AUTH, ACC32-HELD-UNCHANGED, ACC32-RELEASE; docs/plans/0034-webrtc-guest-transport.yaml (wootty back as the fallback shell, served from the bidder) actors: [requester, bidder, kcp, pds, relay, VM host systemd user units, the opentunnel server, socialweb-guest-agent] setup: > Own network: --bidders 1, no gateway; the guest has outbound HTTPS. A secrets bundle with one entry {"path": "/etc/wootty/wootty.env", "value": "WOOTTY_AUTH_TOKEN=<32 base64url characters>\n"}. `run request --network --name sh<6 hex> --hold --secrets --tunnel wootty:7681:opentunnel:shell --tunnel ssh:22:webrtc --exec 'sleep 1500'` until Held, then until status.contract.networks names wootty and ssh. asserts: > The contract is Held with an iroh vmAddress before any tunnel is up. On the guest the journal of setup-guest-binaries names opentunnel, wootty and socialweb-guest-agent installed with the sha256 of the bidder's socialweb-guest-downloads sidecars, fetched from http://192.0.2.1:/v1/downloads, and the guest made no request to github.com or ghcr.io; the env file is root:root 0600, and guest-tunnel-wootty, wootty-wootty and socialweb-guest-agent are active. status.contract.networks holds {tag: wootty, kind: https, address: } and {tag: ssh, kind: webrtc, address: "sha-256 "}. Over HTTPS the wootty address answers 401 on /api/sessions without the cookie and on /api/auth/bootstrap with a wrong bearer, 204 and a wootty_auth cookie with the token, 200 on /api/sessions with the cookie and 200 with the wootty page on /. `run request ssh --transport webrtc --exec 'uname -a'` prints Linux. The token is in no kcp object, record or log line. cases: - "a shell entry without spec.secretsRef: the contract is Failed before any RFP" - "a shell entry on transport webrtc: run request fails before any RFP" - "a token shorter than 32 characters: wootty- does not start and the URL serves no shell" cleanup: > run request release ; the ComputeVM, FirecrackerVM and VM unit are gone and the address no longer answers; standard teardown. - id: TestAGuestWebRTCSSH kind: integration source: docs/plans/0034-webrtc-guest-transport.yaml ACC34-CLI-SSH, ACC34-HANDSHAKE, ACC34-DOWNLOADS actors: [requester, bidder, kcp, pds, relay, VM host systemd user units, socialweb-guest-agent, stun.cloudflare.com] setup: > Own network: --bidders 1, no gateway; the guest and the host reach stun.cloudflare.com:3478 over UDP. `run request --network --name wr<6 hex> --hold --tunnel ssh:22:webrtc --transport webrtc --exec 'uname -a; id'` until it exits, then `run request ssh --transport webrtc --exec 'uname -a; id'` twice: once while the invocation's kcp still runs and once after it stopped. asserts: > The contract is Held on its iroh vmAddress and host keys. On the guest setup-guest-binaries installed socialweb-guest-agent from http://192.0.2.1:/v1/downloads with the sidecar's sha256, and socialweb-guest-agent is active. The requester holds an inbound ContractEvent for the guest's guest.publicKey of tag ssh and status.contract.networks holds {tag: ssh, kind: webrtc, address: "sha-256 "}. Every ssh run prints Linux and uid= with StrictHostKeyChecking=yes against the reported host keys and exits 0; the proxy logs the datachannel open. Every guest.signalWebRTC record in the requester's and the bidder's repo, and every signal ContractEvent, holds only age armored ciphertext and names no recipient; no record, ContractEvent, StateSync or log line of either side holds "a=candidate", an a=fingerprint next to an IP address, or AGE-SECRET-KEY. The Held to shell time is recorded (2.16 s fresh and about 7 s after a kcp restart on the local bidder, 2026-10-24). cases: - "an answer whose a=fingerprint differs from the onNetwork address: the proxy closes the session and ssh fails; no bytes reach the guest port" - "two proxy sessions on one tag at once: each opens only its own answer and both shells work" - "--transport iroh on the same contract: ssh over iroh works as before" cleanup: > run request release ; the contract is Released, the inbound and outbound ContractEvents are gone with it, and no ComputeVM, FirecrackerVM or VM unit is left; standard teardown. - id: TestAGuestEventSubscription kind: integration source: docs/plans/0034-webrtc-guest-transport.yaml W2-GUEST-EVENTS-IN, W2-GUEST-SUBSCRIBE, W2-GUEST-EVENTS-OUT actors: [requester, bidder, kcp, pds, relay, VM host systemd user units, a guest-side WebSocket client] setup: > Own network: --bidders 1, no gateway. A held contract with a webrtc tunnel. Before the client connects, an outbound guest.publicKey ContractEvent is created in the requester workspace and reaches Submitted. Then a client on the guest, with the bearer of guest-login.sh token , opens GET /xrpc/com.publicdomainrelay.temp.compute.guest.subscribeEvents?cursor=0 on the bidder's local GuestControlPlane base, and a second outbound ContractEvent is created. asserts: > The client receives both events as JSON text frames {$type: com.publicdomainrelay.temp.compute.guest.subscribeEvents#event, seq, uri, cid, record}, in seq order, and an empty binary keepalive within 30 s of silence. A replayed submitEvent of the same market.event answers 200 and adds no entry. Reconnecting with the last seq receives nothing old; a cursor past the last seq gets a FutureCursor error frame and a close. A guest.publicKey the guest POSTs to /v1/events {acceptUri, acceptCid, record} answers 2xx and appears in the bidder's repo as a signed market.event bound to the receipt and in the requester as an inbound ContractEvent. cases: - "no Authorization header (a bearer in a token query parameter only): the upgrade is refused 401 AuthenticationRequired" - "the bearer of another ComputeVM: it sees only its own queue" - "a guest.* payload from a DID other than the accept author: refused, nothing queued" - "the ComputeVM is released: the stream closes going away and the queue file is gone" - "POST /v1/on-network: 404" cleanup: run request release ; standard teardown. - id: TestAGuestHostLoopbackFence kind: integration source: docs/plans/0034-webrtc-guest-transport.yaml (guestnet host-loopback fence, 17945ac8) actors: [bidder, VM host systemd user units, socialweb-vmexec, pasta] setup: > Own network: --bidders 1, no gateway. A held guest whose ComputeVM spec.hostPorts lists the local GuestControlPlane port. From the guest, TCP connects to 192.0.2.1 on that port, on the kcp front port, on the bidder OpenBao port and on another local Exposure port; a UDP send and a ping to 192.0.2.1; a TCP connect to the namespace's own gateway address. asserts: > Only the listed port connects; every other TCP connect to 192.0.2.1 is reset at once; UDP, ICMP and the namespace's own address get nothing; outbound internet (stun.cloudflare.com, the opentunnel server) still works. cases: - "the nf_tables table cannot load in the guest namespace: the VM does not start" - "a ComputeVM with no spec.hostPorts: no TCP port of 192.0.2.1 connects" cleanup: run request release ; standard teardown. - id: PolicySpecHarness kind: fixture source: internal/policyengine/policytest actors: [policy engine, deno sandbox, fake PDS, trust resolver] setup: > Deno from SOCIALWEB_POLICYTEST_DENO, the embedded deno (embed_deno), the build-staged pinned zip ~/.tmp/socialweb-computer-build/cache/deno-embed/.zip (extracted to /policy_engine/deno-embedded/), or PATH; none fails the suite. A fake PDS and PLC answer the atproto client in memory. The sandbox points every proxy at 127.0.0.1:9 and can be set unfenced, unavailable or hung. An in-process policy.Runner runs each PolicyWorkflowRun through the RunReconciler and keeps its spec and final status. Trust cases (policytest.TrustCases): unvouched, vouched by self, vouched by a verified or a self-claimed self operator, verified or forged counterparty operator, a claim for the other role, a direct vouch of an operated counterparty, a reverse vouch only, mutual operator vouches, denounced, stranger vouches, same verified operator, counterparty operated by self, forged same operator, different operators, and a contract-scoped association held, expired, released or terminated. asserts: the fixtures are deterministic; no case reads the network. cleanup: each case has its own harness; nothing outlives the test. - id: TestTrustPolicyMatrix kind: policyspec source: internal/policyengine/spec/trust_test.go actors: [bidder, requester, policy engine] setup: > Per policy (tangled-vouch, only-me, mutuals, open) x perspective (bidder, requester) x lane (scope, full) x trust case, a fresh harness and one run of - (or open) with self and the counterparty of that perspective. asserts: > The workflow ran to completion and its action logged a verdict; the action verdict and the decision match: tangled-vouch allows self, a counterparty whose verified operator is self or self's operator, and a counterparty or its verified operator vouched by self or self's verified operator; only-me allows only a counterparty operated by self or self's verified operator, contract-scoped only while the contract is held and unexpired; mutuals also needs the vouch back; open allows. A self-claimed operator, a forged mirror, a reverse vouch alone, a claim for the other role, a denounce or a stranger's vouch never counts. Both lanes give the same verdict. cases: - "matrix////" - "red on rc125: PT-1 reverse vouch admits in scope; PT-2 mutuals scope admits one-way; PT-3 full denies a direct vouch of an operated counterparty; PT-4 scope denies a counterparty operated by self; PT-6 a counterparty of self's own operator is denied" cleanup: none. - id: TestAlwaysRunOpen kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [bidder, requester, policy engine] setup: The open workflow in both lanes and both perspectives. asserts: open runs its action in the sandbox and succeeds before it allows; it never skips. cleanup: none. - id: TestWorkPolicies kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [bidder, requester, policy engine] setup: under-4-cpus with no demand, an RFP without payload and an unreadable payload; bid-payload on a free and a paid bid. asserts: > under-4-cpus allows without a demand and denies "failed to resolve demand payload" when the payload cannot be read; bid-payload allows a free bid and denies a paid one by default. cleanup: none. - id: TestStrictOutputs kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [policy engine] setup: Inline workflows in both lanes - literal allow, no allow output, allow "yes", one job of two denying, a run step. asserts: > Only the literal allow allows. No allow output denies "no allow output", a non-boolean allow denies, any job's allow=false denies with its violation, a run step is refused net-only. cleanup: none. - id: TestRecordTypesFailClosed kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [policy engine, PDS] setup: A PolicyWorkflow whose recordRef names a gha-lite, a typescript, a denoWorker and an unknown record. asserts: > The gha-lite record runs and allows; the others fail before any code runs (empty console) with "typescript policy records are disabled", "denoWorker policy records are disabled" or "unknown policy record type", and deny. cleanup: none. - id: TestCacheKeyOnlyWhenDeclared kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [policy engine, run client] setup: Inline workflows that do and do not declare the cache-key input; open for two counterparties and two lanes. asserts: > The run client always sends runs.CacheKey; a declaring workflow sees a key that starts with it, a non-declaring one sees none. Other parties or another lane get another key. cleanup: none. - id: TestFailClosed kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [policy engine, deno sandbox, trust host, PDS] setup: > bidder-tangled-vouch and open, both lanes; a vouched control allows first, then the sandbox is unfenced, unavailable or hung past a 2s deadline, the trust host errors, the PDS is down, the run cannot be created, or the run is cancelled mid-flight. asserts: > Each denies with its cause (enforces no fence, unavailable, deadline, the trust host error, could not be created, cancelled); a PDS outage denies tangled-vouch and leaves open allowed. cleanup: none. - id: TestNoTrustSnapshotDenies kind: policyspec source: internal/policyengine/spec/engine_test.go actors: [bidder, requester, policy engine] setup: > No trust host, so no trust/ snapshot is seeded, while the PDS holds an association and vouches both ways that would allow; tangled-vouch, mutuals, only-me and open, both lanes and both perspectives. asserts: The action runs; the trust policies deny in both lanes; open still allows. cleanup: none. - id: TestEngineVerdictCache kind: policyspec source: internal/policyengine/spec/cache_test.go actors: [policy engine, trust resolver] setup: bidder- and requester-tangled-vouch repeated with the same and with other inputs. asserts: > A repeated decision still runs and is served from the verdict cache with the uncached verdict; another counterparty, another RFP or another policy body never hits; after a vouch is deleted and the trust cache invalidated the next decision denies; a deny stays a deny; a verdict older than 30s is not served; a trust TTL of 10s serves a deleted vouch until it expires, then denies. cases: - "red on rc125: PT-5 full lane serves a stale allow after the vouch is gone" cleanup: none. - id: TestBidderDecisionSites kind: policyspec source: internal/market/bidder/policy_spec_test.go actors: [bidder, policy engine, PDS] setup: > The bidder's policyEvaluator and fullInput as validateBid (bids.go) and acceptContract (adapters.go) call them: own policy unset, bidder-tangled-vouch, bidder-only-me or open, with and without an attached requester-tangled-vouch record in the requester's repo, over every trust case. asserts: > always-run/bidder-//attached-/: the scope run of the own workflow ran and answered right; a scope deny denies with no full run; otherwise the full run of the own workflow and of every attached record ran and answered right, and the decision allows only if all allow. Unset spec.policy runs bidder-tangled-vouch. cleanup: none. - id: TestBidderAttachedPoliciesFailClosed kind: policyspec source: internal/market/bidder/policy_spec_test.go actors: [bidder, policy engine, PDS] setup: A vouched requester whose RFP attaches a denying workflow, a typescript record, a missing record or a record whose cid changed. asserts: The own scope and full runs ran and allowed; the bid is denied with the attached record's cause. cleanup: none. - id: TestBidderFailClosedWithoutEngine kind: policyspec source: internal/market/bidder/policy_spec_test.go actors: [bidder, policy engine] setup: No run client, an unfenced sandbox, or an attached workflow with no allow output. asserts: The bid decision denies with a reason. cleanup: none. - id: TestRequesterDecisionSites kind: policyspec source: internal/market/requester/policy_spec_test.go actors: [requester, policy engine, PDS] setup: > A requester (requester.New) whose contract policy is unset, requester-tangled-vouch, requester-only-me, requester-mutuals or open, gated by its own bundled policy record, with one bid over every trust case. asserts: > always-run/requester-free-winner (runner.go freeWinner), requester-best-bid (bestBid) and requester-session-pick- (sessionBestBid): the gate's scope run ran and answered right; the session pick runs the full policy only after a scope allow and answers right; the bid is picked only when the policy allows. cleanup: none. - id: TestRequesterAttachedPolicyGate kind: policyspec source: internal/market/requester/policy_spec_test.go actors: [requester, PDS] setup: attachedPolicy over records in the requester's and in a foreign repo. asserts: > The requester's own bundled record is the gate; a foreign repo, another body under a bundled name, another policy name, a changed cid and a non gha-lite record are refused; an unnamed contract policy gates on requester-tangled-vouch and refuses an attached open. cleanup: none. - id: TestRequesterDefaultPolicyRuns kind: policyspec source: internal/market/requester/policy_spec_test.go actors: [requester, policy engine] setup: PolicyEvaluator with no policy name, both lanes, an unvouched bidder. asserts: requester-tangled-vouch runs and denies; "", "tangled-vouch" in any case map to requester-tangled-vouch. cleanup: none. - id: TestRequesterRecordedWinnerRelease kind: policyspec source: internal/market/requester/policy_release_spec_test.go (needs policyfix recordedWinnerAllowed) actors: [requester, policy engine] setup: > A recorded contract (winner, attached policy, RFP, bid) checked as a resumed contract (recordedWinnerAllowed) and as a reused held session (heldSessionAllowed): a vouched winner, an unvouched winner, an unfenced sandbox, a run cancelled mid-flight, a run past a 2s wait, and a run the engine refuses to create. asserts: > The attached policy runs for the recorded winner (no run only when the engine refuses it). A completed allow resumes; only a completed deny returns ErrPolicyRejected, which releases; a failed, cancelled, timed-out or uncreated run refuses to resume and does not release. cleanup: none. - id: TestRequesterScopeCache kind: policyspec source: internal/market/requester/policy_spec_test.go actors: [requester, policy engine] setup: bidAllowedWithPolicy twice for one bid, then for another bidder, then after 31s and a revoked vouch. asserts: > A repeated scope decision gives the same verdict and still records an engine run; another bidder runs its own policy and is denied; after the TTL a revoked vouch denies; a deny stays a deny. cases: - "red on rc125: the requester scope cache answers without an engine run" cleanup: none. deleted_fixtures: - internal/eso/testdata/bundle-v2.11.0-subset.yaml (read only by internal/eso tests) - testdata/apiresourceschema/v1alpha1-1.nodeclaims.socialweb.computer.yaml (read only by internal/apigen tests) - testdata/computevm/*.json (ComputeVM valid/invalid fixtures; read by no non-test code)