Merge nucleic/lucid-river-toad-6efj into dev

This commit is contained in:
2026-07-29 03:06:18 -07:00
parent 57fd65bfe1
commit 3e1f90bdac
3 changed files with 329 additions and 12 deletions
+37 -12
View File
@@ -36,6 +36,8 @@ dotnet run -- --probe # + GetMissingComponents / GetVersion
dotnet run -- --session # + create a session, a SECOND with the same name, identity-test, tear down
dotnet run -- --session --keep # …and leave the sessions running afterwards
dotnet run -- --all-types # include the ABI/marshalling plumbing in the dump
dotnet run -- --internal # can the SERVICE-INTERNAL COM interface be reached? (QI only)
dotnet run -- --internal-call # …and call through it (can crash — that is the finding)
```
**If it reports missing components**, the machine cannot run wslc yet, and the two components are
@@ -109,19 +111,40 @@ told you to clean it up with a command that doesn't exist. Pass `--keep` to leav
how you check the other half of the §2.3 question: whether session state outlives the process that
created it. With `--keep`, `wsl --shutdown` clears everything.
The gateway address is *not* what this probe is for any more — §13.1 established that no API
surfaces one, and D13 moves the control plane to hvsocket via `IWSLCVirtualMachine::GetId`.
The gateway address is *not* what this probe is for — §13.1 established that no API surfaces one,
so it comes from `GetAdaptersAddresses` over `vEthernet (WSL)` instead, which `WslcFacade` now does.
### The three answers that change the design
### `--internal` — is D13's internal arm even reachable?
The newest open question, and the one item 5 stopped at (docs/WINDOWS_PORT.md §13.2). D13 routes
enumeration, reattach and the Terminal panel's pty to the service-internal `IWSLCSessionManager`
(IID `82A7ABC8-…`). But **`wslc.idl` declares interfaces and no activatable class** — there is no
CLSID in it, so `CoCreateInstance` has nothing to name. The only registered coclasses in WSL's
IDLs belong to the compat surface (`WSLCCompatSessionManager` and its factory) and to the WSL
service proper (`LxssUserSession`, `LxssUserSessionInBox`).
The likely answer is that one of those objects also implements the internal interface — COM
objects routinely expose several — so `--internal` activates each and QIs for the internal IIDs.
It **never calls a method**, so a vtable mismatch cannot crash it, and QI alone answers the
question. `--internal-call` then calls `GetVersion` (slot 3) and `ListSessions` to confirm the
vtable really matches the IDL; *that* can take the process down, which is why it is opt-in.
It also QIs `IWSLCVirtualMachine`, which is **expected to fail**: per the IDL only
`IWSLCVirtualMachineFactory::CreateVirtualMachine` produces one, and the SYSTEM service owns the
factory. §13.1's hvsocket-primary decision was retracted on that reading, and a decision reversed
by reading deserves confirming against the machine.
### The 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 | **Settled by D13.** `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 broker binds both surfaces; no CLI. |
| No create-or-attach on `Session` | **Answered:** `IWSLCSessionManager::OpenSessionByName` / `EnterSession` / `ListSessions` on the internal interface. §2.3 reattach has its mechanism. |
| No gateway address anywhere on the API | **Skip TCP:** `IWSLCVirtualMachine::GetId` returns the VM GUID, so §5's AF_HYPERV/AF_VSOCK path (true vsock parity with macOS) should become primary at M1 (b). Gateway TCP stays as fallback, its address from `GetAdaptersAddresses` over `vEthernet (WSL)`. |
| No container enumeration, stats, pty or attach in the SDK | **D13's answer, now gated.** They exist on `wslc.idl`, the service-internal interface `wslc.exe` calls — but that IDL declares no coclass, so reaching it at all is what `--internal` tests. Stats no longer wait on it: `WslcFacade` reads cgroup v2 in the guest instead. |
| `Container` has no `Name`, `Session` has no `GetContainers()` | Sharper than "no enumeration": a container is reachable ONLY through the handle `CreateContainer` returned, so the broker keeps its own name→handle roster — which dies with the process. A restarted broker sees an empty sandbox (§13.2). |
| No create-or-attach on `Session` | `IWSLCSessionManager::OpenSessionByName` / `EnterSession` — same gate as above. Until then `session.ensure` fails `session_exists` and names `wsl --shutdown` as the remedy. |
| No gateway address anywhere on the API | **Gateway TCP stays primary.** The hvsocket alternative needed a VMID from `IWSLCVirtualMachine::GetId`, and that interface is unreachable from a client — retracted in §13.1. The address comes from `GetAdaptersAddresses` over `vEthernet (WSL)`. |
| 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). |
---
@@ -134,9 +157,11 @@ for, and nothing above it should move. These three are different:
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), and now the *primary* control-plane spike rather than the
fallback one (D13): bind an AF_HYPERV listener on the VM GUID from
`IWSLCVirtualMachine::GetId` and dial AF_VSOCK from inside a wslc container. If vsock
traverses the container's namespaces, the Windows control plane gets true parity with macOS
and §5's firewall/NAT variance stops mattering. Measure gateway-TCP reachability in the same
run so the fallback stays evidenced.
- **`GatewaySpike`** — M1 (b), and the control-plane spike that actually matters now: can a
`Bridged` wslc container reach the host at the `vEthernet (WSL)` address `WslcFacade` returns,
under the **default** Windows firewall, and does `control-bridge.js` complete an MCP round trip
over it? This is the committed path, not a fallback.
- **`HvSocketSpike`** — downgraded back to upside (§13.2). It needs a VM GUID to bind `AF_HYPERV`
to, and `IWSLCVirtualMachine::GetId` turned out to be unreachable from a client, so the VMID
would have to come from outside wslc entirely — HCS enumeration, or the WSL VM's registry
identity. Worth doing only if the gateway path shows friction in the wild; not on the M2 path.