# M1 spikes (docs/WINDOWS_PORT.md §13) Programs that answer questions the port is currently *guessing* at. They are checked in and runnable on purpose: a spike whose answer nobody can reproduce six months later is a rumour. They are deliberately **not** in `windows/Nucleic.sln`. The solution's broker contract tests must keep running on any machine against `FakeWslc`; these need the `Microsoft.WSL.Containers` preview NuGet and a real WSL stack, so they are built by path. --- ## `WslcApiDump` — what does the wslc API actually look like? **Status: it has served its original purpose (docs/WINDOWS_PORT.md §13.1).** The real API is known, and it was obtained without a Windows machine at all — the package is public, so the `.nupkg` was downloaded, its projection assembly extracted, and its metadata read with `MetadataLoadContext`. What this tool is *for* has therefore changed: its assumption list now describes the surface actually observed in 2.9.3, so running it says **what the next package version moved**, not what we guessed wrong. **Why it is still reflection.** Same reason as before: a typed check fails to compile on the first rename and reports one problem, where this reports all of them at once. That property is worth keeping for a preview API whose next version can break anything. Two facts it discovered that anything referencing this package needs: - the assembly is **`wslcsdkcs.dll`**, not `Microsoft.WSL.Containers.dll` — loading it by package name fails; - the only published version is **2.9.3**, targeting `net8.0-windows10.0.19041.0`. The `0.1.0-preview.1` pin this repo carried could never have restored. ```powershell cd windows/spikes/WslcApiDump dotnet run # dump the API + check every facade assumption dotnet run -- --probe # + call the two read-only statics (service version, missing components) dotnet run -- --session # + create a real session and print its live property VALUES ``` It writes the full public object model to `wslc-api-dump.txt` (`--out` to relocate) and prints an `ok` / `MISSING` line per assumption, each with *why that member matters* and a nearest-name hint. It exits 0 even when assumptions fail — a mismatch is the product, not an error. Only a genuinely broken run (the assembly won't load) exits non-zero. **What to send back:** the console output, and `wslc-api-dump.txt` if anything is MISSING — which now means the package moved under us, not that we guessed wrong. ### Why `--session` earns its risk `--session` creates a real wslc session named `nucleic-spike` under `%LOCALAPPDATA%\Nucleic\spike\wslc` and leaves it running. It exists for one question that type metadata cannot answer: **where does the WSL-facing host gateway address come from?** §5 makes gateway TCP the primary control-plane transport, `WslcContainerEngine.ensureRunning` returns that address to the Swift side, and `control-bridge.js` dials it. The facade guesses `Session.HostGatewayAddress`. Dumping the live *values* of every session property lets us recognise a gateway IP whatever it is called — and if nothing on the session looks like one, that is itself the finding, and §5's hvsocket fallback gets promoted from upside to dependency. Leaving the session running is also the cheap version of a §2.3 question: broker supervision assumes wslc state is **service-backed**, so that a crashed `nucleic-brokerd` can reattach and re-enumerate rather than orphaning containers. If `wslc session ls` still shows `nucleic-spike` after this process exits, that assumption holds. Tear it down with `wslc` when you're done. ### The three answers that change the design Most mismatches are a one-line edit in `WslcFacade.cs` — that is exactly what the `IWslc` seam is for, and nothing above it should move. These three are different: | Finding | Consequence | | --- | --- | | No container enumeration, stats, pty or attach in the SDK | **Not fatal, and not CLI-only.** `wslcsdk.dll` wraps `WSLCCompat.idl` (the stable SDK surface), which genuinely lacks them; they all exist on `wslc.idl`, the service-internal COM interface `wslc.exe` calls — `ListContainers`, `Stats`, `ResizeTty`, `OpenContainer`/`Attach`, `OpenSessionByName`, and `IWSLCVirtualMachine::GetId` (the VM GUID for AF_HYPERV). Both IDLs are open source. Internal COM vs. CLI is an undecided trade-off — see §13.1. | | No create-or-attach on `Session` | **Answered:** `IWSLCSessionManager::OpenSessionByName` / `EnterSession` / `ListSessions` exist on the internal interface. Reattach (§2.3) is mechanically possible; what remains is choosing the surface. | | No gateway address anywhere on the API | Source it from `GetAdaptersAddresses` over the `vEthernet (WSL)` adapter — **or skip TCP entirely**: `IWSLCVirtualMachine::GetId` returns the VM GUID, so §5's AF_HYPERV/AF_VSOCK path (true vsock parity with macOS) is directly reachable rather than being upside. | | No uid on `ProcessSettings` | Recoverable, and already planned for: exec wraps argv in `setpriv`/`su agent -c`. Interceptors and nash don't care about the numeric uid (§3.2). | --- ## Not yet written - **`WslcSpike`** — the typed happy path: session → GHCR pull of `naros-agent` → container with an NTFS `ContainerVolume` → `exec git status` in the bind-mounted worktree → stdio round-trip → SIGTERM, plus the 9P latency numbers §15 wants (`git status` and `npm install` on a real repo, mounted vs. in-VM). Deliberately held back until `WslcApiDump` has run: written now, against guessed names, it would not compile, and fixing it blind is the mistake this whole approach exists to avoid. - **`HvSocketSpike`** — M1 (b): AF_HYPERV host listener ↔ AF_VSOCK dial from inside a wslc container, plus gateway-TCP reachability and default-firewall behaviour in NAT and mirrored modes. Only worth building once a container can be started at all.