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
plan: 0011-windows-bidder-hcs
paused: >
PAUSED by the user on 2026-10-16: no Windows work until the user resumes it. The M0 spikes on the user's
desktop (hat-9) were running when the machine crashed; the user stopped the work. The cause of the crash is
not known and was not diagnosed. Done: M0-REVISIT only (docs/ARCHITECTURE.md section 15). Every other item is
todo and nothing it produced was committed; its experiments (an HCS VM driver, an HNS NAT network, a port
forwarder, a WSL distro running a Linux market network, temporary services and accounts for HCS identity
probes) were scratch work and their results are lost. The next worker must not start from those results.
Before resuming: the user decides whether the desktop (a dual-boot machine the user also works on) is the host
for these spikes at all, or a separate Windows machine is used; the spikes must run in small, reversible steps,
one at a time, each cleaned up before the next, with no HNS network, forwarding or firewall change left behind
between steps, and the Windows host must never be rebooted or signed out by the worker. What may still be left
on the desktop, to be listed and removed only when the user says so: C:\swc (bin, guest, vms, wsl, image,
versions, kcp-run, bao, probe, socialweb-computer checkout), the WSL distro socialweb-ubuntu and its holder
process, the HNS network socialweb-nat0, the firewall rules socialweb-hcsfwd-guest and socialweb-listen-test,
IP forwarding enabled on vEthernet (WSL (Hyper-V firewall)) and vEthernet (socialweb-nat0), and possibly the
temporary socialweb-hcsprobe-* services, user and scheduled task if a probe was cut short.
written: 2026-10-11
queued: >
QUEUED. Starts only after plan 0006 (docs/plans/0006-socialweb-computer-unified-binary.yaml) and plan
0007 (docs/plans/0007-compute-provider-kcp.yaml) are complete on Linux, every item done with evidence
and their acceptance checks ACC6-* and ACC7-* recorded, and after the plan 0008
(docs/plans/0008-embedded-deno-and-typescript-denopods.yaml) items M1-DENO, M1-TS-CLOSURE,
M2-DENOKCP-FORK, M2-DENOKCP-INPROC, M2-SERVICE-TEMPLATE and M4-MARKET-NETWORK-PORT are done. Then M0-REVISIT
runs first; no other item may move to doing before it is done. Every item is executed ON a Windows
host (decided by the user 2026-10-11); the executor works on that host, not on hat-8.
source: >
The user's decisions of 2026-10-09..11: macOS and Windows bidder work is deferred out of plans 0006,
0007 and 0008 into their own plans (this one for Windows, 0012 for macOS). Plans 0006 and 0007 build the
cross-OS seams on Linux: Go interfaces, GOOS build-tagged files, and per-OS embedded assets compiled in
only for the target OS. Expected seam names (they may differ as landed; M0-REVISIT reconciles): a VM
host/runtime interface (Start, Inspect returning a Verdict Running|Lost|Unknown, Stop with a grace,
List by owner) with one backend per OS; a host-unit/service-manager interface; an OS-neutral
ComputeVM status.runtime {backend, ref, instance, pid?, startTime?}; a content-addressed binary install
versions/<sha256>/; asset dirs runtime/<sha256>/ garbage-collected only when nothing references them.
AS LANDED (M0-REVISIT, ARCHITECTURE section 14 "The Windows bidder: the seams as they landed"): the VM
seam is vmruntime.Runtime (Prepare, Reserve with a Placer, Await, Inspect -> Observation, Shutdown, Stop,
List, Cleanup, Watch, Load, CollectImages); the verdict is hostsvc.Verdict; the service manager is
hostsvc.Manager (unit/argv shaped); status.runtime is {backend, ref=unit, instance=InvocationID, pid,
startTime=ticks, hostBootID, vmmID}; the core installs to bin/<sha>/ (bin/current, bin/previous) and assets
to runtime/<sha256>/. Read every "versions/<sha256>/" below as bin/<sha>/.
Headless servers only. A VM survives a crash or upgrade of the core binary and any logout; a host
reboot makes every VM Lost, replaced through a new RFP. The mechanism is the native one per OS. The
invariant: no socialweb-computer code is in a running VM's process tree; the OS service manager or VMM
owns the VM directly. On Windows that is an HCS compute system created with
ShouldTerminateOnLastHandleClosed=false and no helper process; our code only does one-shot preparation
(disks, network endpoint, the create call) and later opens, inspects and stops the system by id. All
code, docs, architecture and plan items are changeable; the best solution wins.
state_rules:
- TypeScript lives in this repository (decided by the user 2026-10-12): the closure's ten repositories were vendored with their history under ts/<repository>/ and the originals are frozen; a path this plan names under ts/ is changed here, never in the original.
- "AGENTS.md overrides this plan where they differ (amended by M0-REVISIT 2026-10-16): write no unit tests (ignore every test_first field; an offline or livegate test named in an item is a manual live proof or, at the end, an integration test in test/integration), flip an item's status only (the git history is the evidence), put decisions and readings in docs/ARCHITECTURE.md sections 14 and 15."
- "The Windows machine is the user's own desktop and Linux workstation (decided by the user 2026-10-16): it is never rebooted, shut down, hibernated or signed out, not even on request, and nothing that needs a restart is enabled. A proof that needs a reboot is replaced by its closest no-reboot equivalent (the compute system's owner killed, the compute system terminated so Inspect answers Lost) and the reboot-only part is blocked."
- This file is the only state. Every step updates status and evidence.
- status is one of todo, doing, done, blocked.
- An item is done only when its evidence is recorded (commit SHAs, test names watched to fail and
then pass, readings from the Windows host with UTC timestamps, the host's Windows edition and build
named in each reading).
- Open items are mirrored in NEXT.yaml (P11-*).
- A decision marked "decided by Claude, user may veto" was not confirmed by the user; one marked
"decided by the user 2026-10-11" was.
- An item names its dependencies in depends_on; an item may not start while one of them is not done.
Cross-plan dependencies are written <plan>:<item id>.
- >
M0-REVISIT must run before any other item moves to doing, and must be redone (its status back to
doing, every item that depends on it re-checked) whenever plan 0006, 0007 or 0008 changes a seam,
the ComputeVM schema, the asset pipeline, the controller or the install/upgrade flow after
M0-REVISIT's recorded commit SHA.
- >
RFP flow is the spine (org-root CLAUDE.md). Every test or spike that needs a live guest drives the
requester (runComputeContract) against a real local bidder through RFP, bid, accept and cloud-init
user_data. No test, spike or code boots a guest any other way: no hand-made compute system, no
hand-attached VHD, no hand-written authorized_keys. Zero ssh sessions to any guest.
ownership:
owned: >
All code in socialweb-computer-kcp (including the Windows backends and build-tagged files this plan
adds), the forked kcp, deno-kcp and kcp-libs copies plans 0006 and 0008 vendor, and the Windows host
the user provides for this plan. hono-compute-provider and atproto-market may be changed by commit;
never pushed.
not_owned: >
The market protocol records and lexicons, and the core architecture: nodes and guests only ever born
through requester -> RFP -> bid -> accept -> cloud-init user_data, reached only through the relay.
Windows itself (vmcompute, HNS, SCM behaviour) is measured, never patched.
research:
measured_on: >
2026-10-11 on hat-8 (Linux) from this repo at f0460fd (main) and from published documentation only.
NOTHING in this block was observed on a Windows host; every Windows behaviour below is a claim the
M0 items measure. The lifecycle notes this plan was to be written from (scratchpad
lifecycle-windows.md, lifecycle-synthesis.md, lifecycle-linux.md) were not on disk when it was
written; M0-REVISIT re-reads whatever 0006/0007 recorded in their place.
in_repo_today:
- "internal/hostos already has a Windows half: proc_windows.go:23-40 process identity from OpenProcess + GetProcessTimes creation time, :62 Alive, :100 Terminate; lock_windows.go:19 LockFileEx; portrange_windows.go:21 netsh dynamic port range; sysproc_windows.go:18 DieWithParent has no Pdeathsig equivalent"
- "docs/ARCHITECTURE.md:2223-2280 (The host platform): the Windows column is design and cross-compilation only (GOOS=windows go build ./... and go vet clean), no Windows binary has ever run; the gateway-side node substrate on Windows is qemu with -machine q35,accel=whpx:tcg and slirp networking. That is the clusterprovider's own node substrate, not a market bidder, and this plan does not change it."
- "internal/portscan/scan_windows.go:1-15 shells out to netstat -ano"
- "cmd/socialweb-nodeboot/main.go:37-38 the user_data reaches a firecracker guest on a second read-only ext4 disk labelled cidata (cloud-init NoCloud); :457 seed dir /var/lib/cloud/seed/nocloud"
- "cmd/socialweb-nodeinit/main.go:14-16 the guest's PID 1 loads the transport module and waits for /dev/vda; under Hyper-V the SCSI disks arrive through hv_storvsc as /dev/sd*, so the init's device wait is per hypervisor"
- "cmd/socialweb-nodeimage/main.go:42-45 the image builder extracts THIS host's own kernel and module tree; a Windows host has neither, so a Windows bidder's guest kernel and rootfs must be built elsewhere and carried as assets"
- "kcp-libs impl/execrunner/supervisor.go:141 SysProcAttr{Setpgid: true}: the DenoPod runner does not compile for GOOS=windows (syscall.SysProcAttr has no Setpgid there), so plan 0008's forked runner needs a Windows half before run bidder can exist on Windows"
- "atproto-market lib/common/cloud-init-common/mod.ts:110 UserDataModule, :135-153 the registry, :1225-1231 the registered modules; the guest-side modules detect arch with uname -m (:487-494, :739-746, :1101-1108), so an amd64 Windows guest is the arch every module already serves"
- "atproto-market lib/requester-xrpc/mod.ts:881 runComputeContract; ts/atproto-market/test/bidder_container_integration_test.ts:132 the canonical local harness (local dispatcher, fake PLC, bidder, requester)"
- "atproto-market lexicons/com/publicdomainrelay/temp/compute/vm.json:19-45 compute.vm has cpus, mem, disk, network, location, role, user_data and NO arch field"
hcs:
- "HCS ComputeSystem document carries Owner and ShouldTerminateOnLastHandleClosed (hcsshim internal/hcs/schema2/compute_system.go; https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference). hcsshim's own utility VMs set ShouldTerminateOnLastHandleClosed: true (hcsshim internal/uvm/create.go:413) and default Owner to the executable name (:230-246), so the false setting this plan depends on is not the path hcsshim exercises."
- "Chipset.LinuxKernelDirect {KernelFilePath, InitRdPath, KernelCmdLine}, schema 2.2 (https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#LinuxKernelDirect); WSL2 boots its kernel this way and attaches ComPorts 0 and 1 to named pipes (microsoft/WSL src/windows/service/exe/WslCoreVm.cpp, read 2026-10-10)"
- "HcsOpenComputeSystem(id, requestedAccess, &handle) re-attaches to an existing system by id (https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsopencomputesystem); HcsEnumerateComputeSystems takes a query (hcsshim internal/vmcompute: vmcompute.HcsEnumerateComputeSystems), which is how a restarted bidder lists what it owns"
- "HcsGrantVmAccess adds an ACL entry for the VM's per-system virtual account to a file (https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsgrantvmaccess): every VHD and the kernel/initrd must be granted, or the worker cannot open them"
- "HcsCreateComputeSystem's securityDescriptor parameter is reserved and must be NULL (https://learn.microsoft.com/virtualization/api/hcs/reference/HcsCreateComputeSystem); who may call it is decided by the vmcompute service's own access check, which no document states for a Hyper-V Administrators member who is not an Administrator. Podman v6 runs Hyper-V machines for Hyper-V Administrators members after a one-time admin registry setup, but through WMI/VMMS (libhvee), not HCS (https://blog.podman.io/?p=21919, https://github.com/containers/libhvee). Unproven for HCS: M0-HCS-ACCOUNT."
- "hcsshim ext4/tar2ext4 writes an ext4 image from a tar in pure Go and can append a VHD footer (https://github.com/microsoft/hcsshim/blob/main/ext4/tar2ext4/tar2ext4.go), so rootfs and cidata VHDs need no mkfs.ext4 on Windows"
hns:
- "HNS/HCN: networks, endpoints, namespaces and policies through a JSON-schema C API; the service checks the ACL against the caller's token (https://learn.microsoft.com/en-us/windows-server/networking/technologies/hcn/hcn-top, https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema). Network types include NAT and ICS (hcsshim hcn/hcnnetwork.go); flag EnableNonPersistent (8) makes a network vanish at reboot."
- "WinNAT allows a limited number of NAT internal prefixes per host, and WSL2 and Docker Desktop each create one; a collision with an existing prefix is the expected failure (measured in M0-HNS-NETWORK, not assumed)."
windows_services:
- "Virtual accounts NT SERVICE\\<service> need no password and are local managed accounts (https://learn.microsoft.com/en-us/windows/security/identity-protection/access-control/service-accounts); golang.org/x/sys/windows/svc implements a service; golang.org/x/sys/windows/svc/mgr installs one"
- "Job objects with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE terminate every process in the job when the last handle closes (https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects): the Windows equivalent of deno children dying with the bidder"
- "A running .exe cannot be overwritten or deleted on Windows; it can be renamed. versions/<sha256>/socialweb-computer.exe plus an SCM ImagePath change avoids touching the running file."
items:
- id: M0-REVISIT
milestone: M0
title: Re-read what 0006, 0007 and 0008 actually landed and amend this plan to match before anything else
depends_on: []
decision: >
Decided by the user 2026-10-11: everything is changeable and the plan follows what landed, not what
was expected. Before any execution, on the Windows host's checkout: re-read the status and evidence
of every item of plans 0006, 0007 and 0008, docs/ARCHITECTURE.md (at least sections 5, 9 The host
platform, 12, 13), NEXT.yaml, and the code as landed: the VM host/runtime seam (its interface,
Verdict type, Stop grace, List by owner), the host-unit/service-manager seam, the GOOS build-tagged
files and which ones are stubs for windows, the per-OS asset pipeline (internal/assets, how a
windows-only asset is staged and pinned in versions.json), the ComputeVM schema (status.runtime and
its field managers), the ComputeVM controller's re-population rule, the install and upgrade flow
(versions/<sha256>/, GC of runtime/<sha256>/), the DenoPod runner, the market network command and
plan 0006 M0-SIZE-BUDGET's platform matrix (it said darwin carries client modes only and named no
windows target; both are superseded by the user's 2026-10-11 decisions). Then amend THIS file: item
ids, decisions, depends_on (cross-plan ids to the real landed ids), files (real package paths) and
the acceptance checks, so every later item names code that exists. Anything 0006/0007 left
Linux-only that a Windows backend needs (an interface method, a schema field) is written as a new
item here, not patched in silently.
files: ["docs/plans/0011-windows-bidder-hcs.yaml (this file)"]
test_first: >
None (reconciliation). The reading is a diff of this file before and after, plus `GOOS=windows
GOARCH=amd64 go build ./... && go vet ./...` on the landed tree (expected green with stubs) and a
list of every symbol in the seams that has no windows implementation.
done_when: >
This file amended and committed; evidence names the socialweb-computer-kcp commit SHA (and the
hono-compute-provider and atproto-market SHAs) it was checked against, the list of renamed or added
items, and the windows stub list.
risks:
- "If 0006/0007 change after this ran, the state rule sends this item back to doing; the evidence keeps every SHA it was run against."
status: done
evidence: []
- id: M0-HOST-BASELINE
milestone: M0
title: Record the Windows host and run the landed binary's non-VM modes on it
depends_on: [M0-REVISIT]
decision: >
Read and record: Windows edition, version and build (winver, Get-ComputerInfo), CPU virtualization
and SLAT, which optional features are enabled (Microsoft-Hyper-V-All, VirtualMachinePlatform,
HypervisorPlatform, Containers), vmcompute, vmms and hns service states and file versions, whether
WSL2 or Docker Desktop is installed and the NAT prefixes they hold (Get-NetNat), Defender state,
the BitLocker state, and the group membership of the executor's account. Then build the landed tree
for windows/amd64 on this host and run, as an unprivileged user: `version --json`, `check-host`,
kcp (as landed: the release kcp binary behind kine on sqlite, not in-process; neither has a
windows/amd64 asset, so the reading is whether kcp v0.33.0 and kine build for windows/amd64 from
source and run, each on port 0), the embedded deno (0008:M1-DENO) running one fixture entry with
--cached-only, the windows bao release, and one DenoPod through the forked runner (expected: refuses,
runner_windows.go is a stub). Each that fails is an item added here by name. Fallback if kcp or kine
do not build or run on Windows: record the error and add an item that decides between a source
build asset per tool and running the bidder's bootstrap layer elsewhere; decided by Claude, user may veto.
files: ["evidence only; new items appended to this file for every failure"]
test_first: "None (measurement). The reading is each command's output and exit status with UTC timestamps."
done_when: "Host record committed in evidence; each of the five non-VM modes recorded as works or fails-with-item."
risks:
- "Windows Home has no Hyper-V role but has VirtualMachinePlatform (what WSL2 uses); whether HCS VM creation works with only VirtualMachinePlatform is recorded here because it decides which editions the installer accepts."
- "The DenoPod runner (kcp-libs supervisor.go:141 Setpgid) is expected to fail to build for windows; that becomes M1-DENOPOD-RUNNER-WINDOWS, not a blocker for the M0 spikes, which may run the bidder's deno as a plain child in the scratch branch."
status: todo
evidence: []
- id: M0-SCRATCH-BACKEND
milestone: M0
title: A scratch-branch HCS backend behind the landed VM host seam, renting one guest through the RFP flow
depends_on: [M0-HOST-BASELINE]
decision: >
The live spikes need a guest, and a guest is only ever born through the RFP flow (org-root
CLAUDE.md), so this item builds the smallest HCS backend that lets `socialweb-computer run bidder`
on this host win an RFP and boot the guest from its cloud-init user_data. Scratch branch only;
nothing here is merged; M1 items rebuild it test-first. Content: a guest kernel (x86_64, Hyper-V
drivers built in: CONFIG_HYPERV, CONFIG_HYPERV_STORAGE (hv_storvsc), CONFIG_HYPERV_NET (hv_netvsc),
CONFIG_HYPERV_VSOCKETS, CONFIG_SERIAL_8250 console, ext4 built in) built on hat-8 from the kernel
source the Linux image pipeline uses; the rootfs from the Linux image pipeline converted to an ext4
VHD by hcsshim ext4/tar2ext4 on this host; the cidata seed (user-data, meta-data) written by the same
pure-Go path; a compute system document with Owner socialweb-computer-<handle>,
ShouldTerminateOnLastHandleClosed false, Chipset.LinuxKernelDirect, two SCSI attachments, one network
adapter on an HNS endpoint, ComPorts 0 to a named pipe; HcsGrantVmAccess on every file. Driven by
runComputeContract against the market network or, if the market network does not yet run on Windows (M0-HOST-BASELINE),
against the canonical harness shape of ts/atproto-market/test/bidder_container_integration_test.ts with
the bidder pointed at its local dispatcher. The guest reaches onNetwork and the requester's iroh
session answers; zero ssh. As landed the guest image has no initramfs: the scratch guest kernel
boots root=/dev/sda from the rootfs VHD (hv_storvsc built in), the seed is the NoCloud cidata disk of
internal/guestseed, and the fixed guest network of internal/guestnet is its network-config.
files: ["scratch branch only: internal/vmruntime (hcs scratch backend behind vmruntime.Runtime), deploy/guest-kernel/hyperv-x86_64.config, the disk writer; evidence names the branch and SHA"]
test_first: >
The live rent itself: the requester's runComputeContract onNetwork callback fires within the boot
timeout, the guest's cloud-init log (read from the COM1 pipe) shows every module of the user_data
finished, zero ssh sessions. Recorded red first (e.g. the kernel without hv_storvsc built in hangs
waiting for its root) and then green.
done_when: "One guest rented through the RFP flow on this host reached onNetwork and was released with vm.delete 200; boot time and the guest's console log recorded."
risks:
- "The landed guest boots root=/dev/vda with no init of ours; under Hyper-V the root arrives as /dev/sda, so the boot arguments of the hyperv-x86_64 target say root=/dev/sda (the rootfs is the only disk on SCSI controller 0 LUN 0)."
- "If LinuxKernelDirect is refused on this build (schema 2.2), the fallback is UEFI boot from the rootfs VHD with the kernel inside it (WslCoreVm.cpp's other path); recorded and taken into M1-GUEST-IMAGE-HV."
status: todo
evidence: []
- id: M0-HCS-ACCOUNT
milestone: M0
title: Can a non-admin member of Hyper-V Administrators create, open and stop HCS compute systems and HNS endpoints
depends_on: [M0-SCRATCH-BACKEND]
decision: >
Measure, with the scratch bidder of M0-SCRATCH-BACKEND run as each candidate identity in turn:
(a) a virtual account NT SERVICE\socialweb-computer-bidder-<handle> added to Hyper-V Administrators
and nothing else, (b) a plain local user in Hyper-V Administrators, (c) LocalSystem. For each:
HcsEnumerateComputeSystems, HcsCreateComputeSystem (through a real RFP rent), HcsOpenComputeSystem,
HcsShutdownComputeSystem/HcsTerminateComputeSystem, HcsGrantVmAccess, HcnCreateEndpoint and
HcnDeleteEndpoint on an existing network, HcnCreateNetwork. Record each HRESULT. The preferred
outcome is (a) for everything except HcnCreateNetwork, which the installer does once as admin.
Fallback decisions if (a) fails: first, LocalSystem for the bidder service with every deno child
started under a restricted token (CreateRestrictedToken dropping Administrators and SeDebug, a low
integrity level, inside the kill-on-close job) so TypeScript never runs as SYSTEM; second, if HCS is
unusable without admin and LocalSystem is refused by the user, the WMI/VMMS path (Msvm_* through
libhvee's shape) with persistent VMs that a startup sweep deletes after a reboot so reboot = Lost
still holds. Which fallback was taken is recorded and asked of the user (questions_for_user).
files: ["evidence only (HRESULT table per identity)"]
test_first: "None (measurement). The table is the reading: identity x call -> HRESULT, with the UTC time and Windows build."
done_when: "The table is complete for all three identities and the chosen identity is written into M2-INSTALLER-WINDOWS and M2-BIDDER-SERVICE-WINDOWS."
risks:
- "Membership changes need a new logon token: a service must be restarted, a user must log off and on, before a membership reading counts."
status: todo
evidence: []
- id: M0-HCS-SURVIVAL
milestone: M0
title: A compute system with ShouldTerminateOnLastHandleClosed=false outlives its creator; what vmcompute restart does
depends_on: [M0-SCRATCH-BACKEND]
decision: >
Safety first (the machine is the user's own): before (3) and (4) read whether any WSL distribution,
Docker or other HCS system is running; restart vmcompute or hns only when none is, else record the
refusal-to-risk as the reading and block that part. Never a reboot.
With a guest rented through the RFP flow by the scratch bidder: (1) TerminateProcess the bidder
(taskkill /F /PID) and read, every 5 s for 10 minutes, the compute system's State and RuntimeId from
a fresh HcsOpenComputeSystem in another process, plus the guest's onNetwork heartbeat and the
requester's tunnel; (2) repeat with the bidder stopped through SCM; (3) Restart-Service vmcompute
while the guest runs and record whether the system, its RuntimeId and its vmwp.exe survive, and
whether HcsOpenComputeSystem still works after; (4) Restart-Service hns and record the endpoint and
the guest's network. Fallback if (1) fails (the system dies with its creator): the VM's owner becomes
a dedicated, install-time SCM service per bidder whose only code is the create call and a handle
held open (this breaks the 'no socialweb code in the VM's tree' invariant and is asked of the user
before it is built); if (3) kills guests, a vmcompute restart is treated like a reboot (every
ComputeVM Lost) and documented.
files: ["evidence only"]
test_first: "None (measurement). Readings: state/RuntimeId timeline across each event, UTC timestamps."
done_when: "Each of the four events has a recorded outcome; the Verdict mapping for M1-VMHOST-HCS is written from them."
risks: ["vmcompute restart may be refused while systems run; the refusal is the reading."]
status: todo
evidence: []
- id: M0-HCS-IDENTITY
milestone: M0
title: Re-attach by id, list by Owner, and what makes a VM 'the same VM' across a bidder restart
depends_on: [M0-SCRATCH-BACKEND]
decision: >
Measure: HcsEnumerateComputeSystems with a query naming Owners [socialweb-computer-<handle>] returns
exactly this bidder's systems and none of WSL's or Docker's; HcsGetComputeSystemProperties returns
RuntimeId, State and Owner; RuntimeId is stable across a bidder restart and changes if a system with
the same Id is recreated. Decision this item writes (decided by Claude, user may veto):
status.runtime = {backend: hcs, ref: <compute system Id, a GUID the controller chooses and records
before the create call>, instance: <RuntimeId>}; pid and startTime are omitted on Windows because the
worker process is not ours. Verdict: Running iff open succeeds AND Owner matches AND RuntimeId equals
status.runtime.instance AND State is Running; Lost iff open returns 'not found' or RuntimeId differs
or State is Stopped; Unknown on any other error (access denied, vmcompute unavailable), which the
controller retries and never turns into a release.
files: ["evidence only; the Verdict table is copied into M1-VMHOST-HCS's decision"]
test_first: "None (measurement)."
done_when: "Enumerate/open/properties outputs recorded before and after a bidder restart; Verdict table recorded."
risks: []
status: todo
evidence: []
- id: M0-HNS-NETWORK
milestone: M0
title: An HNS NAT network for guests, an endpoint per guest, tenant isolation, and coexistence with WSL and Docker
depends_on: [M0-HCS-ACCOUNT]
decision: >
As landed every guest has the same fixed network (internal/guestnet: guest 172.30.0.2/30, gateway
172.30.0.1, MAC 06:00:ac:1e:00:02, and it dials the constant host address 192.0.2.1); there is no
network index allocator and user_data is final at create. So also measure whether one HNS network per
guest can carry that identical plan (the host side 172.30.0.1 of each guest on its own isolated
network, and 192.0.2.1 reaching the bidder's listeners), or whether the Windows guest needs a
per-backend network-config (a seam change in guestseed/guestnet, to be written as a new item).
Measure: HcnCreateNetwork of type NAT with a /24 the installer chooses (refusing a prefix any
existing Get-NetNat or HNS network holds), with and without EnableNonPersistent; who may create it
(M0-HCS-ACCOUNT's table); an endpoint per guest created at Start and deleted at Stop versus an
install-time endpoint pool; whether two RFP-born guests on the same NAT network reach each other
(tenant isolation) and whether an endpoint ACL policy (HNS ACL policy, Action Block, RemoteAddresses
the network's prefix except the gateway) stops it; which host address a guest dials to reach the
bidder's services (the NAT gateway address; the bidder's onNetwork, OIDC issuer and JSR listeners
must bind it, and pointGuestAtHost from compute-provider-common must name it); and the Windows
Firewall rule needed for guest -> gateway traffic. With WSL2 and Docker Desktop present and running,
record whether our network coexists. Fallback if isolation by ACL fails: one NAT network per guest
(prefix pool), or the ICS network type; recorded.
files: ["evidence only"]
test_first: "None (measurement): guest A -> guest B probe result (from cloud-init runcmd output of RFP-born guests, read on the COM pipe), guest -> gateway result, before and after the ACL."
done_when: "Network creation privilege, isolation result, the guest's host address and the coexistence result recorded; the network decision written into M1-NETWORK-HNS."
risks:
- "A probe between two guests needs both born through the RFP flow, with the probe in their user_data; no ssh into either."
status: todo
evidence: []
- id: M0-COM-CONSOLE
milestone: M0
title: The guest console on a COM-port named pipe, reconnected after the core binary restarts
depends_on: [M0-HCS-SURVIVAL]
decision: >
Measure: ComPorts 0 bound to \\.\pipe\socialweb-vm-<id>-com1; the bidder connects as the pipe
client and writes console lines to <workDir>/console.log; after TerminateProcess of the bidder and
its restart, can a new client connect to the same pipe of the running VM, and is output written
while nobody was connected lost or buffered? Decision if reconnect works: the controller reconnects
at re-population. If it does not: console output after a core restart is lost until the guest's next
reboot (which never happens; a Lost VM is replaced), and the console is diagnostics only; asked of the
user (questions_for_user).
files: ["evidence only"]
test_first: "None (measurement)."
done_when: "Reconnect outcome and the size of the gap recorded."
risks: []
status: todo
evidence: []
- id: M0-BINARY-SWAP
milestone: M0
title: Upgrading a running service whose exe is locked, through versions/<sha256>/ and the SCM ImagePath
depends_on: [M0-HCS-SURVIVAL, M0-HCS-ACCOUNT]
decision: >
With the scratch bidder installed as an SCM service at bin/<sha A>/socialweb-computer.exe and a
guest rented: write bin/<sha B>/socialweb-computer.exe, ChangeServiceConfig the ImagePath,
restart the service; read: the guest's RuntimeId unchanged, the new process's version, the old exe
deletable only after the service stopped using it. Also measure: Defender scanning a newly written
exe (time to first start), and whether the virtual account may rewrite its own service's config
(expected no: the upgrade runs elevated or through a service DACL granting SERVICE_CHANGE_CONFIG to
the account; recorded). Fallback if the ImagePath change is refused for the chosen identity: the
upgrade command runs elevated (one admin prompt per upgrade), recorded; a fixed-path launcher exe is
not introduced, because it would be a second binary to upgrade.
files: ["evidence only"]
test_first: "None (measurement)."
done_when: "Swap reading recorded: RuntimeId before/after, service downtime, Defender delay."
risks: []
status: todo
evidence: []
- id: M0-GATE
milestone: M0
title: Write every M0 outcome into the M1+ items and stop for the user where a fallback was taken
depends_on: [M0-HCS-ACCOUNT, M0-HCS-SURVIVAL, M0-HCS-IDENTITY, M0-HNS-NETWORK, M0-COM-CONSOLE, M0-BINARY-SWAP]
decision: >
Amend M1+ decisions and files from the M0 readings; mark any item made unnecessary as blocked with
the reason; if any M0 item took a fallback that changes an invariant (a helper owning the VM, WMI
instead of HCS, LocalSystem for the bidder), set this item blocked until the user answers the
matching question. The scratch branch is deleted after its SHA is recorded.
files: ["docs/plans/0011-windows-bidder-hcs.yaml"]
test_first: "None (decision record)."
done_when: "Plan amended and committed; open user questions answered or none needed."
risks: []
status: todo
evidence: []
- id: M1-CONTROLLER-RUNTIME-OWNED
milestone: M1
title: The ComputeVM controller asks the VM runtime for liveness, orphans and events instead of the unit manager and HostProcess children
depends_on: [M0-GATE]
decision: >
Added by M0-REVISIT. internal/computevm calls hostsvc.Manager.Inspect for liveness (live, placer,
exits), lists HostProcess children for orphans, builds the VM slice name from vmruntime.SliceName
and watches unit events, and Reserve hands a Placer that creates the HostProcess child. An HCS system
has none of these. The seam becomes: Runtime gets the liveness and watch calls the controller needs
(a runtime that places no HostProcess says so in Capabilities), the Linux runtime keeps its
Placer path unchanged, and the controller branches on the capability, not on GOOS. Status.runtime
stays the landed shape; on Windows backend=hcs, ref=compute system Id, instance=RuntimeId, pid and
startTime empty, hostBootID empty.
files: ["internal/computevm/computevm.go", "internal/vmruntime/vmruntime.go"]
test_first: "None (AGENTS.md: no tests). Proof: the Linux market network still rents a guest."
done_when: "The Linux path is unchanged in a live rent and a controller can run with a runtime that places no HostProcess."
risks: []
status: todo
evidence: []
- id: M1-KCP-WINDOWS
milestone: M1
title: kcp, kine and the other bootstrap executables for windows/amd64 as pinned assets
depends_on: [M0-GATE, "0006:M1-ASSETS", "0006:M2-KCP-SLOTS"]
decision: >
Added by M0-REVISIT. The landed bidder runs the unpatched release kcp v0.33.0 behind kine v0.17.1 (the
cgo sqlite build) and our front; the assets table has no windows/amd64 entry for kcp, kine, deno,
bao or the plugins (versions.json platforms["windows/amd64"] is status seam with no assets), and
the upstream kcp release carries no windows archive. M0-HOST-BASELINE reads whether kcp and kine
build and run on Windows; this item then pins what it found: a source build per tool on the
release builder (CGO_ENABLED=0 where possible, a pure-Go sqlite for kine), deno and bao from their
windows/amd64 releases, each by sha256 in versions.json, staged by the same asset pipeline.
files: ["internal/versions/versions.json (windows/amd64 assets)", "internal/assets", "scripts/build-socialweb-computer.sh"]
test_first: "None (AGENTS.md: no tests)."
done_when: "The windows/amd64 asset list is pinned and the Windows host runs a bootstrap layer (kine, kcp, front) from those assets."
risks: ["kcp may not build for windows at all; if so the bidder's kcp runs elsewhere and this plan stops for the user."]
status: todo
evidence: []
- id: M1-VMHOST-HCS
milestone: M1
title: The Windows backend of the VM host seam over HCS, with no helper process
depends_on: [M0-GATE, M1-CONTROLLER-RUNTIME-OWNED, "0007:M2-CONTROLLER", "0007:M1-API"]
decision: >
hcs_windows.go in internal/vmruntime (build tag windows, ForHost returns it) implements the landed
vmruntime.Runtime (Prepare, Reserve, Await, Inspect, Shutdown, Stop, List, Cleanup, Watch, Load,
CollectImages; Reserve ignores the Placer because an HCS system has no HostProcess child; Watch polls
HcsEnumerateComputeSystems): Start writes the compute system
document (Owner socialweb-computer-<handle>, ShouldTerminateOnLastHandleClosed false, LinuxKernelDirect,
SCSI rootfs + cidata, the HNS endpoint, ComPorts 0 pipe, memory and processor count from the
ComputeVM spec), grants VM access to each file, creates, starts, then closes its handle; it records
status.runtime {backend hcs, ref Id, instance RuntimeId}. Inspect returns the Verdict table of
M0-HCS-IDENTITY. Stop(grace) calls HcsShutdownComputeSystem, waits up to grace for State Stopped,
then HcsTerminateComputeSystem, then deletes the endpoint and the work dir. List(owner) is
HcsEnumerateComputeSystems by Owner. Calls go through golang.org/x/sys/windows LazySystemDLL on
computecore.dll (vmcompute.dll where the build lacks it) with a small owned wrapper, CGO_ENABLED=0;
hcsshim is imported only for ext4/tar2ext4 (decided by Claude, user may veto: hcsshim's hcs package is
container-oriented and pulls a large tree). An HCS system of this Owner with no ComputeVM is the
controller's orphan rule (0007:M2-CONTROLLER), never touched if its Owner differs.
files: ["internal/vmruntime/hcs_windows.go", "internal/hcsapi/ (windows-only wrapper)"]
test_first: >
Offline on any OS: TestTheHcsDocumentForAComputeVMSetsOwnerAndNeverTerminatesOnLastHandle (golden
JSON) and TestTheHcsVerdictTable (fake API: not found -> Lost, RuntimeId differs -> Lost, access
denied -> Unknown). Windows-only behind livegate: TestAnHcsSystemOutlivesItsStarterAndIsAdoptedByTheNext
run through the RFP-flow fixture of 0007 (a rented guest, its bidder killed, a new bidder adopts by
Id). Red first.
done_when: "Offline tests green in check.sh on Linux; the livegate test green on the Windows host with readings."
risks: ["computecore.dll exports differ by Windows build; the wrapper resolves each proc lazily and check-host names any missing one."]
status: todo
evidence: []
- id: M1-NETWORK-HNS
milestone: M1
title: Guest networking on Windows through one HNS NAT network and an endpoint per guest
depends_on: [M0-GATE, M1-VMHOST-HCS]
decision: >
From M0-HNS-NETWORK. As landed there is no network index: every guest has the fixed plan of
internal/guestnet and dials 192.0.2.1. M0-HNS-NETWORK decides whether the Windows backend satisfies
that plan with an HNS network per guest, or the guest's network-config becomes per backend (a new
seam in guestseed/guestnet). The endpoint (MAC guestnet.GuestMAC) and its isolation policy are made at
Start and deleted at Stop; the bidder's guest-facing listeners bind the address the guest dials. Linux
pasta is untouched.
files: ["internal/vmruntime/hns_windows.go", "internal/hcnapi/", "internal/guestnet (per-backend plan, if M0 says so)"]
test_first: "Offline: TestTheEndpointDocumentCarriesTheIsolationAcl (golden). Livegate on Windows: TestTwoRfpGuestsOnOneBidderCannotReachEachOther (probe in each guest's user_data, result on onNetwork). Red first."
done_when: "Both green; the isolation reading recorded."
risks: []
status: todo
evidence: []
- id: M1-GUEST-IMAGE-HV
milestone: M1
title: The guest kernel, initramfs and rootfs for Hyper-V, built on Linux, carried as windows-only assets, written to VHDs in pure Go
depends_on: [M0-GATE, "0006:M1-ASSETS", "0007:M1-GUEST-IMAGE", "0007:M2-BACKPORT"]
decision: >
As landed (0007 M1-GUEST-IMAGE) a guest image is a guestImages target of versions.json built on any
Linux host by scripts/build-guest-image.sh, pinned by the sha256 of each output (vmlinux, rootfs.ext4,
image.json), never embedded, and fetched and verified by internal/guestimage; there is no initramfs
and no nodeinit in the VM path. So this item adds the reserved target hyperv-x86_64: an x86_64
kernel config deploy/guest-kernel/hyperv-x86_64.config with the Hyper-V drivers built in (the M0
config) and boot arguments root=/dev/sda, the same Ubuntu base rootfs and preinstall manifest. On the
host the rootfs ext4 is wrapped as a fixed VHD (a VHD footer appended, pure Go) once per image
fingerprint under runtime/<sha256>/, the per-guest cidata disk is the landed guestseed image the same
way, and a guest's rootfs is a copy (differencing VHDX through virtdisk.dll is a later optimisation).
Whether this kernel is a separate hypervisor target or one multi-driver x86_64 kernel shared with
firecracker is the user's question; until answered, a separate target (decided by Claude, user may veto).
files: ["scripts/build-guest-image.sh (hyperv target)", "deploy/guest-kernel/hyperv-x86_64.config", "internal/guestdisk (vhd wrapper, windows copy)", "internal/guestimage", "internal/versions/versions.json (guestImages target and pins)"]
test_first: >
Offline: TestTheHypervKernelConfigHasEveryDriverBuiltIn (parses the shipped .config: HYPERV,
HYPERV_STORAGE, HYPERV_NET, HYPERV_VSOCKETS, EXT4_FS =y), TestTar2Ext4ProducesAnImageCloudInitReadsAsCidata
(label and files read back with a pure-Go ext4 reader), TestTheInitFindsItsRootByLabel. Red first.
done_when: "Tests green; the windows/amd64 binary carries the image and `version --json` lists it with sha256."
risks: ["The rootfs is the largest asset; its size is budgeted in M2-RELEASE-WINDOWS."]
status: todo
evidence: []
- id: M1-DENOPOD-RUNNER-WINDOWS
milestone: M1
title: The forked DenoPod runner on Windows - a job object per run, identity by creation time
depends_on: [M0-GATE, "0008:M2-DENOKCP-FORK"]
decision: >
The runner is internal/kcplibs/impl/execrunner, and the OS part is its osProcessHost seam (prepare,
killGroup, termGroup, alive, startTicks), whose runner_windows.go refuses today. It gets a real
body: each run is a process created
suspended, assigned to its own job object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE), then resumed; stop
is TerminateJobObject; recovery uses internal/hostos.ProcStart (GetProcessTimes creation time). Every
per-run job is nested in the bidder service's own kill-on-close job (M2-BIDDER-SERVICE-WINDOWS), so a
crash of the core binary kills every deno process and no VM (VMs are not in any job of ours).
files: ["internal/kcplibs/impl/execrunner/runner_windows.go", "internal/hostos/job_windows.go"]
test_first: "Windows livegate: TestAKilledRunnerParentTakesEveryDenoChildWithIt and TestARunIsRecoveredByPidAndCreationTime. Red first (today the package does not build for windows)."
done_when: "GOOS=windows build of the runner green; both tests green on the Windows host."
risks: ["Deno on Windows spawns its own subprocesses rarely; any it spawns inherit the job unless CREATE_BREAKAWAY_FROM_JOB is allowed, which the job forbids."]
status: todo
evidence: []
- id: M1-HOSTUNIT-SCM
milestone: M1
title: The hostsvc.Manager backend on Windows - every non-VM host process is a child of the bidder service in a kill-on-close job
depends_on: [M0-GATE, "0006:M2-HOST-UNITS", M1-KCP-WINDOWS]
decision: >
Amended by M0-REVISIT. As landed, hostsvc.Manager is argv and cgroup shaped, hostsvc.Open refuses on
!linux, ParseBackend knows only systemd-user and proc, the proc backend (devsup) is Linux-only, and a
bidder's bootstrap layer (kine, kcp slot, front) plus its OpenBao are units. Only VMs must outlive the
core, and HCS owns them. So (decided by Claude, user may veto) the Windows backend, named job, starts
each unit as a child process of the bidder service inside the kill-on-close job object, reports
Inspect from the process handle, and a restart of the core re-creates its children (kcp state
persists in kine's sqlite, OpenBao on disk, VMs in HCS). No SCM service per child, no
SC_MANAGER_CREATE_SERVICE, no admin after install. Capabilities say EnforcesLimits false and no
FDStore or PrivateHostname, so hostsvc.RefuseUnsupported refuses those specs by name.
files: ["internal/hostsvc/job_windows.go", "internal/hostsvc/hostsvc.go (BackendJob)", "internal/hostos/job_windows.go"]
test_first: "None (AGENTS.md: no tests)."
done_when: "The bidder's bootstrap layer and OpenBao start as job children on Windows and a killed core takes every one of them with it."
risks: []
status: todo
evidence: []
- id: M2-BIDDER-SERVICE-WINDOWS
milestone: M2
title: run bidder as a Windows service, its deno children dying with it, its VMs not
depends_on: [M1-VMHOST-HCS, M1-NETWORK-HNS, M1-GUEST-IMAGE-HV, M1-DENOPOD-RUNNER-WINDOWS, M1-HOSTUNIT-SCM, M1-KCP-WINDOWS, "0007:M5-RUN-BIDDER"]
decision: >
As landed the bidder core is Type=notify with a watchdog (internal/notify); under SCM the same Ready
and Watchdog calls become service status reports.
`socialweb-computer run bidder --handle <h>` detects it was started by SCM and runs under
golang.org/x/sys/windows/svc (stop, shutdown and preshutdown handled with a bounded drain), service
name socialweb-computer-bidder-<h>, account from M0-HCS-ACCOUNT (preferred NT SERVICE\socialweb-computer-bidder-<h>),
state dir %ProgramData%\socialweb-computer\bidder\<h>, recovery actions restart after 5 s. At start
the process creates a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE and assigns itself only its
deno children (not itself, so SCM's kill of the service closes the last handle and the job takes the
children). Everything else is plan 0007's run bidder unchanged: in-process kcp on port 0, the bidder
set, the vault, token rotation, the ComputeVM controller with the hcs backend, the bidder DenoPod with
--compute-provider-kcp.
files: ["internal/stack/bidder (svc entry, windows file)", "cmd/socialweb-computer (service detection)"]
test_first: "Windows livegate: TestTheBidderServiceKilledLeavesNoDenoAndEveryVm (rent through the RFP flow, taskkill /F the service process: no deno.exe of this bidder remains, the HCS system's RuntimeId unchanged). Red first."
done_when: "Green; Get-Service and the HCS enumerate readings recorded."
risks: []
status: todo
evidence: []
- id: M2-INSTALLER-WINDOWS
milestone: M2
title: One elevated install step - features, account, group, HNS network, firewall, Defender, services
depends_on: [M2-BIDDER-SERVICE-WINDOWS]
decision: >
`socialweb-computer install bidder --handle <h>` run once from an elevated prompt (the user's
question asks whether a one-time admin step is acceptable; recommended yes), idempotent, every step
observed before it is changed: (1) the minimum optional feature M0-HOST-BASELINE proved
(Microsoft-Hyper-V-All or VirtualMachinePlatform) via DISM, printing 'reboot required' and stopping
if one was enabled; (2) the service account and its Hyper-V Administrators membership; (3) the HNS
NAT network with a prefix no existing NAT holds, refused with the conflicting owner named (WSL,
Docker) rather than overriding it; (4) a Windows Firewall rule allowing the guest prefix to reach the
bidder's guest-facing ports on the gateway address only; (5) Defender exclusions for the state dir's
runtime\ and vms\ (VHD IO) and nothing else; (6) the binary into versions\<sha256>\, the bidder and
vault services with their DACLs; (7) check-host. `uninstall bidder` reverses each, refusing while a
ComputeVM is Running unless --drain.
files: ["internal/install/system_windows.go (the install.System of the landed install flow; UserManager has no Windows meaning, the bidder service stands in for the user unit)", "internal/install/templates (Windows service definition)", "docs/bidder-host-windows.md"]
test_first: "Offline: TestTheInstallPlanIsObservedBeforeEachChange (fake system: a second run changes nothing); Windows livegate: TestInstallTwiceThenUninstallLeavesTheHostAsFound (feature step skipped). Red first."
done_when: "Both green; a clean-host install reading recorded end to end with timings."
risks: ["Group Policy may block Defender exclusions or service DACL changes; each refusal is printed with the policy name, not ignored."]
status: todo
evidence: []
- id: M2-BINARY-UPGRADE-WINDOWS
milestone: M2
title: socialweb-computer upgrade on Windows - a new versions/<sha256>/ exe and an ImagePath change, VMs untouched
depends_on: [M2-INSTALLER-WINDOWS, "0006:M3-HOST-INSTALL"]
decision: >
The landed upgrade flow (0006:M3-HOST-INSTALL) gets its windows half from M0-BINARY-SWAP: verify
the new binary's manifest and sha256, write versions\<sha256>\socialweb-computer.exe (never touching
the running file), ChangeServiceConfig ImagePath for the bidder service, restart it; versions and
runtime\<sha256>\ directories no service and no ComputeVM names are deleted by GC. A downgrade is
the same command with an older verified binary.
files: ["cmd/socialweb-computer upgrade (windows file)", "internal/install/windows/"]
test_first: "Windows livegate: TestAnUpgradeKeepsEveryHcsRuntimeIdAndRemovesTheOldVersionOnce. Red first."
done_when: "Green; RuntimeId before/after and service downtime recorded."
risks: []
status: todo
evidence: []
- id: M2-RELEASE-WINDOWS
milestone: M2
title: windows/amd64 in the release matrix, with its own asset list and size budget
depends_on: [M1-GUEST-IMAGE-HV, "0006:M4-BUILD", "0006:M0-SIZE-BUDGET"]
decision: >
The reproducible build (0006:M4-BUILD) gains windows/amd64 carrying: the core binary, deno
windows/amd64, bao windows/amd64, the Hyper-V guest image, the TypeScript closure (0008), and no
k3s, kine, postgres, firecracker or pasta (the gateway modes stay linux-only on Windows unless a
later plan says otherwise; `serve gateway kcp` refuses by name). Budget = measured sizes recorded in
versions.json and enforced by check.sh. When the matrix entry is published (from the start as
build-only, or only once ACC11-* pass) is the user's question; recommended: build and size-check
from M2 on, publish after ACC11-* pass.
files: ["internal/versions/versions.json (windows/amd64 entry)", "scripts/build-socialweb-computer.sh", "deploy/check.sh"]
test_first: "check.sh --full: TestTheReleaseBinaryFitsItsSizeBudget for windows/amd64 red with a 1-byte budget, then green; two clean windows builds byte-identical."
done_when: "Budget recorded and enforced; reproducibility reading recorded."
risks: ["Cross-building on Linux works for CGO_ENABLED=0; anything that needs cgo on Windows is refused at review."]
status: todo
evidence: []
- id: M3-LIVE-RENT
milestone: M3
title: A guest rented through the RFP flow from a Windows bidder, zero ssh
depends_on: [M2-INSTALLER-WINDOWS, "0008:M4-MARKET-NETWORK-PORT"]
decision: >
On the Windows host, with the installed bidder service and the market network (0008's market network command,
or the canonical TS harness if M0 recorded the market network cannot run on Windows): runComputeContract posts
compute.vm and a signed market.rfp, the Windows bidder bids, the requester accepts, the guest is born
from cloud-init user_data in an HCS system, reaches onNetwork, and the requester reaches it only
through the relay tunnel. Read: ComputeVM Running with status.runtime {hcs, Id, RuntimeId}; the bid
config, receipt and onNetwork records field-for-field the shapes the firecracker provider writes.
files: ["test/integration (a Windows integration test, written only at the end per AGENTS.md; until then a manual live run)"]
test_first: "The live test, red until M2 exists."
done_when: "PASS with UTC timestamps on the Windows host; zero ssh sessions in every log."
risks: []
status: todo
evidence: []
- id: M3-LIVE-SURVIVE
milestone: M3
title: Bidder crash and bidder upgrade keep the same VM, then release deletes it
depends_on: [M3-LIVE-RENT, M2-BINARY-UPGRADE-WINDOWS]
decision: >
After M3-LIVE-RENT's rent: TerminateProcess the bidder service, SCM restarts it, read the ComputeVM
Running with the same RuntimeId, the same issuer URL, the guest's next onNetwork accepted; then
`socialweb-computer upgrade --binary <a build with a different closure>`, same reads plus every
DenoPod on the new closure; then releaseCompute's vm.delete answered 200, the ComputeVM gone,
HcsEnumerateComputeSystems by Owner empty, the endpoint and the work dir's VHDs gone.
files: ["test/integration (a Windows integration test, written only at the end per AGENTS.md; until then a manual live run)"]
test_first: "That test, red until M2 exists."
done_when: "PASS readings recorded with UTC timestamps; zero ssh sessions."
risks: []
status: todo
evidence: []
- id: M3-LIVE-REBOOT-LOST
milestone: M3
title: A host reboot makes every VM Lost, never booted again, and the receipt still releases
depends_on: [M3-LIVE-SURVIVE]
decision: >
AMENDED 2026-10-16 (the user forbids any reboot of this machine): the reboot itself is never run and
that part stays blocked. The equivalent that is run: with a guest rented through the RFP flow, stop
the bidder service, terminate the guest's compute system (the HCS system is gone, as after a reboot),
start the service; the controller reads Lost for that Id, creates no system, and the receipt's
release answers vm.delete 200 and removes the Lost ComputeVM and its work dir. What a real reboot
would add and stays unproven: the service's automatic start at boot, HNS network persistence across
a boot, and the boot-id change. Original text: Restart-Computer. After boot the bidder service starts
(automatic start), the controller re-populates: the ComputeVM is Lost (HCS has no system with that
Id), no compute system is created for it, and the requester's releaseCompute of that receipt gets
vm.delete 200 and the Lost ComputeVM and its work dir are deleted. The requester-side replacement
through a new RFP is plan 0005 M3-NODE-LOSS's and is not re-proved here.
files: ["test/integration (a Windows integration test, written only at the end per AGENTS.md)"]
test_first: "That test, red until M2 exists."
done_when: "PASS readings recorded: pre-reboot Running, post-reboot Lost, no new HCS system, release 200."
risks: ["If the HNS network is non-persistent it must be recreated before the controller allocates; the installer's network is persistent unless M0 decided otherwise."]
status: todo
evidence: []
- id: M4-DOCS
milestone: M4
title: The Windows bidder in ARCHITECTURE, measured, and the host guide
depends_on: [M3-LIVE-REBOOT-LOST]
decision: >
docs/ARCHITECTURE.md's host platform section gains a measured Windows bidder column (HCS, HNS, SCM,
job objects, VHD writer) replacing 'design and cross-compilation only' for the parts this plan
measured, and states what stays unmeasured; docs/bidder-host-windows.md is the operator guide
(supported editions and builds, the one elevated install, upgrade, uninstall, what a reboot does).
files: ["docs/ARCHITECTURE.md", "docs/bidder-host-windows.md"]
test_first: "None (documentation); every claim cites an evidence reading of this plan."
done_when: "Committed; NEXT.yaml P11-* items closed and moved to ARCHITECTURE section 15."
risks: []
status: todo
evidence: []
acceptance:
where: "the Windows host the user provides; build steps also on hat-8"
checks:
- id: ACC11-RFP-ONLY
text: Every guest booted in every run of this plan was born from cloud-init user_data through requester -> RFP -> bid -> accept, and zero ssh sessions to any guest appear in any log.
status: todo
evidence: []
- id: ACC11-NO-OUR-CODE-IN-VM-TREE
text: For every running guest, the process owning it is vmwp.exe under vmcompute; no socialweb-computer or deno process is its ancestor, and a TerminateProcess of every socialweb-computer and deno process leaves every guest Running with the same RuntimeId.
status: todo
evidence: []
- id: ACC11-SURVIVE
text: A bidder crash and a bidder upgrade each leave every ComputeVM Running with the same status.runtime.instance, and the bidder re-populates exactly the HCS systems of its Owner.
status: todo
evidence: []
- id: ACC11-RELEASE
text: vm.delete for a rented guest answers 200 and afterwards no HCS system of the bidder's Owner, no HNS endpoint and no VHD of that guest remain.
status: todo
evidence: []
- id: ACC11-REBOOT-LOST
text: After a host reboot every previously Running ComputeVM is Lost, none is booted again, and each still releases with vm.delete 200.
status: todo
evidence: []
- id: ACC11-ISOLATION
text: Two guests of different requesters on one Windows bidder cannot reach each other, and each reaches the bidder only on its guest-facing ports.
status: todo
evidence: []
- id: ACC11-NO-PROTOCOL-CHANGE
text: The bid config, receipt, onNetwork and vm.delete records a Windows bidder writes are field-for-field the shapes the firecracker provider writes.
status: todo
evidence: []
questions_for_user:
- "If a virtual account in Hyper-V Administrators cannot drive HCS (M0-HCS-ACCOUNT), which fallback: (a) the bidder service as LocalSystem with every deno child under a restricted token in a kill-on-close job, or (b) WMI/VMMS with persistent VMs that a startup sweep deletes after a reboot? Recommended: (a); it keeps HCS's no-helper ownership and reboot = Lost by construction, and TypeScript never holds SYSTEM."
- "When does windows/amd64 enter the release matrix? Recommended: built and size-checked from M2-RELEASE-WINDOWS on, published only after ACC11-* pass."
- "Guest kernel: a separate Hyper-V x86_64 kernel target, or one multi-driver x86_64 kernel (virtio + Hyper-V built in) shared with firecracker and qemu? Recommended: one multi-driver kernel if its size stays within budget (one image to pin and test), else separate targets."
- "Is a one-time elevated install step (features, account and group, HNS network, firewall, Defender exclusions, services) acceptable, with every later operation including upgrade running unelevated where M0 proves it can? Recommended: yes."
- "If the COM-port pipe cannot be reconnected after a core restart (M0-COM-CONSOLE), is losing guest console output from that restart on acceptable, given the console is diagnostics only? Recommended: yes."
- "If WSL2 or Docker Desktop already hold the host's NAT capacity, should install refuse (naming them) or create an ICS-type network instead? Recommended: refuse by name; a bidder host is a server, not a desktop."