1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
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 <image> --vm-host-backend
systemd-user --app-values <file: bidder kcp handle {app}.<inst>.bob.test> --ready-timeout 90s
--network-timeout 4m|5m|10m --state-dir <run root>`. 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 <inst> --state-dir <run root>` (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 <account>` when the caller differs) with HOME, XDG_RUNTIME_DIR=/run/user/<uid>, the
user DBUS bus, PATH=/usr/local/bin:/usr/bin:/bin, TMPDIR=<run root>/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 <account> true`
works when the caller differs; SOCIALWEB_IT_ARTIFACTS (default
~/.tmp/swc-it/<utc yyyymmddThhmmss>-<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 <SOCIALWEB_IT_PUBLISH, default /var/tmp/swc-it-<repo dir>>` (15m, build.log) leaves
<publish>/bin/socialweb-computer; SOCIALWEB_IT_RUN_ROOT (default /var/tmp/swcit) exists with mode
1777 and <run root>/<run id> 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 <run root>/<run id> 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 <inst> --name
rb<6 hex> --exec true --contract-timeout 4m --network-timeout 2m --state-dir <run root>` (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 <inst> --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 <inst> --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 <name>
--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 <inst> 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://<root:beta pds service name>/ 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 ws> Bidder/<name> 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/<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 <other bidder DID>` (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 <collection>/<rkey>, 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-<subject>, 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: <case>}; 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":"<case>"; status.cache has a key policy/<case>/... 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: <marker>}, 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/<recordType>/<case>, cid bafyfailclosed<case>} and a PolicyWorkflowRun
of it whose spec.cache entry rec/<uri>/<cid> holds value.json {"$type":"<recordType>"}.
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 <inst>
--name rq<6 hex> --secrets <file> --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 <name>` (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/<name> (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 <inst> --host-backend proc --name sshgw --network-timeout 5m --
--test-owner-sessions-file <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 <owner DID> with LC_SESSION=it-<8 hex> runs hostname. Then `socialweb-computer dev restart
sshgw/ssh-server --instance <inst>` 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.<method>. 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 <inst> --name term<6 hex> --hold
--exec "echo <marker>; 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-<hex of the first 8 bytes of sha256(UID)> 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:<port>. Own network: --bidders 1 --with-gateway, 5m,
gateway option public-origin=<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=<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 <origin>/oauth-client-metadata.json and
redirect_uris containing <origin>/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 <origin>/ 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 <verb> [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 <state-dir>/apps/dev/<instance>-<app>. --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":"<child>"}. 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-<epoch>-<seq> (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-<epoch>-<seq> 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 <instance>-gateway child, opened through the gateway state dir
(clusterprovider/pdr-*-<name>: front.json, server CA, admin client cert). One cycle per
--tenant-every, pod hold --tenant-hold (20s).
asserts: >
default ServiceAccount within 1m; pod soak-<unix>-<seq> (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 <instance>-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 l<lane>c<seq> in the bootstrap account home workspace
(root:<account hash>:<name>) 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:<pod>, core:<subcommand>); 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:<account hash>:<cluster>:<child label>). Each worker lists core v1
namespaces (limit 1) through the kcp front, no client rate limit, 10s timeout.
asserts: >
Prints "requests <n> failed <m>". 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/<NSID>: 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 <plc>/<did>: 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:<hash>:<cluster>: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 <host>/clusters/<path>; 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.<plural>.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_<NAME> 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:<port>/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}/<node>, policy
node-<node>, role <node>, mount jwt-<12 hex of the issuer> (https://bidder.example.com ->
jwt-5860b7149f69); secret/data/k3s/node/<n>, role node-<n>, policy node-secret-<n>; ESO policy is
exactly lookup-self read, secret/data/k3s/eso-demo read, revoke-self update; door path
secret/data/socialweb-door/claims/<claim>/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/<ns> 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 <endpoint>/request/create returns {"status":"ok","detail":{"id"}}; in_progress
is running; completed with exit_status success/failure maps outcome; verdict read from
policy/<name>/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/<path>/apis/policy.socialweb.computer/v1alpha1/[namespaces/<ns>/]policyworkflowruns,
status by server-side apply on /status under the caller's field manager; token mint POST
.../serviceaccounts/<sa>/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>.<ns|default>.<reversed workspace labels below root>.svc.kcp.local (root:acme:prod
-> prod.acme); cluster URL <base>/clusters/<path>.
- 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:<kine>, --bind-address=127.0.0.1,
--shard-base-url=<front>; 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://<cluster>/<node>; 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 <dataDir>/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@<uid>.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
<state>/bin/current/socialweb-computer serve gateway kcp; bin/current and bin/previous point at
bin/<sha>; members re-exec as "<exe> internal <verb>".
- 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:<app> 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 <public
host>/clusters/root:<acct>:<home>: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 <inst> 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 <inst> --name dd<6 hex> --hold
--exec 'sleep 3000'` until the contract in root:<name> 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 <deadline> (the contract TTL): <last error>\" 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 <inst> --name rd<6 hex> --hold
--exec 'sleep 3000'` until Held, then a second ComputeContract with the same spec is created in
root:<name> and held. Then `kubectl delete marketrequester app --wait=false` in root:<name>.
asserts: >
The root's status reads Deleting "2 ComputeContract(s) releasing", then Deleting "<n>
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 <inst>
--name ht<6 hex> --hold --tunnel web:8080 --exec '<an HTTP server on 127.0.0.1:8080 that
answers GET / with 200 and a nonce body>'` 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:<port>/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 <tag>:<port>[:<transport>][:shell[=<envFile>]] or <tag>:<port>:<envFile>, 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 <name>; 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 <inst> --name sh<6 hex> --hold --secrets <bundle>
--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:<port>/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: <https URL>} and
{tag: ssh, kind: webrtc, address: "sha-256 <hex>"}. 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 <name> --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-<tag> does not start and the URL serves no shell"
cleanup: >
run request release <name>; 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 <inst> --name wr<6 hex> --hold --tunnel ssh:22:webrtc --transport
webrtc --exec 'uname -a; id'` until it exits, then `run request ssh <name> --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:<port>/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 <hex>"}. 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 <name>; 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
<audience>, 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 <name>; 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 <name>; 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/<versions.json sha256>.zip (extracted to
<user cache>/policy_engine/deno-embedded/<version>), 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 <perspective>-<policy> (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/<workflow>/<scope|full>/<bidder|requester>/<trust case>"
- "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/<counterparty> 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-<rfp-to-bid|accept>/<mode>/attached-<none|policy>/<case>: 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-<free|paid> (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)