{"metadata": {"vulnerability": "cve-2020-1509", "count": 19, "kinds": {"milestone": 5, "sighting": 2, "kev": 0, "other": 12}}, "data": [{"timestamp": "2019-11-04T00:00:00Z", "date_only": false, "value": "2019-11-04T00:00:00.000Z", "kind": "milestone", "label": "Reserved", "source": "cvelistv5", "record": "cve-2020-1509", "field": "cveMetadata.dateReserved"}, {"timestamp": "2020-08-11T07:00:00Z", "date_only": false, "value": "2020-08-11T07:00:00.000Z", "kind": "other", "label": "Made public", "source": "cvelistv5 / microsoft", "record": "cve-2020-1509", "field": "containers.cna.datePublic"}, {"timestamp": "2020-08-12T00:00:00Z", "date_only": false, "value": "2020-08-12T00:00:00.000000", "kind": "other", "label": "Revision date", "source": "certfr_avis \u00b7 CERTFR-2020-AVI-501", "record": "CERTFR-2020-AVI-501", "field": "revisions.0.revision_date"}, {"timestamp": "2020-08-14T00:00:00Z", "date_only": true, "value": "2020-08-14", "kind": "other", "label": "Submit Time", "source": "cnvd \u00b7 CNVD-2020-72704", "record": "CNVD-2020-72704", "field": "submitTime"}, {"timestamp": "2020-08-17T19:13:19Z", "date_only": false, "value": "2020-08-17T19:13:19.000Z", "kind": "milestone", "label": "Published", "source": "cvelistv5", "record": "cve-2020-1509", "field": "cveMetadata.datePublished"}, {"timestamp": "2020-08-17T19:15:00Z", "date_only": false, "value": "2020-08-17T19:15:00Z", "kind": "other", "label": "Nvd published at", "source": "github \u00b7 GHSA-MXF8-C4GW-PC7V", "record": "GHSA-MXF8-C4GW-PC7V", "field": "database_specific.nvd_published_at"}, {"timestamp": "2020-08-17T19:15:17.007Z", "date_only": false, "value": "2020-08-17T19:15:17.007", "kind": "milestone", "label": "Published", "source": "fkie_nvd \u00b7 FKIE_CVE-2020-1509", "record": "FKIE_CVE-2020-1509", "field": "published"}, {"timestamp": "2020-08-17T19:15:17.007Z", "date_only": false, "value": "2020-08-17T19:15:17.007", "kind": "milestone", "label": "Published", "source": "gsd \u00b7 GSD-2020-1509", "record": "GSD-2020-1509", "field": "namespaces.nvd.nist.gov.cve.published"}, {"timestamp": "2020-09-02T06:09:40Z", "date_only": false, "value": "2020-09-02T06:09:40.000000Z", "kind": "sighting", "label": "Seen", "source": "https://t.me/SecLabNews/8392", "record": "CVE-2020-1509", "field": "creation_timestamp", "uuid": "a2a6f553-e155-4575-b376-ca3e010063e0", "content": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 Google Project Zero \u0440\u0430\u0441\u043a\u0440\u044b\u043b\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043e\u0431 \u0443\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u0438 CVE-2020-1509 \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 Windows, \u043a\u043e\u0442\u043e\u0440\u0443\u044e Microsoft \u043d\u0435\u0434\u043e\u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0432\u044b\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0432\u043e \u0432\u0442\u043e\u0440\u043d\u0438\u043a \u0430\u0432\u0433\u0443\u0441\u0442\u043e\u0432\u0441\u043a\u0438\u0445 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.\n\n \n\u0423\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u044c \u0432 Windows \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u044c Enterprise Authentication", "url": "https://t.me/SecLabNews/8392"}, {"timestamp": "2020-12-18T00:00:00Z", "date_only": true, "value": "2020-12-18", "kind": "other", "label": "Open Time", "source": "cnvd \u00b7 CNVD-2020-72704", "record": "CNVD-2020-72704", "field": "openTime"}, {"timestamp": "2022-05-24T17:25:50Z", "date_only": false, "value": "2022-05-24T17:25:50Z", "kind": "milestone", "label": "Published", "source": "github \u00b7 GHSA-MXF8-C4GW-PC7V", "record": "GHSA-MXF8-C4GW-PC7V", "field": "published"}, {"timestamp": "2023-12-13T01:21:58.089Z", "date_only": false, "value": "2023-12-13T01:21:58.089870Z", "kind": "other", "label": "Updated", "source": "gsd \u00b7 GSD-2020-1509", "record": "GSD-2020-1509", "field": "gsd.osvSchema.modified"}, {"timestamp": "2024-01-04T03:30:33Z", "date_only": false, "value": "2024-01-04T03:30:33Z", "kind": "other", "label": "Updated", "source": "github \u00b7 GHSA-MXF8-C4GW-PC7V", "record": "GHSA-MXF8-C4GW-PC7V", "field": "modified"}, {"timestamp": "2024-01-19T00:15:13.837Z", "date_only": false, "value": "2024-01-19T00:15:13.837", "kind": "other", "label": "Updated", "source": "gsd \u00b7 GSD-2020-1509", "record": "GSD-2020-1509", "field": "namespaces.nvd.nist.gov.cve.lastModified"}, {"timestamp": "2024-05-29T16:32:24.869Z", "date_only": false, "value": "2024-05-29T16:32:24.869Z", "kind": "other", "label": "Updated", "source": "cvelistv5 / microsoft", "record": "cve-2020-1509", "field": "containers.cna.providerMetadata.dateUpdated"}, {"timestamp": "2024-08-04T06:39:10.566Z", "date_only": false, "value": "2024-08-04T06:39:10.566Z", "kind": "other", "label": "Updated", "source": "cvelistv5", "record": "cve-2020-1509", "field": "cveMetadata.dateUpdated"}, {"timestamp": "2024-08-04T06:39:10.566Z", "date_only": false, "value": "2024-08-04T06:39:10.566Z", "kind": "other", "label": "Updated", "source": "cvelistv5 / CVE", "record": "cve-2020-1509", "field": "containers.adp.0.providerMetadata.dateUpdated"}, {"timestamp": "2026-06-17T03:01:35.773Z", "date_only": false, "value": "2026-06-17T03:01:35.773", "kind": "other", "label": "Updated", "source": "fkie_nvd \u00b7 FKIE_CVE-2020-1509", "record": "FKIE_CVE-2020-1509", "field": "lastModified"}, {"timestamp": "2026-10-13T11:53:52Z", "date_only": false, "value": "2026-10-13T11:53:52.000000Z", "kind": "sighting", "label": "Seen", "source": "https://gist.github.com/gilesknap/15d0d71e8c6ab272380f60e05a6dcdad", "record": "CVE-2020-1509", "field": "creation_timestamp", "uuid": "70afbe23-170a-465f-ba11-a6a81b234dab", "content": "# Native Windows sandbox: feasibility (workflow wf_f25cf010-245, 2026-10-12)\n\n&gt; Generated by a 9-agent workflow. Part 2 is an adversarial fact-check that REFUTES parts of Part 1: read both.\n\n## Part 1: Synthesis report\n\n### Verdict\n\nYes, with conditions. The whole agent can run inside a Microsoft Execution Containers (MXC) process container on its native PSEC/BaseContainer tier. Python launches it with no admin rights by exec'ing Microsoft's pinned wxc-exec.exe, the way the Linux core execs bwrap. The host must be Windows 11 24H2/25H2 at build 26100/26200.9278 or later (26H1 at 28000.2804 or later), with the BaseContainer feature enabled. This tier gives an AppContainer identity, a deny-by-default path allowlist that never edits host ACLs, per-container WFP egress rules with CIDR `except` lists, UI limits and a kill-on-close job. That makes it comparable to Linux for secrets held in files, writes, desktop IPC, privilege, and IP-level lateral movement. It does not give a private network stack, an exact policy read-back without admin, or per-port loopback relays. Four Windows-only routes stay open questions until a 1-2 week spike: Credential Manager/DPAPI, SMB sessions, console attach, and toolchain breakage. It is a rewrite of the audit core (bwrap.py, jail.py, shadow.py), the installer's system steps, the shims and the battery: about 2.1-3.3k new lines of Python, plus PowerShell and a ~100-line compiled shim. About a third of the package carries over (about 2.3-2.6k lines: the config grammar, profiles, gitconfig, the token helper, the managed-settings merge and the CLI). It can and should live in this repo as a `claude_sandbox.windows` backend in the same py3-none-any wheel and release, with its own three-module audit core and ADRs amending ADR 26. Build it only if DLS users need Windows-native toolchains. If they only need a sandbox on a Windows PC, harden and document the existing stack under WSL2 (1-2 person-weeks) and stop there.\n\n### The use-case question\n\nDecide this first: do DLS users need the agent to build, run or test Windows-native code? Examples are MSVC/C++, .NET Framework/WPF, PowerShell modules, Windows-only instrument SDKs or drivers, LabVIEW, and Windows paths/registry. Or do they have Windows PCs and work mostly on Linux-targeted code (Python, EPICS IOCs, web)? Also find out about the fleet: Windows 11 25H2 with current cumulative updates, the BaseContainer feature enabled, developers' local-admin rights, and AppLocker/WDAC rules on user-writable paths.\n\n- Sandbox on a Windows host only: choose F (the existing stack inside WSL2, container route). Deliver /mnt and /run/WSL masks for in-distro installs, a --peers guard for /mnt//Users/, doctor checks (refuse WSL1, warn on interop/automount, report networking mode, probe the NAT gateway), two battery checks, a how-to and an ADR. That is 1-2 person-weeks, with no new core and no new ADR 26 exceptions. Optionally add G (launch from PowerShell against podman machine) for 1-2 more person-weeks.\n- Windows-native toolchains: do F anyway (cheap, and it covers Linux work on the same machines). Then run the spike and, if it passes, build A. That is about 11-17 person-weeks to a preview release, plus a recurring cost for Windows cumulative updates, MXC releases and the toolchain tail.\n- Fleet has Windows 10, Windows Server, or Windows 11 builds older than August 2026: A cannot serve those machines; it refuses rather than weakening. Either restrict A to compliant machines and send everyone else to F, or add B, an admin-installed dedicated user plus WFP (srt-win style). B is another 10-20+ person-weeks; prefer adopting srt-win once it leaves alpha.\n- Few users, or the need is mainly command sandboxing: consider waiting for Claude Code's own MXC support, which Microsoft lists as coming soon. It covers shell commands only, though. Claude Code's file tools, MCP servers and hooks run outside it, so it does not close threat route 1.\n\n### Recommended option\n\nA, gated on the use-case answer and the spike, with F delivered first in every case. C and B are later options, triggered only by spike findings or fleet needs.\n\n### Options\n\n#### A: Native: whole agent in an MXC process container (AppContainer-based BaseContainer via the OS PSEC API), driven by a pinned wxc-exec.exe from a launcher-owned ConPTY\n\nA pure policy module turns (profile, conf, env) into an MXC 1.0.0 JSON request:\n- readwrite: workspace, agent state, per-session temp, per-project forge store, narrow cache subpaths\n- readonly: relocated claude.exe, toolchain dirs, shipped skills\n- env: explicit block with inheritDefaultEnv=false\n- ui: limits on\n- network: egress default deny; allow 0.0.0.0/1 and 128.0.0.0/1 `except` the Linux blackhole ranges plus on-link prefixes; one /32 per allow-ip; ingress and hostLoopback deny\n- fallback.allowDaclMutation=false\n\nThe launcher probes the tier, refuses on anything but BaseContainer, owns a ConPTY, and execs wxc-exec by absolute path.\n\n- **Isolation:** Strong for:\n- secrets held in files (deny-by-default, no DACL edits)\n- writes\n- desktop/IPC (pipes need AppContainer ACEs; UIPI; Win32k/job UI limits)\n- privilege (AppContainer is an MSRC-serviced boundary; low IL; no SSO without enterpriseAuthentication)\n- lateral movement over IPv4/IPv6 (per-container WFP CIDR rules; deny beats allow)\n\nGaps:\n- shared host network stack, so the agent can see the host's identity\n- behavioural, not exact, policy read-back\n- hostLoopback bidirectional and all-ports\n- an allow-ip that Windows classes as 'private' forces private inbound\n- OS-brokered traffic (Dnscache; possibly the SMB redirector)\n- process list visible\n\nUnverified: Credential Manager, DPAPI, WAM, SMB session reuse, console attach.\n- **Toolchains:** Native Windows tools, including the agent itself (Claude Code 2.1.265 fixed file tools under AppContainer). cmd and curl work. PowerShell needs Win32k (ui relaxed to 'desktop'). Per-user toolchains need narrow read grants. git refuses repos owned by Administrators. Untested: Git Bash/MSYS2, MSVC, dotnet and node-gyp. Detached servers die with the agent.\n- **Privilege:** Launch: none. Install: none per-user, which matches the Linux trust model where 'root-owned' means outside the jail's writable set. Optional admin for a per-machine install (Program Files, HKLM managed settings, admin-ACL conf). OS prerequisite managed by IT: Win11 build at or above .9278 with BaseContainer enabled. No Win10 or Server.\n- **Effort:** 11-17 person-weeks to a preview after a 1-2 week spike; medium-high ongoing maintenance.\n- **Verdict:** Recommended if the use case needs Windows-native toolchains and the spike passes.\n\n#### A-classic: Native: hand-built classic AppContainer/LPAC via ctypes plus DACL grants (MXC Tier 3 done ourselves)\n\nCreateAppContainerProfile + SECURITY_CAPABILITIES + CreateProcessW from ctypes. Workspace access by adding ACEs to user files. Network by capabilities, plus admin WFP rules for an RFC1918 block.\n\n- **Isolation:** Same AppContainer boundary as A. The capability classes are not RFC1918: routed lab subnets count as Internet, so without admin WFP there is no lateral-movement control. Recursive ACE edits leave residue after crashes and race concurrent runs.\n- **Toolchains:** Worse than A. C:\\ root and \\Device\\Null need admin prep, the latter after every boot. Loopback needs elevated exemptions.\n- **Privilege:** Admin once at install, admin on every boot (scheduled task), admin for loopback exemptions; none per launch.\n- **Effort:** 20-30 person-weeks; high maintenance; the riskiest ctypes code.\n- **Verdict:** Reject: A does the same without mutating ACLs or needing admin.\n\n#### B: Native: dedicated local sandbox user + restricted token + private desktop + job + ConPTY + persistent WFP (srt-win, Codex 'elevated')\n\nAdmin setup creates a hidden standard user, logon-right denies, ambient write-deny ACEs and SID-keyed WFP filters. Each launch runs CreateProcessWithLogonW to start a runner, then a restricted token on a per-exec desktop.\n\n- **Isolation:** User boundary (MSRC-serviced) keeps out the developer's profile, DPAPI, Credential Manager, HKCU and Kerberos/NTLM. Gaps:\n- anything Users or Authenticated Users can read or write stays reachable (Authenticated Users have Modify on C:\\ subfolders; ProgramData)\n- the SMB redirector is invisible to SID-keyed WFP\n- logon-SID ReadProcessMemory pitfall\n- medium IL (no UIPI) needs a private desktop\n- **Toolchains:** Good for machine-wide installs. Per-user toolchains invisible, separate caches, git safe.directory, Schannel and Git Bash issues seen in srt and Codex.\n- **Privilege:** Admin once at install; admin for every network-policy change unless a SYSTEM service is added; none per launch. GPO may forbid local accounts.\n- **Effort:** 20-30 person-weeks to build; less if srt-win (Apache-2.0, alpha) is adopted as a pinned binary.\n- **Verdict:** Not primary. Only for pre-PSEC hosts if IT allows; prefer srt-win once mature.\n\n#### C: Hybrid: A by default, plus an optional admin-installed SYSTEM WFP helper (exact per-port loopback/ingress and read-back), and/or B on non-PSEC hosts\n\nThe helper opens a dynamic WFP session with a max-weight sublayer keyed on the container SID. It reads its filters back (FwpmFilterEnum0) and holds the kill-on-close job, so its death kills the agent. Policy comes only from the admin conf.\n\n- **Isolation:** A plus exact read-back, per-port local-port and callback-port, and audit of competing hard permits.\n- **Toolchains:** As A.\n- **Privilege:** Admin once to install the service; none per launch.\n- **Effort:** A + 4-8 person-weeks for the helper; + 20 or more for a B fallback.\n- **Verdict:** Defer. Trigger it if the spike shows loopback or ingress gaps matter, or the fleet needs non-PSEC support.\n\n#### D: Windows Sandbox (disposable Hyper-V guest; .wsb plus a nonce-authenticated stdio relay)\n\nLauncher writes a .wsb with mapped folders. A guest helper runs the agent under ConPTY and relays I/O over TCP, as MXC's experimental backend does.\n\n- **Isolation:** VM boundary with a clean guest per launch. Networking is all-or-nothing NAT to the LAN. The default guest user is admin, so in-guest firewall rules are removable. Clipboard, audio and vGPU are on by default. No documented host-side egress filter.\n- **Toolchains:** Same Windows build as the host. Toolchains must be mapped or reinstalled per launch; MSVC from a read-only map is unverified.\n- **Privilege:** Admin once to enable the feature (not on Home). Standard users may need Hyper-V Administrators (unverified on 24H2).\n- **Effort:** 10-16 person-weeks; egress still unsolved.\n- **Verdict:** Reject: one instance per host, `wsb exec` has no I/O, no lateral-movement control.\n\n#### E: Windows containers with Hyper-V isolation (Docker/Moby, Server Core image with toolchain)\n\n`docker run --rm -it --isolation=hyperv --user ContainerUser -v ws:ws image claude` from the existing launcher pattern.\n\n- **Isolation:** VM-grade, but process isolation is not a boundary. Default NAT reaches the LAN and the host, and Hyper-V-isolated NAT port ACLs are not reconfigurable. Bind mounts access the host as LocalSystem. Docker API access equals admin.\n- **Toolchains:** Server Core images with Git, Python, Node and Build Tools (multi-GB). Slower IO. No GUI. Agent TUI under docker -it unverified.\n- **Privilege:** Admin once (features, reboot, engine). Per launch, docker-users, which Docker documents as able to elevate to admin. Docker Desktop is paid at DLS's size; base images are dev/test only on clients.\n- **Effort:** 12-20 person-weeks plus a new egress design.\n- **Verdict:** Reject: per-launch privilege is admin-equivalent (breaks OPS-06) and there is no egress jail.\n\n#### F: WSL2 + the existing Linux stack (baseline, not native)\n\nInstall as on Linux inside a WSL2 distro: rootless podman plus the published image. Keep projects on ext4.\n\n- **Isolation:** Full parity: the WSL2 kernel has the user, pid and net namespaces and TUN. Needs:\n- /mnt, /mnt/wslg and /run/WSL masked for in-distro installs (CLAUDE_SANDBOX_HOST_INSTALL=1)\n- a --peers guard, because the $HOME check misses /mnt/c/Users/\n- interop and automount off\n- a decision on the NAT gateway, which is the Windows host and stays reachable on every port\n\nAll distros share one kernel.\n- **Toolchains:** Linux toolchains only; no MSVC, .NET Framework or Windows PowerShell.\n- **Privilege:** Admin once to enable WSL; none per launch.\n- **Effort:** 1-2 person-weeks (masks, doctor, battery checks, docs, ADR).\n- **Verdict:** Do it in every case: the answer for 'sandbox on a Windows host' and the comparison baseline.\n\n#### G: Windows-host launcher driving podman machine (WSL2 provider)\n\n`uvx claude-sandbox` runs on Windows and starts the same Linux container. Needs:\n- import safety (termios, SIGHUP)\n- host-to-container path mapping\n- an ntpath-correct --peers guard\n- engine resolved to an absolute .exe outside the workspace\n- console-close cleanup\n- an msvcrt key read\n\n- **Isolation:** As F; the jail runs in the podman-machine VM. Docker Desktop (rootful) fails the jail.\n- **Toolchains:** Linux only.\n- **Privilege:** Admin once (WSL, podman); none per launch.\n- **Effort:** 1-2 person-weeks (300-450 changed lines + tests) on top of F.\n- **Verdict:** Optional convenience after F.\n\n#### H: Same-user restricted token or Low IL (Codex 'unelevated', Gemini CLI)\n\nWRITE_RESTRICTED or Low-IL token plus a job, with ACL or label grants on writable roots.\n\n- **Isolation:** Confines writes only. Reads, Credential Manager, DPAPI and SSO stay open; network 'blocks' are env vars or a rate cap. Not a serviced boundary.\n- **Toolchains:** Moderate; Git Bash breaks under restricted tokens.\n- **Privilege:** None.\n- **Effort:** 4-6 person-weeks.\n- **Verdict:** Reject: does not address threat routes 1 or 4.\n\n#### I: Rely on the agents' own Windows sandboxes (Codex MXC mode; Claude Code MXC 'coming soon')\n\nUse vendor command sandboxes instead of wrapping the agent.\n\n- **Isolation:** Covers commands only. The agent's file tools, MCP servers, hooks and login token run unconfined; there is no lateral-movement policy we control.\n- **Toolchains:** Vendor-defined.\n- **Privilege:** None.\n- **Effort:** Docs only.\n- **Verdict:** Complementary, not a substitute; test nesting inside A.\n\n### Property mapping (Linux to recommended option)\n\n| Property | Linux | Windows | Fidelity | Notes |\n|---|---|---|---|---|\n| SEC-01 Home directory default-deny (core) | tmpfs over $HOME, allowlist bound back (bwrap.py:272,290-329) | BaseContainer deny-by-default. Grants only to agent state (%USERPROFILE%\\.claude; .claude.json via CLAUDE_CONFIG_DIR if single-file grants fail), shipped skills (ro), narrow cache subpaths and the per-project forge store. .ssh, .aws and AppData browser and credential files stay unreadable with no DACL edits. | equivalent | Writes elsewhere are denied rather than discarded. Junctions, 8.3 names and ADS are unverified (spike plus battery). |\n| SEC-02 Per-agent credential separation (important) | Per-profile binds; gap via the shared /user-terminal-config | Per-agent grant sets in policy.py; no shared store mount exists natively | equivalent | The Linux gap does not arise. Pi on Windows is unverified. |\n| SEC-03 Environment scrub (core) | --clearenv + allowlist + pass-env deny-list (bwrap.py) | The launcher passes the whole block (inheritDefaultEnv=false; SYSTEMROOT and LOCALAPPDATA required). The deny-list gains PATH, PATHEXT, COMSPEC, PSModulePath, NODE_OPTIONS, PYTHON*, GIT_CONFIG*. | equivalent | Whether the container can read setx-stored values in HKCU\\Environment is unverified (battery). |\n| SEC-04 Secret-bearing runtime dirs masked (core) | tmpfs /tmp, /run/user, /run/secrets | The user's %TEMP% is not granted; per-session temp instead. Named pipes (openssh-ssh-agent, VS Code IPC, docker_engine) need AppContainer ACEs ('Everyone' does not match). allow-write maps to a single readwrite grant. | partial | Pipes with NULL DACLs and brokers built for AppContainers stay reachable; the battery enumerates \\\\.\\pipe\\. |\n| SEC-06 Forge tokens via sandbox-owned helpers (important) | Per-project container store, root-interpreter credential helper, gh shim | Per-project store under the sandbox state dir (gh file storage, not Credential Manager), granted only to that project's sessions. The credential helper runs from the fixed interpreter with -I. The gh shim is first on the session PATH. | equivalent | `clean` deletes the store, as recreating the container does. Tokens are readable in-session, as on Linux. |\n| SEC-07 Curated git config (important) | GIT_CONFIG_GLOBAL=/etc/claude-gitconfig, SYSTEM=/dev/null | GIT_CONFIG_GLOBAL points at a launcher-written file outside grants; GIT_CONFIG_NOSYSTEM=1 | equivalent | Battery checks that Git for Windows' %ProgramData%\\Git\\config is ignored. Avoid NUL (\\Device\\Null ACL issues). |\n| PRIV-01 Whole tree confined, no broker runs code outside (core) | Namespace inheritance; brokers hidden by /tmp and /run/user masks | The AppContainer token is inherited by every child; kill-on-close job. Task Scheduler, WMI, out-of-proc COM, BITS, WinRM and wsl.exe are gated by ACLs that lack AppContainer ACEs. | partial | Each broker unverified; battery probes schtasks, Win32_Process.Create, Shell.Application and wsl.exe. |\n| PRIV-02 No privilege gain on exec (core) | NO_NEW_PRIVS | No setuid on Windows. The AppContainer token has privileges stripped and Administrators deny-only; elevation from AppContainer is blocked. The launcher refuses to run elevated. | equivalent | UAC is not a boundary, but the AppContainer is. |\n| PRIV-03 No effective capabilities (core) | --cap-drop ALL; locked mounts and netns | No privileges; low IL; the BFE DACL denies filter changes; PSEC owns the policy in an elevated OS context | equivalent |  |\n| PRIV-04 Process isolation (core) | PID namespace; outer /proc visible read-only | No PID namespace. MIC no-read-up/no-write-up on process objects blocks VM read/write, DUP_HANDLE and remote threads into medium processes. | equivalent | Process list and likely command lines are visible, the same exposure Linux accepts. |\n| PRIV-06 Terminal input injection blocked (core) | script(1) pty + private /dev/pts | Launcher-owned ConPTY; outer console handles excluded via HANDLE_LIST; the agent cannot open the launcher or terminal (MIC); UIPI blocks SendInput into the terminal window | partial | AttachConsole/DuplicateHandle routes unverified (spike). Without our own ConPTY, WriteConsoleInput could queue input for the parent shell. |\n| PRIV-07 Devices denied by default (important) | --dev /dev; allow-device validated --dev-bind; gpu flag | Device objects need AppContainer ACEs, so devices are denied by default. No known per-device grant; allow-device and gpu refuse on Windows in the MVP. | partial | USB-serial lab adapters and CUDA under BaseContainer unverified. |\n| IPC-01 Desktop and IDE IPC hidden (core) | tmpfs /tmp, /run/user; DISPLAY/DBUS scrubbed | MXC ui: Win32k disabled (default), clipboard none, injection false, job UI limits; VS Code IPC pipes need AppContainer ACEs | partial | PowerShell needs Win32k (ui 'desktop'). Read-type messages and screen painting cross IL when Win32k is on. |\n| IPC-02 Outer loopback unreachable (important) | Private netns loopback; explicit relays only | hostLoopback=deny blocks loopback both ways | partial | Equivalent with no relays configured. Any local-port or callback-port sets hostLoopback=allow, which is bidirectional; per-port narrowing via egress rules is unverified. AF_UNIX sockets are files, so path grants apply. |\n| FS-01 Read-only system, bounded writable set (core) | --ro-bind / / plus ordered rw binds | readwritePaths/readonlyPaths/deniedPaths; system dirs readable via ALL APPLICATION PACKAGES, never writable; per-session temp deleted at exit | equivalent | Grants at volume roots do not recurse; upward traversal is denied, which affects git discovery. |\n| FS-02 Workspace scoped to the launch dir (important) | $PWD or workspace-root | readwrite grant on the launch dir or the configured workspace-root | equivalent | Refuse drive roots and UNC workspaces. |\n| INT-01 Launch code cannot be redirected (core) | Shim execs root-owned CPython by absolute path with -I; tools by fixed path | Compiled shim execs the pinned CPython (copied, with a ._pth) in the install root by absolute path with -I. The install root sits outside every grant (per-user %LOCALAPPDATA%\\Programs\\claude-sandbox, or %ProgramFiles%). wxc-exec, System32 tools and git run by absolute path. | equivalent | policy.py must refuse any grant covering the install root, conf or shim dir. Per-user matches Linux, where container root is the developer. |\n| INT-02 Launch policy outside the writable set (core) | /etc/claude-sandbox.conf, ro; env overrides only from the launching shell | %ProgramData%\\claude-sandbox (admin DACL) or the per-user state dir (never granted); never read from the workspace | equivalent | Battery checks the agent cannot write HKCU\\Environment. A VS Code workspace terminal.integrated.env.windows setting can still inject env vars, as on Linux. |\n| INT-03 Entry points always reach the wrapper (core) | Vendor binaries off PATH; /dev/null ro-binds in writable PATH dirs; launch refusal | The shim dir comes first on PATH (system PATH per-machine, first user-PATH entry per-user); claude.exe is relocated off %USERPROFILE%\\.local\\bin. The launch refuses when any agent name x PATHEXT extension sits ahead of the shim or in the cwd. The session sets NoDefaultCurrentDirectoryInExePath. | partial | cmd.exe searches the cwd before PATH. Whether deniedPaths can pre-block files that do not exist yet is unverified. App Execution Aliases compete for names. |\n| INT-04 Sandbox code and agent binaries read-only (core) | Root-owned libexec under ro root; masks over vendor binary copies | Install root not granted; claude.exe and shipped skills readonly grants; vendor update dirs not granted | equivalent |  |\n| INT-05 No vendor self-update (important) | /etc managed settings + Codex managed config + env | Claude: C:\\Program Files\\ClaudeCode\\managed-settings.json or HKLM, or the HKCU policy value (applied only when no admin document exists, and not writable from the container), plus DISABLE_UPDATES in env. Codex: env only. | partial | Codex's Windows managed-config path is unverified. Binaries are unwritable in-session either way. |\n| NET-01 Private network stack the agent cannot reconfigure (core) | Own netns via unshare -rn + pasta | No unprivileged netns: host interfaces, routes and loopback are shared and enumerable. The agent cannot change routes or WFP filters. | partial | Cannot reconfigure: equivalent. Own stack and identity hiding: none. |\n| NET-02 Destination allowlist (core) | Blackholes for RFC1918, CGNAT, connected, link-local, WireServer; gateway, forwarder and allow-ip /32 | egress default deny. Allow 0/1 and 128/1 `except` 10/8, 172.16/12, 192.168/16, 100.64/10, 169.254/16, 168.63.129.16/32, 0/8, 127/8, 224/4, 240/4 and every on-link prefix (GetAdaptersAddresses). One /32 allow per allow-ip. Never put the ranges in egress.deny, because deny beats allow and would kill every allow-ip. | partial | An allow-ip Windows classes as private (domain intranet) needs ingress.default=allow, which opens private inbound: make it an explicit opt-in. Whether SMB through the kernel redirector bypasses this is unverified. Gateway is blocked (stricter than Linux). |\n| NET-03 Exact read-back, fail closed (core) | ip route/rule read back; must() refuses | MXC returns typed errors (no fallback) for egress rules PSEC cannot enforce. The launcher probes the tier and runs a pre-agent self-test under the same policy, expecting WSAEACCES for each blocked class. | partial | Non-admins cannot enumerate WFP filters; exact read-back needs the C helper. |\n| NET-04 IPv4 only (core) | pasta --ipv4-only + check_ipv6 | No IPv6 allow rules under default deny; MXC programs IPv4-mapped destinations at the v4 layers | equivalent | Host IPv6 addresses stay visible. |\n| NET-05 DNS only through a forwarder (important) | resolv.conf to 192.0.2.53 forwarder; real resolvers not routed | getaddrinfo through the Dnscache service (outside container filters); internal names resolve as on Linux; direct 53/853 to internal resolvers blocked | equivalent | Dnscache's LLMNR/mDNS act on the LAN for the agent (disclosure). nslookup and c-ares fail. Unverified under PSEC (spike). |\n| NET-06 Explicit relays only (important) | Per-port socat over 0600 Unix sockets | No relays; hostLoopback=allow plus an egress allow 127.0.0.1/32 tcp N | weak | Bidirectional and all-ports until per-port loopback narrowing is verified; C's helper restores per-port. Default local-port off on Windows. |\n| OPS-01 Fail closed (core) | Refuse on missing bwrap, pasta, tun or bad conf | Refuse when: the build is below the floor; the tier is not BaseContainer; wxc-exec, the shim or the interpreter is missing or fails its hash; conf is invalid; the launch is elevated; devices or gpu are set. allowDaclMutation=false; never --audit, permissiveLearningMode or captureDenials allow. | equivalent | CLAUDE_SANDBOX_EGRESS_JAIL=0 keeps its meaning. |\n| OPS-04 Deterministic verification (important) | 22-check bash battery via `claude-sandbox verify`; adversarial skill | verify-sandbox-battery.ps1 (Windows PowerShell 5.1, by absolute path, read-only) run in the same container policy; Windows section in the verify skill | equivalent | New code; check list in repo_placement. |\n| OPS-05 Lifecycle and cleanup (supporting) | --die-with-parent; relay and holder cleanup; SIGHUP tree | MXC kill-on-close job; SetConsoleCtrlHandler (CTRL_CLOSE_EVENT gives 5 s); per-session temp removed | equivalent | Detached dev servers die with the agent, as on Linux. |\n| OPS-06 No host admin rights (important) | Rootless podman + unprivileged userns | PSEC tier: none to install (per-user) or launch; the OS update and feature enablement are IT prerequisites | partial | Equivalent on supported builds; none on Win10, Server or pre-August-2026 builds (refused). |\n| CTR-01 Host filesystem absent (core) | Container mounts only the project, state and config | No container layer; BaseContainer deny-by-default is the only wall | partial | One layer, like Linux custom devcontainers. |\n| CTR-02 Host environment not forwarded (important) | Engine -e list of CLAUDE_SANDBOX_* only | Single layer: the explicit env block (SEC-03) | partial |  |\n| CTR-03 Escape lands as the unprivileged developer (core) | Rootless uid mapping | The agent is the developer's SID in an AppContainer at low IL; an escape lands at the developer's medium IL | equivalent | For admin developers, medium IL plus a UAC bypass gives admin. Recommend non-admin accounts or Administrator protection. |\n| CTR-05 One container per project (important) | Named container per project | Per-project dirs (forge store, temp, state) keyed by workspace path; concurrent containers allowed | partial | No per-project outer shell or installed-tool isolation. |\n| WIN: API-reachable secrets (DPAPI, Credential Manager, WAM/PRT) | n/a (no analogue). Secret Service is unreachable: /run/user masked, D-Bus scrubbed. | Blob files outside grants are unreadable. CredRead/CredEnumerate (gh and Git Credential Manager defaults), CryptUnprotectData and WAM from BaseContainer are untested. | partial | Spike blocker. Claude Code's own login is a file (~/.claude/.credentials.json). |\n| WIN: Domain SSO / Kerberos / NTLM | n/a (no analogue; no host ccache in container) | Never grant enterpriseAuthentication; LSASS refuses default-credential network auth to AppContainers | equivalent | Entra PRT via WAM unverified. |\n| WIN: Same-user process injection | PID namespace + ptrace check (PRIV-04) | MIC: low IL cannot OpenProcess medium processes (Code.exe, browsers, terminal) for write, VM or thread access | equivalent | Battery probes OpenProcess on the launcher. |\n| WIN: UAC / auto-elevation | n/a (no analogue; NNP, no setuid) | AppContainer token cannot auto-elevate; the launcher refuses an elevated launch | equivalent |  |\n| WIN: Console input injection | TIOCSTI via script(1) (PRIV-06) | Own ConPTY; HANDLE_LIST; MIC on launcher | partial | See PRIV-06; spike blocker. |\n| WIN: Current-directory executable search | n/a (no analogue) | NoDefaultCurrentDirectoryInExePath in the session; doctor recommends it user-wide; launch refuses agent names planted in the cwd | partial | A workspace git.exe or .bat still runs when the developer types `git` in cmd there (ADR 28 analogue). |\n| WIN: SMB redirector / UNC paths | n/a (netns blackholes CIFS; mounting needs privilege) | Depends on BaseContainer denying \\\\server paths and mapped drives, and on no enterpriseAuthentication | partial | Unverified; srt saw SID-keyed WFP miss SMB. Spike blocker at DLS (file servers). |\n| WIN: World-writable machine locations (C:\\ subfolders, ProgramData, Public) | n/a (ro root) | Authenticated Users ACEs do not match AppContainer tokens | equivalent | This is where A beats B. |\n\n### Rewrite assessment\n\n**Verdict:** the audit core, the installer's system and provisioning steps, the shims, the battery and the e2e tests are rewritten. Config, profiles, tokens, gitconfig, the managed-settings merge and the CLI carry over. Line counts are from `wc -l` (repo-seams): `src/claude_sandbox` is 7,374 lines.\n\n**Reuse unchanged (about 480 lines):** `__main__` (adds a win32 `_shadow` branch), `_version`, `errors`, `cli` (lazy imports only), `gitconfig` (interpreter path becomes a parameter), `helpers/pi_local`, `host/commands`.\n\n**Adapt behind platform seams (about 2.5k lines, about 1.8-2.0k of them OS-neutral):**\n\n| Module | Change |\n|---|---|\n| `__init__` | `install` on Windows runs the Windows installer |\n| `context` | detect the jail from TokenIsAppContainer as well as IS_SANDBOX; per-OS 'installed' check |\n| `config` | per-OS conf path; Windows refuses tun, passt, allow-device and gpu |\n| `profiles` | Windows profile data, `.exe` names, ntpath basename |\n| `tools` | lazy termios, msvcrt key read, fixed System32 tool dirs, DACL-aware `write_atomic` |\n| `helpers/commands` | verify through the ps1 battery; version stamp; lazy doctor import |\n| `helpers/auth` | getpass/msvcrt |\n| `helpers/tokens` | owner SID plus protected DACL instead of uid/0700 |\n| `installer/actions` | no O_NOFOLLOW/chown; no symlinks |\n| `installer/steps` | reuse the managed-settings JSON merge, conf, version and skills planning with Windows paths |\n\nAbout 150-250 changed lines in total.\n\n**Linux-only, untouched on Linux, replaced on Windows (3,356 lines):** `bwrap.py` 508, `jail.py` 998, `shadow.py` 593, `helpers/doctor` 346, `installer/{__init__,__main__,system,provision}`. Move the range constants (BLACKHOLES, LINK_LOCAL, WIRESERVER) from `jail.py` into a neutral `netranges.py` (about 40 lines) so both cores share one list.\n\n**Container route `host/` (1,007 lines):** unchanged. Used only by F and G; G needs 300-450 changed lines.\n\n**New Windows code (option A):**\n\n| Module | Role | Rough LOC |\n|---|---|---|\n| `windows/policy.py` | \u2248`bwrap.py`. Pure function (profile, config, env) \u2192 MXC request: grants, env block, ui, fallback, network via `net.lower()`. Refuses grants over the install root or conf. | 400-600 |\n| `windows/net.py` | \u2248policy half of `jail.py`. On-link prefixes, `except` lists, allow-ip, local-port and callback lowering, private-class opt-in, the pre-agent self-test. | 250-400 |\n| `windows/launch.py` | \u2248`shadow.py`, same step order: recursion guard, binary and PATH checks, conf, gitconfig, prepare dirs, tier probe, ConPTY plus relay and resize, exec wxc-exec, wait, exit code, ctrl handler, cleanup. | 500-700 |\n| `windows/win32.py` | ctypes declarations only: ConPTY, pipes, STARTUPINFOEXW, proc-thread attribute list, console modes, SetConsoleCtrlHandler, GetAdaptersAddresses, token queries, SID and DACL helpers. | 300-500 |\n| `windows/install.py` + `provision.py` | Pinned uv Windows zip; CPython 3.13 copied with `._pth`; venv; prune; compileall; fetch and verify wxc-exec and the shim; relocate claude.exe; managed settings; per-user or per-machine. | 500-800 |\n| `windows/doctor.py` | Windows doctor checks | 150-250 |\n| Non-Python | C shim (GetCommandLineW tail; prepend `\"\" -I -m claude_sandbox _shadow  --`; pass exit code) 50-150; `install.ps1` bootstrap 100-150; `verify-sandbox-battery.ps1` 300-500 | 450-800 |\n| Tests | Policy golden tests (\u2248`test_argv`), net lowering, launch plumbing with fakes, on-runner e2e. About 0.7-2k of `tests/python` is reusable. | 1.5-2.5k |\n\n**Totals:** about 2.1-3.3k lines of new Windows Python, about 0.4-0.65k of PowerShell, about 0.1k of C, and about 2k lines of tests.\n\nWithout MXC (hand-built AppContainer, ACL and WFP code in ctypes, as repo-seams' 3-5k estimate assumed), add 1.5-2.5k lines of the riskiest code.\n\nDo not rebase the Linux core on MXC's own bubblewrap backend: it lacks the egress jail, the entry guard and the battery contract.\n\n### Repo placement\n\n**Recommendation:** placement option (a): one package, one wheel, one tag, one docs site, one threat model. A `claude_sandbox.windows` backend is selected by `sys.platform`, and neither core imports the other.\n\nRejected alternatives:\n- A second distribution conflicts with ADR 23's refuse-list ('a second console script or package').\n- A separate repo breaks ADR 3's 'one canonical, audit-friendly home', and its copies of the conf grammar, deny-lists, token helper and profiles would drift.\n\n**Layout:**\n```\nsrc/claude_sandbox/\n  __init__.py __main__.py cli.py context.py config.py profiles.py\n  gitconfig.py tools.py errors.py _version.py netranges.py   # shared, import-safe on both OSes\n  bwrap.py jail.py shadow.py                 # Linux audit core (unchanged)\n  host/  helpers/  installer/                # Linux/container side (lazy Unix imports)\n  windows/\n    policy.py net.py launch.py               # Windows audit core\n    win32.py                                 # ctypes declarations only\n    install.py provision.py doctor.py\n.devcontainer/claude-sandbox/                # Linux bash bootstrap, shims, battery (unchanged)\nwindows/\n  install.ps1  verify-sandbox-battery.ps1    # force-included in the wheel\n  shim/shim.c  pins.toml                     # SHA-256 of shim, wxc-exec, uv zip\ntests/python/ (shared, linux)  tests/python/windows/\nskills/verify-sandbox/ (+ references/windows-checks.md)\n```\n\n**Guardrail amendments (CLAUDE.md and the claude-sandbox skill):**\n1. **Stdlib-only.** No third-party import anywhere. Win32 is reached only through ctypes, with every declaration in `windows/win32.py`. Native binaries the jail depends on (bwrap and pasta on Linux; wxc-exec.exe and the shim on Windows) are pinned by SHA-256, installed outside every agent-writable path and run by absolute path. Refuse the MXC Rust, .NET and Node SDKs.\n2. **Fixed interpreter on Windows.** A pinned CPython copied, not linked, into the install root with a `._pth` file. The install root is `%LOCALAPPDATA%\\Programs\\claude-sandbox` (per-user, never granted to a container) or `%ProgramFiles%\\claude-sandbox` (per-machine). It runs by absolute path with `-I`; never from uv's cache, PATH, App Execution Aliases or `py.exe`. On Windows, `uvx claude-sandbox` may install but never builds a jail.\n3. **Single-policy-module rule.** No path grant, environment variable, capability or network rule enters the MXC request outside `windows/policy.py` (network rules via `windows/net.py`, called only from it).\n4. **Small audit core.** Exactly two cores of three modules each, named in an ADR, each readable top to bottom. `win32.py` holds declarations, not policy. Shared logic lives in neutral modules.\n5. **What stays non-Python.** Add Windows PowerShell 5.1 for the bootstrap and the battery, and one C shim built and signed in CI. Bootstraps stay bootstraps.\n6. **Fail closed on Windows.** BaseContainer tier only; `allowDaclMutation=false`; never `--audit`, permissive or capture-allow modes; refuse elevated launches.\n7. **Import safety.** Every module imports on both OSes; `test_wheel` imports per platform.\n\n**CI:**\n- Keep the ubuntu jobs. Add `pyright --pythonplatform Windows` (runs on Linux).\n- **windows-latest** (Server 2025, 10.0.26100; admin with UAC disabled): uv sync --locked, ruff, pytest for shared and Windows unit and golden tests, ConPTY plumbing and the import-everything test. It cannot run PSEC, which is Windows 11 only.\n- **windows-11-arm** (Windows 11 Enterprise 25H2, 10.0.26200.9457, above the .9278 floor): a probe job. If MXC reports BaseContainer, run the e2e as a standard local user created in the job: wxc-exec launch, battery and the network self-test. The Azure runner gives real negative targets in 168.63.129.16, 169.254.169.254 and its 10.x subnet. Otherwise skip, and make a self-hosted DLS Windows 11 x64 runner the required e2e gate.\n- Hosted runners cannot test WSL2 plus podman (nested virtualization is not officially supported) or Windows Sandbox. F's e2e needs a self-hosted Windows host or manual runs.\n- Coverage: upload `.coverage` per OS and run `coverage combine` before `fail_under=95`.\n\n**Packaging:**\n- One py3-none-any wheel built on Linux by hatch-vcs, one tag.\n- Add the classifier 'Operating System :: Microsoft :: Windows :: Windows 11'.\n- The shim and wxc-exec are not in the wheel. The release workflow builds and signs the shim on windows-latest and attaches it to the GitHub release. The installer downloads it and the MXC release asset and verifies the SHA-256 values from `pins.toml`, as `install.sh` does for uv.\n- Add `.gitattributes` `eol=lf` for scripts.\n\n**Docs (Di\u00e1taxis):**\n- tutorials: getting-started-windows\n- how-to: run-on-windows-with-wsl2 (F), install-on-windows (A), verify-on-windows, use-from-a-windows-host (G, if built)\n- reference: windows-defences, windows-verification-checks, a Windows column in configuration.md, a Windows section in deliberately-exposed.md\n- explanations: windows-sandbox-internals, and threat-model deltas (shared network stack, OS-brokered traffic, Windows-only routes)\n\n**Integrity battery (`verify-sandbox-battery.ps1`):**\n- Sentinel: IS_SANDBOX set.\n- Token: AppContainer, low IL, no privileges, Administrators deny-only.\n- Profile listing: exact.\n- Environment: allowlist exact; no GH_TOKEN or ANTHROPIC_API_KEY.\n- Credential stores: CredEnumerate empty; DPAPI canary fails.\n- Named pipes refused: ssh-agent, docker_engine, VS Code IPC.\n- Process access: OpenProcess on the launcher and AttachConsole fail.\n- Write denials: C:\\\\ subfolder, %ProgramData%, HKCU\\\\Environment, HKCU\\\\Software\\\\Policies, install root, conf.\n- Entry points: Get-Command claude resolves to the shim; no planted names.\n- Domain identity: Kerberos default-credential request fails.\n- git: curated config in effect; system config ignored.\n- Network:\n  - blocked: RFC1918, CGNAT, connected subnet, IMDS, WireServer, global IPv6, broadcast and multicast, an unlisted loopback port, UNC to a blocked host;\n  - allowed: internet and each allow-ip.\n- Brokers refused: schtasks, Win32_Process.Create, Shell.Application, wsl.exe.\n- Tier and pins: MXC tier recorded; wxc-exec and shim hashes match.\n\nThe verify skill gains Windows adversarial probes: junction, hard-link and ADS escapes, 8.3 names, \\\\\\\\?\\\\ paths. ADR 14's separation holds: no shared check library.\n\n**ADRs:**\n- **New ADR 30, Native Windows backend on MXC process containers.** Amends:\n  - ADR 26: ctypes rule, interpreter location, second core, PowerShell and C artefacts.\n  - ADR 23: host install on Windows.\n  - ADR 9/18: compiled shim; claude.exe relocation.\n  - ADR 13: Windows managed-settings sources, including the HKCU fallback.\n  - ADR 24: read-only grant replaces per-skill binds; mechanism open.\n  - ADR 27/28/12: PATHEXT, cwd-first search, NoDefaultCurrentDirectoryInExePath.\n  - ADR 6/29: per-project store without a container.\n  - ADR 11: Windows profile layout.\n  - ADR 16: pinned binaries.\n- **New ADR 31, Windows egress policy.** The MXC lowering of ADR 15: `except` instead of deny, behavioural verification, private-class opt-in, Dnscache. Amends ADR 15 and ADRs 20/21 (relays become hostLoopback).\n- **New ADR 32, WSL2 as the supported Linux-toolchain route.** WSL masks in `bwrap.py`, the peers guard, doctor checks, the NAT gateway decision. Amends ADR 15 and ADR 4.\n- Kept: ADR 3, one home. Nothing is superseded outright.\n\n### Plan\n\n- **0. Decide and spike** (1.5-2 person-weeks): Answer the use-case question and de-risk A\n  - Deliverables: - User and fleet survey: needs, Win11 build and BaseContainer state, admin rights, AppLocker/WDAC, EDR\n- Spike report and the seed request JSON\n- ADR 30 draft with a go/no-go\n  - Exit criteria: Use case recorded. Spike success criteria met (go) or the failing route named (no-go, stop at F).\n- **1. WSL2 baseline (F), in every case** (1-2 person-weeks): Make Windows-via-WSL2 a supported, checked configuration\n  - Deliverables: - bwrap.py --tmpfs /mnt, /mnt/wslg and /run/WSL when WSL is detected\n- --peers guard refusing parents that contain /mnt//Users/\n- Doctor WSL section: refuse WSL1; warn on interop and automount; report networking mode; probe the NAT gateway for Windows host services\n- Battery checks: /mnt/c unreadable; MZ stub cannot run\n- How-to\n- ADR 32\n  - Exit criteria: Battery and the new checks pass on Windows 11 WSL2 in NAT and mirrored modes with rootless podman (in-distro and container routes); docs published\n- **1b. Windows-host launcher (G), optional** (1-2 person-weeks): Launch the Linux container from PowerShell or Windows VS Code\n  - Deliverables: - Import safety\n- Path mapping\n- ntpath peers guard\n- Absolute engine path\n- Console-close cleanup\n- msvcrt key read\n- Tests\n  - Exit criteria: `uvx claude-sandbox claude` on Windows 11 with podman machine passes the battery; Docker Desktop refused with a clear message\n- **2. Cross-platform groundwork** (1-2 person-weeks): Shared modules importable and tested on Windows\n  - Deliverables: - Lazy Unix imports\n- netranges.py split\n- Seams in config, profiles, tools and tokens\n- pyright Windows pass\n- windows-latest unit job\n- coverage combine\n- Per-platform test_wheel\n  - Exit criteria: `uvx claude-sandbox --version` and `doctor` run on windows-latest; Linux golden argv tests and coverage unchanged\n- **3. Native core MVP for Claude Code (A)** (4-6 person-weeks): Sandboxed `claude` on a Windows 11 25H2 host as a standard user\n  - Deliverables: - windows/policy.py, net.py, launch.py, win32.py\n- wxc-exec integration with ConPTY relay\n- Per-user installer and provisioner\n- C shim\n- HKCU managed settings\n- Refusal logic\n- Policy golden tests\n  - Exit criteria: - `claude` from cmd, PowerShell, Windows Terminal and the VS Code terminal launches sandboxed with no UAC prompt\n- Every property-table row marked equivalent is confirmed by a probe\n- Launch refuses on a non-PSEC host, when elevated, and with a planted claude.cmd in the cwd\n- **4. Verification** (2-3 person-weeks): Deterministic and adversarial assurance on Windows\n  - Deliverables: - verify-sandbox-battery.ps1\n- `claude-sandbox verify` on Windows\n- Windows section of the verify skill\n- Required e2e gate: self-hosted Windows 11 runner, or windows-11-arm if BaseContainer is on\n  - Exit criteria: Battery green on every PR; an adversarial pass with no open high findings\n- **5. Breadth and preview release** (2-4 person-weeks): Usable preview for DLS\n  - Deliverables: - Codex profile, and Pi if it runs natively\n- Per-machine hardened install (Program Files, HKLM, admin-ACL conf)\n- Forge flows with file token storage\n- Shipped skills\n- Di\u00e1taxis docs\n- Signed-shim release pipeline\n- ADRs 30 and 31 accepted\n  - Exit criteria: Tagged release with Windows marked preview; docs live; at least a few DLS users run it for two weeks without a sandbox-caused blocker\n- **6. Triggered hardening (C, or B fallback)** (3-6 person-weeks (helper); 10+ (fallback)): Close gaps the spike or fleet require\n  - Deliverables: SYSTEM WFP helper for per-port loopback, callback and exact read-back; or adopt srt-win as the pre-PSEC fallback\n  - Exit criteria: Defined by the trigger; NET-06 and NET-03 rise to equivalent if the helper ships\n\n### Spike\n\n**Goal:** prove or kill option A on its biggest unknown. The unknown has two halves: whether the whole agent and DLS toolchains run inside an MXC BaseContainer launched by wxc-exec.exe from a launcher-owned ConPTY, and whether the Windows-only secret and lateral routes are closed. Time-box 5-8 working days.\n\n**Steps:**\n1. **Host.** Windows 11 25H2 at the current cumulative update (build at or above 26200.9278), ideally a DLS domain-joined image with real GPO, AppLocker and EDR. The developer works as a standard user. Install Git for Windows, Python, Node, VS Build Tools and the .NET SDK machine-wide, plus native Claude Code. Set up live secrets for the probes: gh logged in (Credential Manager), a Git Credential Manager entry, a mapped drive to a DLS file share, and ssh-agent with a key loaded.\n2. **MXC binary.** Fetch a pinned MXC release. Record wxc-exec's SHA-256 and Authenticode signer. Run the support probe and record the tier and ingress support. Confirm a request with `fallback.allowDaclMutation=false` fails with a typed error, not a fallback, on a host or VM without BaseContainer.\n3. **Request.** Hand-write the request policy.py would emit:\n   - readwrite: workspace, %USERPROFILE%\\\\.claude, per-session temp\n   - readonly: claude.exe and toolchain dirs\n   - env: explicit, inheritDefaultEnv=false\n   - ui: Win32k on with desktop handle isolation, clipboard none, injection false\n   - network: egress default deny; allow 0/1 and 128/1 with `except` the Linux ranges plus on-link prefixes; /32 for DLS GitLab; ingress and hostLoopback deny\n4. **Launch.** A ~100-line ctypes ConPTY harness starts wxc-exec. Run a 30-minute interactive Claude session:\n   - TUI, resize, Ctrl-C\n   - Bash tool via Git Bash; PowerShell tool\n   - edit and git commit; uv venv plus pytest; npm install\n   - dotnet build and a cl.exe or MSBuild hello-world\n   - git push over HTTPS to the allow-ip GitLab, with ingress.default deny and then allow, to see whether Windows classes it as private\n5. **Negative probes (from the agent's shell).** Each of these must fail:\n   - read ~\\\\.ssh\\\\id_*\n   - CredEnumerate\n   - CryptUnprotectData on a canary blob\n   - connect to \\\\\\\\.\\\\pipe\\\\openssh-ssh-agent\n   - dir the mapped drive and \\\\\\\\fileserver\\\\share\n   - Invoke-WebRequest -UseDefaultCredentials to an intranet Kerberos URL\n   - TCP to a connected-subnet host, 10/8, 172.16/12, 192.168/16, 100.64/10, 169.254.169.254 and a global IPv6 address\n   - UDP broadcast on 5064 (EPICS CA search)\n   - OpenProcess(VM_READ) on Code.exe\n   - AttachConsole(launcher) plus WriteConsoleInput\n   - schtasks /create; Win32_Process.Create; wsl.exe\n   - writes to C:\\\\src, %ProgramData% and HKCU\\\\Environment\n   - junction and hard-link escapes from the workspace\n\n   Separately, from cmd outside the session, plant claude.cmd in the workspace and type `claude`: the launch must refuse.\n6. **Overhead.** Measure launch time and build-time overhead against an unsandboxed run.\n\n**Success criterion:** all of the following, as a standard user with no UAC prompt:\n- (a) BaseContainer reported; no silent fallback anywhere.\n- (b) Claude's TUI and both shell tools, git, uv/pytest, npm and at least one of dotnet or MSVC work.\n- (c) Every probe in step 5 fails, apart from routes Linux also exposes (internet egress, DNS resolution).\n\nAny success in CredEnumerate, DPAPI, SMB/UNC, Kerberos or console attach is a no-go for A unless a policy setting closes it. Output: a go/no-go note plus the request JSON, which seeds the policy.py golden tests.\n\n### Risks\n\n- **[high]** MXC/PSEC is new: GA 2026-10-07, the OS API is undocumented on learn.microsoft.com, and early GA bugs exist (unrelated mapped drives or locked BitLocker volumes block startup; rename and delete denied, [codex#52978](https://github.com/openai/codex/issues/52978)). Monthly cumulative updates or MXC releases may change behaviour.. *Mitigation:* Pin wxc-exec by hash; probe every launch; fail closed; nightly e2e on a real Windows 11 host; label the backend preview; keep request generation pure so the backend can move to mxc_ffi or direct PSEC.\n- **[high]** Windows-only secret and lateral routes are unverified from BaseContainer: Credential Manager, DPAPI, WAM, reuse of the developer's SMB sessions or mapped drives (kernel redirector outside per-container WFP), console attach.. *Mitigation:* These are spike blockers and permanent battery checks; deniedPaths for UNC and mapped drives if needed; no-go if any stays open.\n- **[high]** Toolchain compatibility tail: Git Bash/MSYS2, MSVC (mspdbsrv, vswhere COM), dotnet build servers, PowerShell needing Win32k, per-user installs. Codex's tracker had about 684 Windows-sandbox issues on 2026-10-12.. *Mitigation:* Spike first; publish a supported-toolchain matrix; recommend machine-wide installs; narrow read-only grants per toolchain in profiles.\n- **[high]** Fleet readiness: A needs Windows 11 24H2/25H2 at build .9278 or later (26H1 at 28000.2804 or later) with the BaseContainer feature enabled; Windows 10, Server and older builds get nothing.. *Mitigation:* Survey in phase 0; refuse clearly; route those users to F; consider srt-win-style B only if demand justifies it.\n- **[medium]** Enterprise controls block or flag the install: AppLocker/WDAC rules against executables in %LOCALAPPDATA% (per-user Python, shim); SmartScreen on an unsigned shim; EDR flagging AppContainer launches.. *Mitigation:* Signed shim; a per-machine install packaged by DLS IT; engage IT during phase 0.\n- **[medium]** Network fidelity below Linux: shared stack and identity; hostLoopback is bidirectional and all-ports; a private-class allow-ip opens private inbound; no exact read-back without admin; OS-brokered DNS (LLMNR/mDNS).. *Mitigation:* Default local-port off on Windows; private-class opt-in with a warning; pre-agent behavioural self-test; document in deliberately-exposed; option C helper if needed.\n- **[medium]** Audit-core growth and reviewer expertise: a second core in Win32 territory the Linux maintainers may not know (the 'spread the core' failure of [claude-sandbox#14](https://github.com/DiamondLightSource/claude-sandbox/issues/14)).. *Mitigation:* MXC does the security-critical Win32 work; win32.py holds declarations only; an ADR names the two cores and their rules; seek an external review of the Windows core before GA.\n- **[medium]** Nested sandboxes: Codex's MXC mode, and Claude Code's future MXC support, may fail or silently degrade inside our container.. *Mitigation:* Test nesting in phase 5; configure vendor sandboxes explicitly per profile; record the result.\n- **[medium]** Ongoing maintenance: Windows updates, MXC releases, vendor path or updater changes for claude.exe on Windows.. *Mitigation:* Budget about 1-2 days a month; pins plus nightly e2e; doctor checks relocation drift.\n- **[medium]** WSL2 route specifics: /mnt and interop exposure for in-distro installs; --peers can mount /mnt/c/Users/ read-write; the NAT gateway is the Windows host; one kernel shared across distros.. *Mitigation:* Phase 1 deliverables and ADR 32; threat-model text widened for WSL.\n\n### Open questions\n\n- Use case: do DLS users need Windows-native toolchains, and how many users is that?\n- Fleet: Windows 11 build mix, BaseContainer enablement, local-admin rights, AppLocker/WDAC and EDR policy on DLS desktops.\n- From inside BaseContainer: do CredRead/CredEnumerate, CryptUnprotectData and WAM/PRT see the developer's secrets?\n- Can a BaseContainer process open UNC paths, mapped drives, or reuse the developer's SMB sessions? Does the kernel redirector bypass per-container WFP?\n- Can the agent AttachConsole to, or duplicate handles from, the launcher or terminal?\n- Do Git Bash/MSYS2, MSVC, dotnet and node-gyp run under BaseContainer, and with which grants?\n- Do egress rules apply to loopback, so that local-port relays could be narrowed per port?\n- Can PSEC grant a single file (~/.claude.json), and can deniedPaths protect paths that do not exist yet (entry guard)?\n- Does Windows class DLS ranges (for example GitLab 172.23.142.119) as 'private' on domain machines? That decides whether allow-ip forces private inbound.\n- Is BaseContainer enabled on the hosted windows-11-arm runner (Windows 11 Enterprise 26200.9457)?\n- Is wxc-exec.exe Authenticode-signed by Microsoft, and is its use outside testing supported? The README says 'for testing or when the SDK cannot be embedded'.\n- What is BaseContainer's MSRC servicing status? MSRC lists the AppContainer boundary but does not mention MXC.\n- Where is Codex's Windows managed config? Does Pi run natively on Windows?\n- How should shipped skills appear without bind mounts: a launcher-injected plugin dir, a managed-settings marketplace, or per-launch junctions?\n- Could a signed generic launcher (for example distlib's, with an -I shebang) replace a bespoke C shim?\n- Does cmd.exe honour NoDefaultCurrentDirectoryInExePath in every launch context DLS users use?\n- WSL NAT mode: are Windows host services reachable through the gateway the jail keeps open, and should ADR 15 blackhole it as a destination?\n- Should G (the Windows-host podman-machine launcher) be supported at all, or is the WSL terminal enough?\n\n### Load-bearing claims\n\n- [high] MXC is generally available as of 2026-10-07 (process container on Windows 11; microVM experimental); Claude Code support is 'coming soon'; Intune policy 'will soon be available' (https://blogs.windows.com/windowsdeveloper/2026/10/07/microsoft-execution-containers-policy-driven-containment-for-ai-agents/)\n- [high] MXC process isolation needs Windows 11 24H2/25H2 at build 26100/26200.9278 (KB5120998), 26H2 at 26300.9550, or 26H1 at 28000.2804; Windows 11 only (https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/os-version-support.md ; MXC README platform table)\n- [high] PSEC per-container WFP filters are scoped to the container SID and applied with no UAC prompt per launch; PSEC is the only ProcessContainer path with directional egress filters; explicit egress rules fail with a typed unsupported-policy error when PSEC cannot enforce them (https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/networking.md)\n- [high] The MXC 1.0.0 contract supports CIDR peers with an `except` list; deny takes precedence over allow; ingress has only `default` (private-network inbound) and a bidirectional `hostLoopback` (src/mxc-sdk/src/core/mxc_contract/published/v1_0_0/network.rs (microsoft/mxc))\n- [high] privateNetworkClientServer is one bidirectional capability, so reaching private-network addresses requires ingress.default=allow, which also permits private inbound (MXC networking.md, section 1)\n- [high] Each egress peer expands to at most 256 address blocks (MAX_BLOCKS_PER_PEER); a policy holds at most 65,536 entries (src/mxc-sdk/src/core/mxc_common/network_blocks.rs:75,81)\n- [high] fallback.allowDaclMutation defaults to true and Tier 3 modifies host DACLs; Tier 3 needs admin host-prep, including \\Device\\Null after every boot (https://github.com/microsoft/mxc/blob/main/docs/schema.md ; docs/backends/process-container/host-prep.md)\n- [high] wxc-exec.exe accepts stable-schema JSON requests 'for testing or when the SDK cannot be embedded'; native executors reserve stdio for the workload; --audit turns off all sandbox security (https://github.com/microsoft/mxc/blob/main/README.md)\n- [high] The Windows process container requires SYSTEMROOT and LOCALAPPDATA in env; ui.disable (default true) maps to the Win32k system-call disable (MXC docs/schema.md)\n- [high] MSRC services AppContainer, user, session and VM boundaries; administrators are part of the TCB (admin-to-kernel is not a boundary); MXC/BaseContainer is not listed (https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria (revised 2026-05-20))\n- [high] Claude Code's sandbox runs on macOS, Linux and WSL2; on native Windows it runs commands unsandboxed; the sandbox covers shell commands only (https://code.claude.com/docs/en/sandboxing)\n- [medium] Claude Code 2.1.265 fixed Read, Write and Edit inside an AppContainer or restricted-token sandbox on Windows; on Linux and Windows the login lives in ~/.claude/.credentials.json (https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md)\n- [high] Claude Code on Windows reads C:\\Program Files\\ClaudeCode\\managed-settings.json or HKLM\\SOFTWARE\\Policies\\ClaudeCode; it uses the user-writable HKCU value only when no admin document is present (https://code.claude.com/docs/en/managed-settings)\n- [medium] AppContainer without the enterpriseAuthentication capability cannot use the user's domain credentials (LSASS-enforced) (https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/app-capability-declarations (via win-process-isolation input))\n- [high] AppContainer 'private network' means local subnets plus AD/GPO ranges, not RFC1918; other addresses count as Internet (learn.microsoft.com hh831418 and troubleshooting-uwp-firewall (via win-network-egress input))\n- [medium] Non-admins cannot add or enumerate WFP filters (BFE default DACL) (https://learn.microsoft.com/en-us/windows/win32/fwp/access-control (via input))\n- [medium] Codex recommends MXC on Windows and labels its 'elevated' and 'unelevated' modes legacy; Cursor and Zed run their Linux sandbox in WSL2 instead of a native one (https://learn.chatgpt.com/docs/windows/windows-sandbox.md ; https://cursor.com/blog/agent-sandboxing ; https://zed.dev/docs/ai/sandboxing (via prior-art input))\n- [high] windows-latest is Windows Server 2025 (10.0.26100); windows-11-arm is Windows 11 Enterprise 10.0.26200 build 9457 (image 20261004.186.1); hosted Windows runners run as admin with UAC disabled; nested virtualization is not officially supported (actions/runner-images Windows2025-Readme.md and Windows11-Arm64-Readme.md; docs.github.com hosted-runners pages (via repo-seams input))\n- [high] Windows Sandbox allows one instance per host and `wsb exec` has no process I/O; Hyper-V-isolated containers on NAT cannot have their port ACLs reconfigured; Docker documents that docker-users can elevate to administrators (learn.microsoft.com windows-sandbox overview and CLI pages; network-isolation-security; docs.docker.com windows-permission-requirements (via win-vm-containers input))\n- [high] bwrap.py binds / read-only (bwrap.py:224) and masks no /mnt path; the --peers home guard compares only the Linux $HOME (host/launcher.py:447-457) (Repo: src/claude_sandbox/bwrap.py, src/claude_sandbox/host/launcher.py)\n- [high] In the Linux design 'root-owned' only means outside the jail's writable binds: container root is the developer. So a per-user Windows install outside every container grant matches the Linux trust model. (linux-properties implicit assumptions; ADR 26; docs/reference/deliberately-exposed.md)\n- [high] About 2.3-2.6k lines carry over; 3,356 Linux-only lines are replaced on Windows; the package is 7,374 lines (repo-seams wc -l measurements)\n- [medium] The CLI fails to import on Windows (termios at tools.py:15 and helpers/auth.py:13; signal.SIGHUP at jail.py:153 and host/launcher.py:41) (repo-seams (simulated on Linux, not run on Windows))\n- [high] The WSL2 kernel (linux-msft-wsl 6.6.y and 6.18.y) enables user, pid and net namespaces, SECCOMP and BINFMT_MISC, with TUN as a module (microsoft/WSL2-Linux-Kernel config-wsl (via wsl2-route input))\n- [high] Node.js refuses to spawn .bat or .cmd without shell:true since the CVE-2024-27980 fix (2024-04-10), so shims must be .exe (Node.js April 2024 security release (via repo-seams input))\n\n## Part 2: Adversarial fact-check and critique\n\n### Does the recommendation survive?\n\nOnly partly. Two parts hold. Delivering F (the existing stack in WSL2, container route) is sound and should be the only work committed now. Keeping any future Windows backend in this repo as one package is also reasonable, but only once an ADR openly accepts a second audit core and wxc-exec in the trusted base. The main recommendation, 'A, gated on the use case and a 1-2 week spike, 11-17 person-weeks to a preview', does not hold as written. Public MXC 1.0.0 issues ([mxc#1464](https://github.com/microsoft/mxc/issues/1464), [mxc#1061](https://github.com/microsoft/mxc/issues/1061), [mxc#1455](https://github.com/microsoft/mxc/issues/1455), [mxc#1461](https://github.com/microsoft/mxc/issues/1461), [mxc#1261](https://github.com/microsoft/mxc/issues/1261), [mxc#1465](https://github.com/microsoft/mxc/issues/1465), [codex#52978](https://github.com/openai/codex/issues/52978)) show that under BaseContainer today, git, Node path resolution, Git Bash, `cmd dir` and asyncio all fail unless the drive root is granted. A read-only drive-root grant on BaseContainer exposes the whole volume, which defeats SEC-01. So the spike's own success criteria (b) and (c) cannot both pass on current builds, and the fixes sit in Windows LCUs and MSYS2, outside DLS control. On managed DLS desktops, the per-user no-admin install is also likely blocked: AppLocker or WDAC on %LOCALAPPDATA%, HKCU\\Software\\Policies being admin-only, and PowerShell CLM all point to an IT-packaged per-machine install.\n\nThe recommendation should become:\n1. Deliver F via the container route (masks, peers guard, doctor, battery checks, ADR 32), plus guidance for a remote DLS Linux host.\n2. Answer the use-case and fleet questions, including whether Windows-native work needs lab devices, which A would refuse anyway.\n3. Put A on watch with explicit re-entry triggers:\n   - [mxc#1464](https://github.com/microsoft/mxc/issues/1464) and [mxc#1061](https://github.com/microsoft/mxc/issues/1061) fixed in a shipped LCU or MSYS2 release;\n   - the drive-root and volume-root semantics fixed;\n   - the 127.0.0.1/32 recipe confirmed;\n   - Credential Manager, DPAPI, SMB, Kerberos and console-attach probes shown closed;\n   - Claude Code's own MXC integration published.\n   When the triggers are met, run a re-spike of 1 week or less on a DLS-managed x64 image before amending ADR 26 or starting phase 2.\n4. Plan any eventual A as an IT-deployed per-machine install with HKLM managed settings, not a per-user one.\n\nC, B and the other options stay deferred or rejected, as the report says.\n\n### Claim checks\n\n- **CONFIRMED**: MXC went GA on 2026-10-07 (process container on Windows 11, microVM experimental). Claude Code support is 'coming soon'. Intune policy 'will soon be available'.\n  - Evidence: https://blogs.windows.com/windowsdeveloper/2026/10/07/microsoft-execution-containers-policy-driven-containment-for-ai-agents/ (Logan Iyer, 2026-10-07): MXC is 'now generally available'; MicroVM is 'experimental'; 'Anthropic Claude Code' is listed among agents that 'will be releasing support'; 'Intune policy will soon be available'. The GitHub API shows microsoft/mxc v1.0.0 published 2026-10-07 and not marked prerelease.\n  - Correction: Microsoft's wording is 'will be releasing support', with no date. The same post also announces a GA 'Session container', which 'runs under a distinct Windows account and session'. The report never evaluates it (see missing).\n- **CONFIRMED**: Process isolation needs Windows 11 24H2/25H2 at build 26100/26200.9278 (KB5120998), 26H2 at 26300.9550, or 26H1 at 28000.2804. Windows 11 only.\n  - Evidence: https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/os-version-support.md lists these builds. https://support.microsoft.com/en-us/servicing/os/windows-11/2026/08/kb5120998-windows-11-24h2-25h2-update: released 2026-08-27 as an 'Optional non-security preview update'; 'New! Process Isolation for Microsoft Execution Containers (MXC)'; it is a 'gradual rollout ... availability varies by device'. KB5124010 (.9550, 2026-09-22) is also an optional preview with gradual rollout.\n  - Correction: Both build floors are optional preview releases with gradual rollout. Managed fleets usually reach them only through the next Patch Tuesday update (for example 26200.9457), and availability can still vary by device. The verdict text also leaves out 26H2 (26300.9550).\n- **UNCERTAIN**: The host needs 'the BaseContainer feature enabled', which IT manages as a prerequisite.\n  - Evidence: https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/host-prep.md: Tier 3 is chosen when the BaseContainer API is 'present but not usable (the symbol resolves yet the feature is disabled)'. base_container_runner.rs treats ERROR_CALL_NOT_IMPLEMENTED as 'capability gated off'. MXC's CI doc says tier selection 'follows from the host's Windows build'. No Microsoft page says how to turn the feature on. https://learn.microsoft.com/en-us/windows/whats-new/temporary-enterprise-feature-control: features shipped in monthly updates can be 'automatically disabled for devices that have their Windows updates managed by policies'; MXC is not listed there (page updated 2025-10-17).\n  - Correction: Describe the gate as a gradual-rollout feature whose enablement on a managed device is undocumented. Make 'wxc-exec --probe on a real DLS-managed image' a phase-0 survey item instead of assuming IT can switch it on.\n- **CONFIRMED**: PSEC applies per-container WFP filters scoped to the container SID with no UAC prompt per launch. PSEC is the only path with directional egress rules. Rules PSEC can't enforce fail with a typed error.\n  - Evidence: https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/networking.md \u00a71: 'scoped to its container SID and applied with no UAC prompt per launch'. \u00a72: 'PSEC is the only ProcessContainer path that receives directional egress filters'; 'Explicit egress rules ... fail with a typed unsupported-policy error when PSEC cannot enforce them.' \u00a73: 'PSEC applies outbound WFP filters in the OS's elevated context'.\n  - Correction: None. The same doc also says 'Requests the PSEC contract cannot preserve continue to an AppContainer tier', so the launcher's own refusal on any other tier is still needed.\n- **CONFIRMED**: The 1.0.0 contract supports CIDR peers with `except` lists; deny beats allow; ingress has only `default` and a bidirectional `hostLoopback`.\n  - Evidence: src/mxc-sdk/src/core/mxc_contract/published/v1_0_0/network.rs: NetworkPeer {cidr, except}; 'Deny takes precedence over allow'; host_loopback is described as 'bidirectional host-loopback connectivity'. networking.md \u00a71.1: 'GA ingress is limited to the default and hostLoopback allow/deny toggles.'\n  - Correction: none\n- **CONFIRMED**: privateNetworkClientServer is one bidirectional capability, so reaching private addresses needs ingress.default=allow, which also opens private inbound.\n  - Evidence: networking.md: 'ProcessContainer therefore requires ingress.default: \"allow\" before the container can communicate with private-network addresses. Enabling it also permits private-network server traffic' and '...cannot allow private-network outbound while denying private-network inbound.'\n  - Correction: none\n- **CONFIRMED**: Each egress peer expands to at most 256 address blocks (MAX_BLOCKS_PER_PEER), and a policy holds at most 65,536 entries.\n  - Evidence: src/mxc-sdk/src/core/mxc_common/network_blocks.rs:75 MAX_BLOCKS_PER_PEER = 256 and :81 MAX_EGRESS_ENTRIES = 65_536. I recomputed the report's peers: 0.0.0.0/1 minus its except list gives 19 blocks; 128.0.0.0/1 gives 55, or 93 with three extra public on-link /16-/24s. Both fit.\n  - Correction: none\n- **CONFIRMED**: fallback.allowDaclMutation defaults to true, and Tier 3 rewrites host DACLs. Tier 3 needs admin host preparation, including \\Device\\Null after every boot.\n  - Evidence: https://github.com/microsoft/mxc/blob/main/docs/schema.md Fallback Policy table: default true, '\u26a0\ufe0f This modifies host filesystem security descriptors'. host-prep.md: wxc-host-prep is 'requireAdministrator'; \\Device\\Null 'resets to an AppContainer-hostile default at every boot'.\n  - Correction: none\n- **CONFIRMED**: wxc-exec.exe accepts stable-schema JSON 'for testing or when the SDK cannot be embedded'. Native executors reserve stdio for the workload. --audit turns off all sandbox security.\n  - Evidence: https://github.com/microsoft/mxc/blob/main/README.md quotes all three points. The v1.0.0 release notes (api.github.com/repos/microsoft/mxc/releases/tags/v1.0.0) say 'mxc-release-binaries.zip ... contains signed binaries that can be useful for testing'. The only asset is that 536 MB zip, with a published sha256.\n  - Correction: This settles one open question: the binaries are signed. Microsoft still positions them for testing, though; the supported surface is the Rust, .NET and Node SDKs. A stdlib-only Python consumer depends on the secondary path, and its installer must download 536 MB to extract one executable.\n- **CONFIRMED**: The process container requires SYSTEMROOT and LOCALAPPDATA in its environment, and ui.disable (default true) maps to the Win32k system-call disable.\n  - Evidence: schema.md Environment section: 'The Windows process container requires SYSTEMROOT and LOCALAPPDATA to be present'. UI Policy: disable 'maps to the Win32k system-call disable mitigation'.\n  - Correction: In practice ui.disable must be false for nearly every toolchain. [mxc#1465](https://github.com/microsoft/mxc/issues/1465) shows whoami, powershell, node and cl.exe all import USER32; only cmd and curl run with Win32k off. The 'Win32k disabled (default)' claim under IPC-01 does not hold for this workload.\n- **CONFIRMED**: MSRC services the AppContainer, user, session and VM boundaries; administrator-to-kernel is not a boundary; MXC/BaseContainer is not listed.\n  - Evidence: https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria lists the AppContainer sandbox boundary ('based on the container capabilities'), the user, session and VM boundaries, and the Administrator-to-Kernel non-boundary. It has no mention of MXC, BaseContainer or PSEC.\n  - Correction: none\n- **CONFIRMED**: Claude Code's sandbox runs on macOS, Linux and WSL2, runs commands unsandboxed on native Windows, and covers shell commands only.\n  - Evidence: https://code.claude.com/docs/en/sandboxing: 'The sandbox covers shell commands only. Claude's file tools, MCP servers, and hooks run outside it.' 'On native Windows, Claude Code runs commands unsandboxed.'\n  - Correction: That Claude Code's future MXC support will also cover commands only is an inference; nothing about it is published.\n- **CONFIRMED**: Claude Code 2.1.265 fixed Read, Write and Edit inside an AppContainer. The login lives in ~/.claude/.credentials.json on Windows.\n  - Evidence: https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md under '## 2.1.265': 'Windows: Fixed Read, Write and Edit refusing every file (\"symlink resolution changed after permission was checked\") when running inside an AppContainer or restricted-token sandbox'. An older entry reads 'Fixed credential save crash on Linux/Windows corrupting ~/.claude/.credentials.json'.\n  - Correction: none\n- **CONFIRMED**: On Windows, Claude Code reads C:\\Program Files\\ClaudeCode\\managed-settings.json or HKLM\\SOFTWARE\\Policies\\ClaudeCode, and uses the HKCU value only when no admin document is present.\n  - Evidence: https://code.claude.com/docs/en/managed-settings: file in C:\\Program Files\\ClaudeCode\\; HKLM\\SOFTWARE\\Policies\\ClaudeCode 'Settings'; 'the same Settings value under HKCU\\SOFTWARE\\Policies\\ClaudeCode' is used 'only when no admin document is present above it'.\n  - Correction: See the next item: the HKCU value is under Software\\Policies.\n- **UNCERTAIN**: Implied by INT-05 and the phase-3 deliverable 'HKCU managed settings': a per-user install with no admin rights can deliver Claude's updater controls through the HKCU policy value.\n  - Evidence: The value lives at HKCU\\SOFTWARE\\Policies\\ClaudeCode. https://helgeklein.com/blog/windows-7-default-hkcu-registry-permissions/ lists HKCU\\Software\\Policies as owned by SYSTEM, with a protected DACL: SYSTEM and Administrators full control, the user read-only. Claude's docs call HKCU 'user-writable' but give no detail. The ACL source is secondary and from the Windows 7 era; I did not re-check it on Windows 11.\n  - Correction: Check in the spike whether a standard user can create the key. If not, a per-user install has no managed-settings channel and the updater controls become env-only. The claude-sandbox skill refuses that ('They must stay in managed settings'). In that case an IT-deployed HKLM or Program Files policy is required.\n- **CONFIRMED**: An AppContainer without enterpriseAuthentication cannot use the user's domain credentials (enforced by LSASS).\n  - Evidence: https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/app-capability-declarations: enterpriseAuthentication is the capability that lets an app use 'Windows domain credentials' to reach remote resources. CVE-2020-1509 (Project Zero; https://www.securityweek.com/microsofts-patch-lsass-flaw-incomplete-google-researcher-says): 'LSASS doesn't correctly enforce the Enterprise Authentication Capability'. Authentication to registered proxies is allowed by design, and the first patch was incomplete.\n  - Correction: The enforcement is in LSASS and has been bypassed before. Add a probe for authentication to a configured WPAD/PAC proxy, which is an allowed target.\n- **CONFIRMED**: For AppContainers, 'private network' means local subnets plus AD/GPO ranges, not RFC1918; every other address counts as Internet.\n  - Evidence: https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh831418(v=ws.11): the intranet address space is 'composed of configured Active Directory sites and subnets' plus the GPO setting 'Private network ranges for apps'; 'All other endpoints ... are considered endpoints on the Internet.'\n  - Correction: On domain-joined DLS PCs the AD-site subnets (for example 172.23.x) are probably private-class. Treat 'allow-ip forces ingress.default=allow' as likely, not merely open.\n- **CONFIRMED**: Non-admins cannot add or enumerate WFP filters (default BFE DACL).\n  - Evidence: https://learn.microsoft.com/en-us/windows/win32/fwp/access-control: the default engine security descriptor gives Everyone only FWPM_ACTRL_OPEN and FWPM_ACTRL_CLASSIFY; Administrators get GENERIC_ALL; Network Configuration Operators get GR, GW and GX.\n  - Correction: Members of Network Configuration Operators can enumerate filters.\n- **CONFIRMED**: Codex recommends MXC on Windows and labels its elevated and unelevated modes legacy. Cursor and Zed run their Linux sandbox in WSL2.\n  - Evidence: https://learn.chatgpt.com/docs/windows/windows-sandbox: MXC is 'recommended' and the default; elevated is the 'preferred legacy fallback'; unelevated is the 'legacy fallback'; Codex 'uses MXC for Windows commands'. https://cursor.com/blog/agent-sandboxing (2026-02-18): 'On Windows, we run our Linux sandbox inside WSL2.' https://zed.dev/docs/ai/sandboxing: sandboxing is 'supported only when the agent action runs inside WSL'.\n  - Correction: none\n- **CONFIRMED**: windows-latest is Server 2025 (10.0.26100); windows-11-arm is Windows 11 Enterprise 10.0.26200.9457 (image 20261004.186.1); hosted Windows runners run as admin with UAC off; nested virtualization is not officially supported.\n  - Evidence: actions/runner-images README maps windows-latest to Windows Server 2025 (OS 10.0.26100 Build 33438). images/windows/Windows11-Arm64-Readme.md: Windows 11 Enterprise 10.0.26200 Build 9457, image 20261004.186.1. https://docs.github.com/en/actions/reference/runners/github-hosted-runners: 'Windows virtual machines are configured to run as administrators with User Account Control (UAC) disabled.' The concepts page says nested virtualization 'is not officially supported'.\n  - Correction: The facts hold. Repo fit covers what the CI plan built on them cannot do.\n- **CONFIRMED**: Windows Sandbox allows one instance per host and wsb exec has no I/O. Hyper-V-isolated NAT containers can't have their port ACLs reconfigured. docker-users members can elevate to admin.\n  - Evidence: learn.microsoft.com/.../windows-sandbox/: 'currently doesn't allow multiple instances to run simultaneously'. The windows-sandbox-cli page: 'no support for process I/O'. learn.microsoft.com/virtualization/windowscontainers/container-networking/network-isolation-security: Hyper-V isolation in NAT/Transparent mode 'can't reconfigure the default port ACLs'. docs.docker.com/desktop/setup/install/windows-permission-requirements/: 'members of the docker-users group are able to elevate to administrators' when Windows containers are enabled.\n  - Correction: none\n- **CONFIRMED**: bwrap.py:224 binds / read-only and masks no /mnt path; the --peers guard compares only the Linux $HOME (host/launcher.py:447-457).\n  - Evidence: Repo: bwrap.py:224 is '\"--ro-bind\", \"/\", \"/\",' and bwrap.py contains no '/mnt'. host/launcher.py:449-451 checks only f\"{self.home}/\".startswith(f\"{parent}/\").\n  - Correction: none\n- **CONFIRMED**: In the Linux design, 'root-owned' only means outside the jail's writable binds, so a per-user Windows install outside every grant matches the trust model.\n  - Evidence: docs/reference/deliberately-exposed.md: 'The supported setup is a root user inside rootless Podman on Linux.'\n  - Correction: Mostly holds. One difference: a per-user Windows install is a single interpreter and conf shared by all projects, and any unsandboxed process the developer runs can write it. Linux keeps one per project container.\n- **CONFIRMED**: About 2.3-2.6k lines carry over; 3,356 Linux-only lines are replaced; the package is 7,374 lines.\n  - Evidence: wc -l: 7,374 total. bwrap 508 + jail 998 + shadow 593 + doctor 346 + installer/{__init__ 88, __main__ 47, system 449, provision 327} = 3,356.\n  - Correction: Minor slips: the 'reuse unchanged' set sums to 549 lines, not about 480, and host/commands is counted twice. Several 'unchanged' modules need edits anyway (cli's lazy imports; gitconfig, see repo fit).\n- **CONFIRMED**: The CLI fails to import on Windows (termios at tools.py:15 and helpers/auth.py:13; signal.SIGHUP at jail.py:153 and host/launcher.py:41).\n  - Evidence: I read the files: those imports are at module top. config.py imports tools, and cli.py imports host.launcher at top level, so the failures cascade.\n  - Correction: none\n- **CONFIRMED**: The WSL2 kernels (6.6.y and 6.18.y) enable user, pid and net namespaces, SECCOMP and BINFMT_MISC, with TUN as a module.\n  - Evidence: raw.githubusercontent.com/microsoft/WSL2-Linux-Kernel/linux-msft-wsl-6.6.y (and 6.18.y)/arch/x86/configs/config-wsl: CONFIG_USER_NS=y, PID_NS=y, NET_NS=y, SECCOMP=y, BINFMT_MISC=y, TUN=m.\n  - Correction: none\n- **CONFIRMED**: Since CVE-2024-27980, Node refuses to spawn .bat or .cmd files without shell:true, so the shims must be .exe.\n  - Evidence: https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2: 'Node.js will now error with EINVAL if a .bat or .cmd file is passed to child_process.spawn ... without the shell option set.'\n  - Correction: none\n- **REFUTED**: BaseContainer makes A 'comparable to Linux for secrets held in files', and (FS-01) 'grants at volume roots do not recurse'.\n  - Evidence: [mxc#1455](https://github.com/microsoft/mxc/issues/1455) (open, 2026-10-09; 26200.9457, MXC 1.0.0, tier base-container): with C:\\ in readonlyPaths, the contained cmd lists %USERPROFILE% and prints a file outside every grant. The fix comment states that BaseContainer 'treats a drive-root readonly path as a recursive read of the subtree'. schema.md's 'does not cascade' wording covers readwritePaths only.\n  - Correction: A read-only grant on a drive root exposes the whole volume. policy.py must refuse drive-root grants, but that conflicts with git ([mxc#1464](https://github.com/microsoft/mxc/issues/1464)) and with pwsh 7, which MXC's own CI grants the drive root 'until pwsh 7.7 leaves preview'.\n- **REFUTED**: Toolchains: 'cmd and curl work'; git only 'refuses repos owned by Administrators'; Git Bash/MSYS2, MSVC, dotnet and node-gyp are untested.\n  - Evidence: [mxc#1464](https://github.com/microsoft/mxc/issues/1464) (open, 2026-10-10, 24H2 26100.9457, base-container): GetFinalPathNameByHandleW(VOLUME_NAME_DOS) always fails. `git init` gives 'fatal: unable to get current working directory: Permission denied' unless C:\\ is readable. Node fs.realpathSync.native returns EPERM on every path, even with C:\\ readable. [mxc#1061](https://github.com/microsoft/mxc/issues/1061) (open): MSYS2/Git Bash aborts with NtCreateDirectoryObject(\\BaseNamedObjects\\...) 0xC0000022; tier 1 was confirmed on 2026-10-10, and a Microsoft maintainer wrote that a fix needs MSYS changes plus 'In OS, we will need true namespace virtualization support for BNO'. [mxc#1461](https://github.com/microsoft/mxc/issues/1461) (26300.9550): `cmd /d /c dir` prints 'Access is denied.' [codex#52978](https://github.com/openai/codex/issues/52978) and [mxc#1204](https://github.com/microsoft/mxc/issues/1204): rename and delete are denied; Microsoft says an OS fix ships 'end of October'.\n  - Correction: As of 2026-10-12, the Windows dev loop does not run under BaseContainer without a drive-root grant, which voids SEC-01. That covers git status and commit, npm/Node path resolution, Git Bash (Claude's Bash and Monitor tools), git hooks, and the `!`-form credential helpers. The fixes are in Windows and MSYS2, outside DLS control, so the spike's criteria (b) and (c) cannot both pass on current builds.\n- **REFUTED**: NET-02's default (egress allow 0/1 and 128/1 except a list that includes 127/8; hostLoopback deny) is workable, and IPC-02 is 'equivalent with no relays'.\n  - Evidence: [mxc#1261](https://github.com/microsoft/mxc/issues/1261) (open, base-container): with egress deny and hostLoopback deny, Python socket.socketpair() hangs forever. That breaks the asyncio self-pipe and MCP SDK/FastMCP servers. An explicit egress allow for 127.0.0.1/32 with hostLoopback deny gives 'socketpair ok, host 127.0.0.1 listener blocked, internet blocked'.\n  - Correction: Remove 127/8 from `except` and add an explicit egress allow for 127.0.0.1/32, keeping hostLoopback deny. Add battery checks that loopback inside the container works and host loopback stays blocked. Linux's private netns gives in-jail loopback for free; MXC does not.\n- **REFUTED**: PowerShell needs Win32k '(ui relaxed to \"desktop\")', and the spike sets 'Win32k on with desktop handle isolation'.\n  - Evidence: https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/UIPolicy_Schema.md: `disable` (Win32k) and `isolation` are separate fields. isolation \"desktop\" sets neither UILIMIT_HANDLES nor GLOBALATOMS ('All handles on the desktop'). [mxc#1464](https://github.com/microsoft/mxc/issues/1464) ran git, cmd and PowerShell with only ui.disable=false.\n  - Correction: Set ui.disable=false and keep processContainer.ui.isolation at \"container\" (the default). Relaxing isolation to \"desktop\" gives up handle and atom isolation (IPC-01) for no gain.\n- **REFUTED**: Phase 3 and the spike: from cmd, with claude.cmd planted in the workspace, typing `claude` makes 'the launch refuse'.\n  - Evidence: The report itself notes that cmd.exe searches the current directory before PATH, so the planted claude.cmd runs instead of the shim and no launcher code runs to refuse it. Setting NoDefaultCurrentDirectoryInExePath 'in the session' affects only processes inside the container. Likewise, user PATH is appended after system PATH (Microsoft Q&amp;A, learn.microsoft.com/en-us/answers/questions/1159843), so a machine-wide claude runs unwrapped ahead of a per-user shim.\n  - Correction: Have the installer set NoDefaultCurrentDirectoryInExePath=1 in HKCU\\Environment (no admin needed), and have doctor verify both that and PATH order. Turn the exit criterion into: with that variable set, cmd resolves `claude` to the shim. Fail-open entry points must be prevented at install time; the shim cannot refuse what never runs.\n- **UNCERTAIN**: Whether Credential Manager is reachable from BaseContainer is an open spike blocker.\n  - Evidence: [mxc#607](https://github.com/microsoft/mxc/issues/607) (open, 2026-07-06) is a feature request: 'a way to allow access to the Windows Credential Manager'. That implies sandboxes lack access today, but I found no probe result.\n  - Correction: Keep it as a spike probe. Weak evidence points to closed.\n- **UNCERTAIN**: CI: a hosted windows-11-arm probe job, or a self-hosted DLS Windows 11 runner, can be the required e2e gate.\n  - Evidence: [mxc#1465](https://github.com/microsoft/mxc/issues/1465) (open, 2026-10-11, 25H2 26200.9550): when the caller is in session 0 (OpenSSH, services, 'CI build agents'), every USER32-importing workload (whoami, powershell, node, cl.exe) fails with STATUS_DLL_INIT_FAILED, whatever the ui settings. The same request works from an interactive session. Self-hosted Actions runners install as a service by default. windows-11-arm is ARM64.\n  - Correction: The self-hosted gate must be an auto-logon interactive runner. Hosted ARM64 e2e never runs the x64 wxc-exec, shim or CPython that DLS desktops would use. BaseContainer availability on the hosted image is also subject to gradual rollout.\n- **UNCERTAIN**: 'srt saw SID-keyed WFP miss SMB.'\n  - Evidence: The sandbox-runtime README's Windows (alpha) section describes the SID-keyed WFP fence and says nothing about SMB.\n  - Correction: Cite a source or drop the claim. The SMB/redirector route stays a spike probe either way.\n\n### Repo-fit issues\n\n- The report says 'exactly two cores of three modules each', but win32.py holds the STARTUPINFOEX/HANDLE_LIST, job and DACL code that PRIV-06 and the token store depend on. A ctypes misdeclaration fails silently. On top of that, wxc-exec (a fast-moving Rust executor shipped in a 536 MB zip) joins the trusted computing base. The audit surface roughly doubles, and the report never weighs that against ADR 3's principle that the surface must be 'small enough to audit in one read'.\n  - Rule: CLAUDE.md 'Keep the audit core small' (the bf65407 / [claude-sandbox#14](https://github.com/DiamondLightSource/claude-sandbox/issues/14) failure); ADR 26 audit core; ADR 3\n  - Fix: Name win32.py as core in the ADR. Treat wxc-exec as TCB, pinned by a per-file SHA-256 like bwrap and pasta. Budget an external Windows security review before any preview. State the doubled audit surface openly in ADR 30.\n- The updater controls have no managed-settings home on a per-user install. Program Files and HKLM need admin, and HKCU\\SOFTWARE\\Policies appears to be read-only for standard users. The report lists an 'HKCU managed settings' deliverable for a no-admin install and falls back to env vars.\n  - Rule: ADR 13; claude-sandbox skill: updater controls 'must stay in managed settings', and moving them to user scope is refused\n  - Fix: Make an IT-deployed HKLM or Program Files policy (via GPO or Intune) a prerequisite for A. Otherwise record an explicit ADR 13 exception with compensating controls (read-only claude.exe grant, launch refusal) and accept the regression.\n- Of the report's three candidate skill mechanisms, two are already refused. A managed-settings marketplace installs into the user's plugin cache. Junctions in ~/.claude/skills sit in a writable, host-shared directory, so the agent can replace them.\n  - Rule: ADR 24 and the claude-sandbox-shipped-skills refuse list ('Copying shipped skills into the user's ~/.claude'; marketplace 'installs into the user's plugin cache')\n  - Fix: Use only a read-only grant on the install-root skills directory, passed by the launcher as a Claude-only plugin or skills directory. Record that Codex and Pi lose shipped skills on Windows, or defer skills for them.\n- Entry points (Invariant 1) fail open on a per-user install. User PATH always follows system PATH, cmd searches the cwd first, and the VS Code extension launches its bundled binary unless claudeCode.claudeProcessWrapper is set (that setting appears in the Claude Code CHANGELOG). In each case the shim never runs, so its refusal never fires.\n  - Rule: ADR 9/18 (shadow on PATH); ADR 27's entry-point guard, kept by ADR 28\n  - Fix: Put the shim directory at the head of system PATH through a per-machine install. Have the installer set NoDefaultCurrentDirectoryInExePath in HKCU\\Environment. Have doctor check PATH order, App Execution Aliases and claudeProcessWrapper. Rewrite the impossible 'planted claude.cmd \u2192 refuse' exit criterion.\n- Some 'reuse unchanged' and 'adapt' estimates are too low. gitconfig's helpers use the `!` shell form (`helper = ! -I -m claude_sandbox _git_credential` and `!glab auth git-credential`). Git for Windows runs those through sh.exe, which is MSYS2 and broken under BaseContainer ([mxc#1061](https://github.com/microsoft/mxc/issues/1061)). Separately, installer/actions.py and helpers/tokens.py guard against links with O_NOFOLLOW and S_ISLNK. The report's answer, 'no O_NOFOLLOW; no symlinks', misses junctions: standard users and the agent can create them without privilege, and Python's islink and S_ISLNK do not report them.\n  - Rule: ADR 26 audit-core correctness; repo-seams reuse estimate\n  - Fix: Move gitconfig, actions and tokens to the 'adapt' column. Add dashed external git-credential-*.exe helpers (another compiled shim) and reparse-point-safe file operations (FILE_FLAG_OPEN_REPARSE_POINT, st_reparse_tag checks) for every launcher or installer path the agent can write. Add both to the estimate.\n- The PowerShell battery and install.ps1 meet enterprise script controls. Under WDAC or AppLocker script rules, PowerShell runs in Constrained Language Mode, which blocks the Add-Type/P/Invoke calls the CredEnumerate, OpenProcess and AttachConsole probes need. GPO ExecutionPolicy can also demand signed scripts. Inside the container, PowerShell additionally needs Win32k.\n  - Rule: ADR 26 (what stays non-Python); ADR 14 (self-contained battery)\n  - Fix: Either Authenticode-sign the scripts (and cost the signing infrastructure), or write the Windows battery as a separate, self-contained Python ctypes file run by the fixed interpreter. That still keeps ADR 14's separation: a separate file, no shared library.\n- The fixed interpreter is taken from 'uv's Windows zip'. Whether those CPython builds are Authenticode-signed is unverified. In %LOCALAPPDATA%, they may fail WDAC publisher rules or Defender ASR's prevalence and age rule, and AppLocker's default rules block executables in user-writable paths outright.\n  - Rule: ADR 26 fixed interpreter; the OPS-06 claim of 'no admin to install'\n  - Fix: Evaluate python.org's PSF-signed embeddable package, which already ships a ._pth file, and an IT-packaged per-machine install in Program Files as the default on DLS desktops.\n- Several CI claims won't hold as written. Hosted windows-11-arm is ARM64 only, while DLS desktops are x64. A self-hosted runner installed as a service runs in session 0, where [mxc#1465](https://github.com/microsoft/mxc/issues/1465) breaks every USER32 workload. Running 'coverage combine' needs a [tool.coverage.paths] mapping, and the ubuntu job's fail_under=95 fails once windows/ exists unless that job omits it. pyright strict over ctypes WinDLL declarations is real work under the 'fix code, don't relax' rule. Signing the C shim in CI needs a code-signing identity (DLS certificate or Azure Artifact Signing) that nothing in the plan provides.\n  - Rule: CLAUDE.md dev rules (\u226595% branch coverage, pyright strict)\n  - Fix: Specify an x64, auto-logon, interactive self-hosted runner as the gate. Add the coverage path mapping and per-OS omit rules. Cost the signing identity and pipeline in phase 5.\n- The refuse rule against 'a second console script or package' comes from the claude-sandbox-container skill, not from ADR 23's text. The F option's in-distro WSL install relies on CLAUDE_SANDBOX_HOST_INSTALL=1, which deliberately-exposed.md places outside the supported setup ('root user inside rootless Podman').\n  - Rule: ADR 23 and the container skill; docs/reference/deliberately-exposed.md\n  - Fix: Cite the skill for the second-package rule. Support F through the container route only, or have ADR 32 explicitly widen the supported setup.\n\n### Missing from the report\n\n- Known OS-level blockers are missing from the risks and the spike: [mxc#1464](https://github.com/microsoft/mxc/issues/1464) (git working directory, Node realpath), [mxc#1061](https://github.com/microsoft/mxc/issues/1061) (Git Bash/MSYS2, which needs OS namespace virtualization), [mxc#1455](https://github.com/microsoft/mxc/issues/1455) (drive-root grants recurse), [mxc#1461](https://github.com/microsoft/mxc/issues/1461) (volume-root attributes; cmd dir), [mxc#1261](https://github.com/microsoft/mxc/issues/1261) (loopback inside the container, asyncio), [mxc#1465](https://github.com/microsoft/mxc/issues/1465) (USER32 workloads from session 0) and [mxc#1204](https://github.com/microsoft/mxc/issues/1204) / [codex#52978](https://github.com/openai/codex/issues/52978) (rename and delete; Microsoft says the fix ships in the late-October LCU). The spike should first check which of these the current LCU fixes.\n- Microsoft's GA Session container (MXC isolation_session) is not evaluated. It runs the workload as a distinct Windows account in its own session, which MSRC services as user and session boundaries. It is unsuitable today: it rejects filesystem policy (no host-folder sharing), requires all-allow networking, and its API is Windows.AI.IsolationSession.Preview (mxc docs/development/architecture/backends/isolation-session/oneshot.md). It should be listed, rejected, and watched as a possible successor to option B and srt-win.\n- Other options are missing: MXC's WSLC backend (GA 'WSL container') as a host for G; a remote DLS Linux dev host reached through VS Code Remote-SSH or Tunnels, which needs no new code and competes with F for the 'Windows PC, Linux work' case; and an IT-managed Windows VM (Hyper-V or Dev Box) with network segmentation for Windows-native toolchains.\n- The use case conflicts with A's refusals. Windows-only instrument SDKs, drivers and LabVIEW usually need USB or serial devices and lab-network access. A refuses allow-device and gpu, blocks private ranges, and a private-class allow-ip opens private inbound. A fits building Windows code, not driving instruments.\n- Managed-estate realism is thin. AppLocker or WDAC default rules block executables under %LOCALAPPDATA%, so the per-user, no-admin install fails and an IT-packaged per-machine install becomes the real path. Also unaddressed: PowerShell Constrained Language Mode and ExecutionPolicy, the Defender ASR prevalence rule against fresh binaries, HKCU\\Software\\Policies being admin-only, and the cost of code signing.\n- How DLS IT gets BaseContainer switched on is undocumented. Both build floors are optional preview updates with gradual rollout, and the API has a 'feature disabled' state.\n- Linux defences dropped from the property table: the Chrome native-messaging channel (--no-chrome injection, locked-down-defences.md) and the SysV-IPC/named-object namespace and UTS rows. Also missing are Windows-only 'what a session leaves' channels: DLL search-order planting in the workspace, and git hooks and `!` credential helpers that run through sh.exe.\n- Launcher and installer reparse-point safety is not addressed. Junctions need no privilege to create, so agent-writable directories threaten the launcher's per-session temp cleanup and the token store.\n- Loopback inside the container is blocked by default, which breaks dev-server-plus-client workflows and asyncio and MCP servers ([mxc#1261](https://github.com/microsoft/mxc/issues/1261)). Neither the property table nor the toolchain notes account for it.\n- Delivering wxc-exec is not costed. The only release asset is a 536 MB zip described as 'useful for testing'.\n- The effort estimates are not credible as a schedule. '11-17 person-weeks to preview' depends on fixes from Microsoft and MSYS2 that DLS cannot control. '1-2 days a month' of maintenance ignores MXC's pace: five releases from 0.6.1 to 1.0.0 between June and October 2026, with pre-0.9 contracts retired. F's 1-2 weeks is plausible, but its end-to-end tests have to be manual or self-hosted.\n- There are no re-entry triggers or decision criteria for picking A up later.\n\n## Appendix: research digests\n\n### win-process-isolation\n\n**Recommendation:** Build the Windows jail on MXC's process container, pinned to its native BaseContainer (PSEC) tier. It is the only primitive that gives an unprivileged, deny-by-default path allowlist without changing the developer's ACLs. It also brings low-IL AppContainer identity (MIC and UIPI protect VS Code, terminals and browsers, and there is no domain SSO without enterpriseAuthentication), per-container WFP egress and ingress rules, job UI limits and kill-on-close, which makes it the closest Windows analogue of bwrap.\n\nFail closed, as the Linux jail does. Set fallback.allowDaclMutation=false and refuse to launch where the tier is unavailable: anything below Windows 11 24H2/25H2 with the 2026-08/09 updates, or a device where the feature is not enabled. Never drop silently to a restricted token or to DACL mutation.\n\nDo the script(1) step ourselves. The launcher owns a ConPTY, passes the agent only the PTY handles, and must be a process the agent cannot open.\n\nReject write-restricted tokens and low IL as the main isolation mechanism. They don't confine reads, and Credential Manager, DPAPI and SSO stay reachable. Keep the dedicated-standard-user pattern (Codex 'elevated') only if older Windows 11 builds must be supported. It needs admin once, a private desktop, per-user firewall rules and explicit lock-down of drive-root trees writable by Authenticated Users.\n\nStdlib-only Python is feasible:\n- ctypes covers ConPTY, job objects and handle lists.\n- MXC can be reached by subprocess to wxc-exec.exe (stable JSON schema) or by ctypes into mxc_ffi.dll. Either way the package ships a pinned Microsoft binary.\n- The zero-binary route, ctypes into processmodel.dll with a hand-built PSEC FlatBuffer, keeps ADR 26 literal but depends on an undocumented OS contract. That trade-off is the maintainer's call.\n\nAdmin once at install gives the /etc and /usr/libexec analogues: Program Files for an embeddable CPython with a ._pth file and for wxc-exec, and C:\\Program Files\\ClaudeCode\\managed-settings.json for the updater guard. Without admin, these go in user paths outside every container grant. That protects them from the agent, but not from the user's other processes.\n\nThis is a new jail core, not a port of bwrap.py, jail.py or shadow.py. The config, CLI, policy vocabulary and the verification-battery idea carry over.\n\nBefore committing, spike on a Windows 11 25H2 machine:\n- Git Bash/MSYS2, which Claude Code's Bash tool uses.\n- MSVC, dotnet and npm.\n- CredRead, DPAPI and WAM from inside the container.\n- AttachConsole and DuplicateHandle routes to the outer console.\n\nAlso track Microsoft's 'coming soon' MXC support in Claude Code. It would sandbox shell commands, not the whole agent process.\n\n**Key findings:**\n\n- Microsoft Execution Containers (MXC) reached GA on 2026-10-07. It is a policy-driven containment layer: an AppContainer-based process container, a session container (separate account and session) and a WSL container. It offers a JSON policy (stable schema 1.0.0), Rust/.NET/Node SDKs, a wxc-exec.exe CLI and a C ABI. GitHub Copilot and OpenAI Codex already use it, and Microsoft lists Anthropic Claude Code as 'coming soon'. https://blogs.windows.com/windowsdeveloper/2026/10/07/microsoft-execution-containers-policy-driven-containment-for-ai-agents/ , https://github.com/microsoft/mxc\n- MXC's preferred Windows tier is a new OS API: CreateProcessSecurityEnvironment (processmodel.dll) plus PROC_THREAD_ATTRIBUTE_SECURITY_ENVIRONMENT. It takes a 'PSEC' FlatBuffer of read-write/read-only/deny/enumerate paths, capabilities, Win32k disable, job UI limits and per-container WFP rules, needs no admin and makes no host-DACL changes. The API is not documented on learn.microsoft.com; the schema is vendored in MXC. https://github.com/microsoft/mxc/blob/main/external/windows-sdk/ProcessSecurityEnvironment.fbs , https://github.com/microsoft/mxc/blob/main/docs/development/guides/process-container-adding-os-features.md\n- Availability: process isolation shipped in KB5120998 (2026-08-27 optional preview, builds 26100/26200.9278), and VS Code's Windows agent sandbox requires the 2026-09-08 security update KB5124008. Without a usable BaseContainer, MXC falls back to AppContainer with host-DACL edits and admin prep (C:\\ root once, \\Device\\Null every boot) unless fallback.allowDaclMutation=false. https://support.microsoft.com/en-us/servicing/os/windows-11/2026/08/kb5120998-windows-11-24h2-25h2-update , https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/host-prep.md , https://code.visualstudio.com/docs/agents/run/agent-sandboxing\n- Codex ships three Windows modes. MXC is recommended: no admin, no extra accounts, no firewall rules, and leftover children are killed on exit. 'Elevated' uses dedicated sandbox users, ACLs, firewall rules and a private desktop, with admin once. 'Unelevated' uses a write-restricted token derived from the user, which 'doesn't support denied read paths'. https://learn.chatgpt.com/docs/windows/windows-sandbox.md , https://github.com/openai/codex/blob/main/codex-rs/mxc-sandbox/README.md , https://github.com/openai/codex/blob/main/codex-rs/windows-sandbox-rs/src/token.rs\n- Claude Code has no native-Windows sandbox: it 'runs commands unsandboxed' and points users to WSL2. Its sandbox covers shell commands only; file tools, MCP servers and hooks run outside it. So a whole-agent wrapper keeps its value even after Claude Code adopts MXC; the request is open as anthropics/claude-code#69495. https://code.claude.com/docs/en/sandboxing , https://github.com/anthropics/claude-code/issues/69495\n- Microsoft services AppContainer, user and session isolation as security boundaries. UAC is defence-in-depth only, and Russinovich: 'Protected Administrator' equals 'Administrator'. Integrity levels and restricted tokens are not listed. https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria , https://learn.microsoft.com/archive/blogs/markrussinovich/psexec-user-account-control-and-security-boundaries\n- Low IL and write-restricted tokens do not confine reads, because MIC defaults to no-write-up only. CredRead works whenever the user SID is enabled, and SSPI SSO stays available. AppContainer needs the enterpriseAuthentication capability to use domain credentials, enforced in LSASS (the CVE-2020-1509 bypass was fixed). https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control , https://learn.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credreadw , https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/app-capability-declarations\n- Job objects are lifecycle tools, not containment. 'Child processes created using Win32_Process.Create are not associated with the job', and WMI and Task Scheduler are known job escapes. https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects , https://projectzero.google/2015/05/in-console-able.html\n- Console injection: WriteConsoleInput writes into the input buffer that every process on a console shares. A launcher-owned ConPTY is the script(1) analogue, effective only if the agent cannot open the launcher or terminal. Microsoft documents the same risk for sudo's inline and disableInput modes. https://learn.microsoft.com/en-us/windows/console/writeconsoleinput , https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session , https://learn.microsoft.com/en-us/windows/advanced-settings/sudo/\n- Separate-user designs have Windows-specific leaks. CreateProcessWithLogonW shares the caller's logon SID, so Codex adds a private desktop. The default C:\\ ACL grants Authenticated Users Modify on subfolders, and default named-pipe DACLs grant Everyone read. https://github.com/openai/codex/blob/main/codex-rs/windows-sandbox-rs/src/desktop.rs , https://stigaview.com/products/win11/v2r2/WN11-00-000095 , https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew\n- Toolchains break in practice. Git Bash fails under Codex's restricted-token modes (codex#15016). PowerShell fails with STATUS_DLL_INIT_FAILED under full job UI limits. Per-user Python cannot run in AppContainers without grants. git rejects repos created elevated. The ssh-agent and Docker Desktop pipes are denied. https://github.com/openai/codex/issues/15016 , https://github.com/microsoft/mxc/blob/main/tests/playground/playground-limitations.md , https://github.com/openai/codex/issues/51955 , https://github.com/openai/codex/issues/41415\n- No unprivileged bind-mount view exists. CreateBindLink is admin-only and system-wide. Server silos need admin and are not a serviced boundary. Win32 app isolation still requires MSIX and is stuck in preview, with the repo idle since 2025-04. https://learn.microsoft.com/en-us/windows/win32/api/bindlink/nf-bindlink-createbindlink , https://projectzero.google/2021/04/who-contains-containers.html , https://learn.microsoft.com/en-us/windows/win32/secauthz/app-isolation-overview\n\n**Unverified:**\n\n- Whether CredRead/CredEnumerate from an AppContainer or BaseContainer process returns the developer's Credential Manager entries (gh, Git Credential Manager) or an empty per-AppContainer vault; and the behaviour at low IL. Write-restricted tokens are expected to read them.\n- Whether CryptUnprotectData on the developer's existing user-scope DPAPI blobs succeeds from AppContainer, BaseContainer or low IL.\n- Whether WAM/TokenBroker and the Entra PRT/BrowserCore SSO path are reachable from AppContainer or BaseContainer.\n- Whether a sandboxed process can AttachConsole(pid) to, or duplicate console handles from, the launcher or terminal when it cannot open those processes. Believed blocked; not tested.\n- Behaviour of Git Bash/MSYS2 (Cygwin fork emulation, shared memory, signal pipes) under MXC BaseContainer. Only restricted-token failures are documented.\n- Behaviour under AppContainer or BaseContainer of MSVC (cl/link, mspdbsrv, vswhere/SetupConfiguration COM), MSBuild node reuse, dotnet build servers, and npm/node-gyp. No primary data found.\n- Readability of HKCU (for example HKCU\\Environment holding setx-stored tokens) from AppContainer or BaseContainer.\n- Whether MXC session containers (IsolationSession) need admin or the admin-only 'experimental agentic features' toggle, and how a workspace is shared into the per-run agent account.\n- MSRC servicing status of MXC/BaseContainer itself. InfoQ (June 2026) quoted preview docs saying MXC profiles 'should not yet be treated as security boundaries'; the GA post says nothing either way.\n- Process enumeration and command-line visibility of other users' or medium processes from AppContainer or BaseContainer.\n- How BaseContainer availability is gated after KB5124008: staged feature rollout versus build number. Codex says 'rolling out', and MXC falls back when 'the feature is disabled'.\n- Whether python -I skips registry PythonPath entries (HKCU\\Software\\Python\\PythonCore\\\\PythonPath). The docs only promise this for ._pth layouts.\n- Hard-link and junction semantics under BaseContainer path grants; Codex lists these as still needing Windows validation.\n- That ProjFS requires enabling an optional Windows feature with admin; not checked in this session.\n- Whether VS Code's Windows agent sandbox uses MXC internally. Likely, given the KB requirement and Copilot's listing, but its docs do not say.\n\n### win-vm-containers\n\n**Recommendation:** A heavy native-Windows backend is feasible, but it is a new backend, not a port. Nothing from bwrap.py, jail.py or shadow.py carries over. What does carry over is the shape:\n- a stdlib-only Python launcher calling tools by absolute path;\n- explicit mount and environment allow-lists;\n- config outside the workspace;\n- vendor updaters off;\n- a deterministic battery, re-implemented for Windows.\n\nIt could live in this repo as a separate subtree with its own small audit core.\n\nThe closest structural match to today's design is a Hyper-V-isolated Windows container per project:\n- Server Core ltsc2025 image with Git, Python, Node and VS Build Tools baked in;\n- `docker run --rm -it --isolation=hyperv --user ContainerUser`, driven through docker.exe the way podman is driven today;\n- VM-grade boundary, concurrency, a real terminal and image-based toolchains.\n\nIts costs:\n- one-time admin, the Containers and Hyper-V features, and a reboot;\n- per-launch docker-users membership, which Docker says is admin-equivalent;\n- a Docker Desktop subscription at DLS's size, or the less-supported Moby route;\n- multi-GB images.\n\nWindows Sandbox is the best zero-install option: built in, clean every launch, and lockable by GPO. But it allows one instance per host, has no stdio, defaults to an admin guest user and offers all-or-nothing networking. That makes it a single-session mode at best; reuse MXC's design, or MXC itself, rather than reinventing it.\n\nThe decisive gap is egress. No heavy option reproduces the Linux egress jail out of the box on a Windows 11 client. The most promising design keeps Cohort B's IP/CIDR/UDP semantics:\n- an in-guest WFP policy set by an admin bootstrap (default-outbound-block, allow public ranges plus allow-ip, DNS only to the gateway);\n- the agent run as a separate standard guest user;\n- the rules read back before the agent starts.\n\nA no-route-plus-host-relay design is the alternative, but it cannot carry UDP device traffic and ADR 0015 rejected env-var proxies.\n\nBefore committing, prototype:\n1. Windows Sandbox or a Hyper-V VM with that guest policy and a non-admin agent.\n2. A Hyper-V container on a no-NAT network.\n3. Claude Code and Codex TUIs under `docker -it` and under a ConPTY relay.\n\nExpect MXC's process and session containers (another workstream) to cover most users once Claude Code supports them. Keep a VM-backed mode for higher-risk work, or as a second layer of defence.\n\n**Key findings:**\n\n- Windows Sandbox runs only one instance at a time per host. The overview page, updated 2026-10-08, says it 'currently doesn't allow multiple instances to run simultaneously', so per-project concurrency is impossible: https://learn.microsoft.com/en-us/windows/security/application-security/application-isolation/windows-sandbox/\n- The 24H2 wsb CLI cannot connect a host terminal. 'Currently, there is no support for process I/O' for `wsb exec`, and ExistingLogin needs an RDP session opened with `wsb connect`. A launcher would need its own nonce-authenticated TCP relay plus ConPTY in the guest: https://learn.microsoft.com/en-us/windows/security/application-security/application-isolation/windows-sandbox/windows-sandbox-cli\n- Windows Sandbox networking is just Enable or Disable on the Hyper-V Default Switch. Microsoft warns it 'can expose untrusted applications to the internal network'. Defaults are on for networking, clipboard redirection, audio input and vGPU (minimum memory 2048 MB, default cap 4 GB). The default user WDAGUtilityAccount is an administrator: https://learn.microsoft.com/en-us/windows/security/application-security/application-isolation/windows-sandbox/windows-sandbox-configure-using-wsb-file\n- Microsoft's own MXC Windows Sandbox backend (experimental) supports block-all networking only: 'allow-lists, block-lists, proxies, and unrestricted outbound access are unsupported'. Its guest locks the firewall with netsh and then runs workloads under the same account. So nothing Microsoft ships gives an 'internet yes, LAN no' policy for a Windows Sandbox guest: https://github.com/microsoft/mxc/blob/main/docs/backends/windows-sandbox/windows-sandbox.md\n- MSRC (page last revised 2026-05-20) says process-isolated Windows Server Containers 'are not intended to be used in hostile multitenancy scenarios', while Hyper-V isolated containers fall under the serviced VM boundary. Only --isolation=hyperv is acceptable for an agent: https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria\n- For Hyper-V-isolated containers on the client's default NAT (or Transparent) network, 'currently you can't reconfigure the default port ACLs'. So a host-enforced RFC1918 block needs a different network design. HNS endpoint ACLs (RemoteAddresses, Block, Out) exist for other modes: https://learn.microsoft.com/en-us/virtualization/windowscontainers/container-networking/network-isolation-security\n- Docker: enabling Windows containers means 'members of the docker-users group are able to elevate to administrators on the host', and ContainerAdministrator 'is a local administrator on the host machine'. Windows containers work only with an all-users install, on Pro or Enterprise. Docker Desktop is paid for organisations above 250 employees or $10M revenue: https://docs.docker.com/desktop/setup/install/windows-permission-requirements/\n- On Windows 11 clients Hyper-V isolation is the default, and Windows Server 2025 images run in both modes from 24H2. Process isolation on a client is 'meant for development/testing', and the base-image EULA limits client hosts to 'test or development purposes only': https://learn.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility\n- Toolchain images. Compressed sizes from MCR, queried 2026-10-12: servercore:ltsc2025 2.23 GiB, nanoserver:ltsc2025 0.18 GiB, server:ltsc2025 6.20 GiB. Docker's official python images ship only windowsservercore variants, and there are no Windows node images. Microsoft documents VS Build Tools in servercore/.NET Framework images (needs -m 2GB). Nano Server lacks PowerShell and WMI: https://learn.microsoft.com/en-us/visualstudio/install/build-tools-container\n- MXC went GA on 2026-10-07 for process, session and WSL containers. Its VM-class backends (windows_sandbox, microvm, hyperlight) are experimental, and only windows_sandbox runs a Windows guest. A JSON request to wxc-exec.exe can be driven from stdlib Python. Claude Code support is 'upcoming': https://blogs.windows.com/windowsdeveloper/2026/10/07/microsoft-execution-containers-policy-driven-containment-for-ai-agents/\n- Claude Code on native Windows has no sandbox: it 'runs commands unsandboxed'. Its sandbox covers shell commands only, not file tools, MCP servers or hooks. On WSL2, Windows binaries launch on the host over a Unix socket: https://code.claude.com/docs/en/sandboxing\n- The upper-bound options are moving. Dev Box entered its closing-down period on 2026-09-14 and retires on 2028-09-18; Microsoft recommends Windows 365. Windows 365 supports WSL, Windows Sandbox and Hyper-V inside Cloud PCs with 4 vCPU or more. Anthropic's own Claude Desktop Cowork on Windows runs a Linux VM through HCS (needs Hyper-V; Pro, Enterprise or Education): https://learn.microsoft.com/en-us/azure/dev-box/dev-box-retirement-guide\n\n**Unverified:**\n\n- Whether Windows Sandbox (or a Hyper-V-isolated container) registers a Hyper-V firewall VMCreatorId, which would let New-NetFirewallHyperVRule block LAN egress from the host. The HCN schema's FirewallVMType lists only Host, WSA and WSL.\n- Whether a standard user can launch Windows Sandbox on the 24H2 Store build without UAC or Hyper-V Administrators membership. The only evidence is a 2019 test on 1903.\n- Real launch latencies: Windows Sandbox boot to a ready guest, Hyper-V container start, Hyper-V VM boot. Microsoft says only 'a few seconds' for Windows Sandbox; a third-party blog claims 5-10 s for Hyper-V containers and 1-2 s for process isolation.\n- Whether a bootstrap can run the agent inside Windows Sandbox as a separate non-admin local user (CreateProcessWithLogonW and the seclogon service), making the in-guest firewall agent-proof.\n- That MXC's Windows Sandbox workloads can undo the guest firewall. This is inferred from firewall.rs and executor.rs: netsh lockdown, then cmd.exe in the same account. Not tested live, and the UAC state inside the guest is unknown.\n- Named-pipe mounts and `--network none` for Hyper-V-isolated containers on Windows 11, and whether Docker can attach containers to a custom no-NAT HNS network (Internal or Private) created through the HCN API.\n- Whether host Windows Firewall outbound rules filter WinNAT-forwarded traffic from Default Switch or container NAT guests.\n- Whether Windows Sandbox guest traffic follows the host's VPN routes into internal networks (likely, since it is NAT through the host).\n- Claude Code and Codex TUI fidelity under `docker run -it` with Windows containers, and inside a ConPTY relay.\n- Whether Claude Code and Git for Windows run on Nano Server. Claude Code documents Windows Server 2019+ but says nothing about containers.\n- How Windows Sandbox mapped folders handle symlinks and junctions: whether a guest-created junction can be traversed to host paths.\n- Whether VS Build Tools can run from a read-only mapped install inside Windows Sandbox, given the missing registry and COM registration (vswhere, MSBuild discovery).\n- Whether single-file bind mounts are rejected for Docker Windows containers. MXC confirms Windows Sandbox shares directories only.\n- Windows Sandbox domain and credential isolation: no primary doc states it is not domain-joined. This is inferred from the local WDAGUtilityAccount and the disposable image.\n- Visual Studio Build Tools licensing for container and VM use at DLS.\n- Absolute image sizes on disk (only compressed MCR sizes were measured), and the size of a Server Core image with Build Tools installed.\n\n### wsl2-route\n\n**Recommendation:** Treat WSL2 as a supportable, high-fidelity baseline \u2014 NOT the target \u2014 and make it an explicit, checked configuration rather than the current undocumented accident. Concretely, with minimal, ADR-26-compatible changes: (1) In src/claude_sandbox/bwrap.py, when WSL is detected (a stdlib file check, e.g. /proc/sys/fs/binfmt_misc/WSLInterop exists or 'microsoft' in /proc/version), add `--tmpfs /mnt`, `--tmpfs /mnt/wslg` and `--tmpfs /run/WSL` to the argv \u2014 these are binds in the one allowed place, closing the /mnt/c secret-exposure and the interop-socket/WSLg channels. This is the one must-fix; without it the jail is strictly weaker on WSL than on Linux. (2) Add a `claude-sandbox doctor` WSL section: refuse WSL1; warn when [interop] enabled or [automount] enabled is true (parse /etc/wsl.conf), when passt is too old, and surface networkingMode (NAT vs mirrored) so the operator knows to re-run the verify battery. (3) Add a how-to 'Run on Windows via WSL2' documenting: WSL2-only; set wsl.conf [interop] enabled=false and [automount] enabled=false (belt-and-braces with the /mnt mask); keep the project in the ext4 VHD, not /mnt/c; the shared-kernel/VM caveat; that no Intune policy enforces interop/automount; and that this route cannot build or test Windows-native code. (4) Extend the verify-sandbox battery with two checks: /mnt/c is not readable from inside the jail, and an MZ stub cannot be launched via WSLInterop. Prefer entry point (i) with rootless podman; call out that Docker Desktop's WSL2 backend is weaker (shared engine VM) and that podman machine is closer to the supported model. Position all of this as the comparison baseline against which the separate native-Windows (AppContainer/restricted-token) design is judged \u2014 the WSL route gives full Linux-sandbox fidelity but zero Windows-native toolchain coverage, so a team that must build MSVC/.NET/PowerShell code still needs the native-Windows effort.\n\n**Key findings:**\n\n- The repo today claims only Linux + rootless podman; the sole non-Linux mention is docs/how-to/use-the-container-image.md:197 ('macOS with a Linux VM are untested; rootful Docker cannot host the default jail'). No README/tutorial/how-to mentions WSL or Windows. So the WSL route is currently undocumented and unchecked. (/scratch/hgv27681/work/claude-sandbox/docs/how-to/use-the-container-image.md)\n- The WSL2 kernel supports every primitive the jail needs: CONFIG_USER_NS, PID_NS, NET_NS, VETH, SECCOMP(+FILTER), BINFMT_MISC all =y and TUN=m on both linux-msft-wsl-6.6.y (stable) and 6.18.y (WSL 3.0.x). The inner jail therefore runs unchanged. (https://github.com/microsoft/WSL2-Linux-Kernel/blob/linux-msft-wsl-6.18.y/arch/x86/configs/config-wsl)\n- src/claude_sandbox/bwrap.py does `--ro-bind / /` and masks only $HOME, /tmp, /run/user, /run/secrets \u2014 it never masks /mnt or /run/WSL (confirmed by grep: no /mnt match). With default [automount] the Windows C: drive is readable read-only inside the jail. This is the single most important change the route needs. (/scratch/hgv27681/work/claude-sandbox/src/claude_sandbox/bwrap.py:224)\n- wsl.conf [interop] enabled and [automount] enabled both default true; they are per-distro and set in /etc/wsl.conf by the distro's root. The config doc is dated 2026-09-16. (https://learn.microsoft.com/en-us/windows/wsl/wsl-config)\n- No Intune/GPO policy exists for interop or automount \u2014 the WSL ADMX (intune/WSL.admx in microsoft/WSL) and the Intune settings page expose AllowWSL, AllowInboxWSL, AllowWSL1, AllowDebugShell, AllowDiskMount, DefaultNetworkingMode, the *UserSettingConfigurable knobs (kernel, networking, firewall, nested virt, kernel debug) and new AllowWSLContainer(Privileged)/registry allowlist \u2014 but nothing for interop or drive mounting. (https://learn.microsoft.com/en-us/windows/wsl/intune)\n- WSL interop is brokered by /init via a Unix socket under /run/WSL/_interop, discovered from the WSL_INTEROP env var or by walking the parent-PID tree (src/linux/init/util.cpp UtilConnectToInteropServer). The jail's clearenv removes WSL_INTEROP and --unshare-pid truncates the parent walk; masking /run/WSL and interop=false close it fully. (https://github.com/microsoft/WSL/blob/master/src/linux/init/util.cpp)\n- The binfmt_misc WSLInterop interpreter is registered VM-wide with the fix-binary 'F' flag (available across mount namespaces/chroots) by WSL's init; `echo 0 &gt; /proc/sys/fs/binfmt_misc/WSLInterop` disables it per-session but non-persistently (interop re-enables on next session). (https://learn.microsoft.com/en-us/windows/wsl/filesystems)\n- Mirrored networking (GA Win11 22H2+) lets WSL reach Windows via 127.0.0.1 and the LAN directly; the Hyper-V firewall (default-on with Win11 22H2 + WSL 2.0.9+, VMCreatorId {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}, LoopbackEnabled true by default) filters WSL traffic and is configurable only via PowerShell/CSP (not GPO). The egress jail's private netns does not inherit mirrored loopback. (https://learn.microsoft.com/en-us/windows/wsl/networking , https://learn.microsoft.com/en-us/windows/security/operating-system-security/network-security/windows-firewall/hyper-v-firewall)\n- All WSL2 distros share one kernel and (per provider) VM. Docker's Hardened Desktop docs: 'All WSL 2 distributions share the same Linux kernel instance ... Other WSL distributions can modify kernel settings that affect Docker Desktop's security' and recommend the Hyper-V backend for maximum isolation. (https://docs.docker.com/security/for-admins/hardened-desktop/enhanced-container-isolation/limitations/)\n- Claude Code's own Bash sandbox runs on 'macOS, Linux, and WSL2'; on native Windows it runs commands unsandboxed, and the docs tell Windows users to use WSL2 for sandboxing. On WSL2 it uses bubblewrap+socat, and notes the AppArmor unprivileged-userns restriction applies 'including inside WSL2'. (https://code.claude.com/docs/en/sandboxing)\n- Codex documents a native-Windows sandbox (MXC on Win11 24H2+, plus elevated/unelevated restricted-token modes with firewall rules) and still says 'Use the native Windows sandbox by default. Choose WSL when you need Linux-native tooling'; unelevated mode has 'weaker network isolation'. This confirms native Windows is a real alternative to compare, with its own (weaker, non-bwrap) primitives. (https://learn.chatgpt.com/docs/windows/windows-sandbox.md)\n- Both native agents now run on Windows without WSL (Claude Code native since ~2.1.x, Git Bash now optional with a native PowerShell tool; Codex native GA), so 'native Windows' is a genuine target rather than a theoretical one \u2014 the WSL route competes against it, not against nothing.\n\n**Unverified:**\n\n- Whether, in mirrored networking mode, pasta reliably attaches to the jail's netns and the jail's route read-back passes on all 2026 WSL builds \u2014 the kernel config and the design strongly suggest yes, but I could not execute the battery on a WSL2 host in this environment. Treat mirrored-mode egress as verify-before-trust.\n- Whether a process inside the bwrap jail, with clearenv + unshared PID namespace but an UNMASKED /run/WSL, can still reach a usable interop socket (e.g. via the VM-wide fix-binary /init without a live per-pid socket). I established the discovery paths from WSL source but did not run an end-to-end attempt; the recommended /run/WSL mask + interop=false makes this moot.\n- The exact container runtime and per-session VM topology of the new WSLc (`wslc.exe`) feature: the architecture deep-dive (2026-09-29) confirms per-session wslcsession.exe processes, per-session storage VHDs under %LocalAppData%\\wslc, Consomm\u00e9 networking, virtiofs, and (from WSL source) that the WSLc guest runs dockerd+runc with CDI \u2014 but it does not state whether each session gets a VM fully separate from the user's WSL distro VM, nor its default seccomp/AppArmor/userns posture. WSLc is a possible future packaging but not assessed as a jail host here.\n- Whether running the existing jail inside the podman-machine or docker-desktop WSL distro trips the repo's known rootful-docker/old-passt failure in practice (the docs predict passt attach failure under rootful docker); not executed.\n- Precise minimum Windows build where the AppArmor unprivileged-userns restriction is enforced inside WSL2 by default \u2014 Claude Code docs say it can apply 'including inside WSL2' and give the sysctl check, but the enabling build/distro combination was not pinned to a primary source.\n\n### win-network-egress\n\n**Recommendation:** Design sketch: build the native Windows egress jail on MXC ProcessContainer with PSEC. Keep a WFP helper service as an optional fallback, and publish an honest list of gaps.\n\n1. Enforcement, tier 1 (no admin). Launch the whole agent tree (claude, codex, pi) through MXC, not just the agent's shell tool. Lower the conf to an MXC request with a pure function, the analogue of bwrap.py:\n- egress.default = deny.\n- Main allow rule: {to: [{cidr: 0.0.0.0/1, except: X}, {cidr: 128.0.0.0/1, except: X}]}. X is 10/8, 172.16/12, 192.168/16, 100.64/10, 169.254/16, 168.63.129.16/32, 0/8, 127/8, 224/4, 240/4, plus every on-link prefix from GetAdaptersAddresses. Splitting /0 into halves keeps each peer under MXC's 256-block cap.\n- One allow rule {cidr: /32} per allow-ip.\n- For each local-port: {to: 127.0.0.1/32, ports: tcp N}, with ingress.hostLoopback = allow only when local-port or callback-port is configured.\n- No IPv6 allows, which makes it IPv4-only like pasta --ipv4-only.\n- ingress.default = deny.\n- Never put the internal ranges in egress.deny: deny beats allow and would kill every allow-ip.\n- If an allow-ip falls in Windows' 'private' class, MXC needs ingress.default = allow. Refuse unless the conf opts in explicitly, and warn that LAN inbound opens.\n\n2. Refuse to launch, as Linux does.\n- Before each launch, run MXC's request-aware probe (PSEC \u2265 1.1 with ingress support) and refuse if it fails. MXC already errors rather than falling back for explicit egress rules.\n- Then run a stdlib self-test under the identical policy, the analogue of checks 19\u201320. Expect WSAEACCES for: a connected-subnet host, 10.0.0.1, 172.16.0.1, 192.168.0.1, 100.64.0.1, 169.254.169.254, 168.63.129.16, a global IPv6 address, UDP 255.255.255.255, TCP 445 and UDP 53 to the resolver, and an unlisted loopback port.\n- Expect no WSAEACCES for an internet address and for each allow-ip.\n- Exact filter readback is impossible without privilege; document that.\n\n3. Fallback, tier 2 (admin once at install, none at launch). For hosts without PSEC, or sites wanting exact readback and per-port inbound, add a small SYSTEM service.\n- Its interpreter is admin-owned, run by absolute path with -I; it is stdlib plus ctypes into fwpuclnt.dll.\n- Per launch it opens a dynamic WFP session and creates a maximum-weight sublayer. Filters are keyed on the per-session AppContainer SID (ALE_PACKAGE_ID).\n- Permit filters for allow-ip and relay ports sit above blocks for the ranges above, all IPv6, multicast/broadcast, ICMP (at RESOURCE_ASSIGNMENT too), 53/853 and 445/139.\n- It covers ALE_AUTH_CONNECT, RECV_ACCEPT (callback ports by local port) and RESOURCE_ASSIGNMENT.\n- It reads its filters back with FwpmFilterEnum0, audits other sublayers for hard permits, and refuses on mismatch.\n- It holds the agent's kill-on-close job, so its death kills the agent.\n- It takes policy only from %ProgramData%\\claude-sandbox\\claude-sandbox.conf (Administrators/SYSTEM write ACL), the /etc analogue. Its named pipe denies the AppContainer.\n\n4. Avoid the Windows Firewall-rule and dedicated-user designs as the primary mechanism. They need admin for every policy change, can be voided by GPO local-merge settings, express /32 punches only through complement ranges, and add VM/NAT escape risk.\n\n5. Repo fit. These can be shared with the Linux side: the conf keys (allow-ip, local-port, callback-port, egress-jail), the CLAUDE_SANDBOX_* environment variables, the range constants (BLACKHOLES, WIRESERVER, LINK_LOCAL in /scratch/hgv27681/work/claude-sandbox/src/claude_sandbox/jail.py) and the probe destination lists. The enforcement is a rewrite: no unshare, pasta, ip or socat. A small Windows jail module that emits MXC JSON can stay audit-sized.\n\nWhat cannot be matched:\n(a) A private per-agent network stack. Windows has no unprivileged network namespace, so the agent shares host interfaces, loopback and routes. Relays become port permits on the shared loopback, and identity disclosure grows.\n(b) Traffic Windows sends on the agent's behalf. Dnscache DNS (equivalent to pasta's forwarder) and LLMNR/mDNS sit outside the agent's filters. The SMB redirector for UNC paths, WebDAV, BITS, the spooler and interactive-user COM servers may too; they must be tested and blocked by other means.\n(c) Under MXC at GA: private-network egress without private inbound, per-port ingress, port-limited callback loopback, and unprivileged exact readback.\n(d) Windows 10, Server and pre-Aug-2026 Windows 11 hosts, which need tier 2 and admin.\n(e) Precedence over enterprise firewall or EDR hard permits.\n\nPrivilege summary: tier 1 needs no admin at install or launch. Tier 2 needs admin once at install and none at launch.\n\n**Key findings:**\n\n- Microsoft Execution Containers (MXC) went GA on 2026-10-07. On PSEC hosts its Windows ProcessContainer gets per-container WFP egress filters 'scoped to its container SID and applied with no UAC prompt per launch'. Rules cover IPv4 and IPv6 by CIDR (with 'except'), protocol and port, and an explicit block always wins. Claude Code support is listed as 'coming'. https://blogs.windows.com/windowsdeveloper/2026/10/07/microsoft-execution-containers-policy-driven-containment-for-ai-agents/ and https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/networking.md\n- MXC process isolation requires Windows 11 24H2/25H2 builds 26100.9278/26200.9278 (KB5120998, Aug 2026), 26H2 26300.9550 or 26H1 28000.2804; there is no Windows 10 or Server support. The OS contract is CreateProcessSecurityEnvironment (processmodel.dll), whose FlatBuffers schema carries egress DestinationRule.except. https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/os-version-support.md and https://github.com/microsoft/mxc/blob/main/external/windows-sdk/ProcessSecurityEnvironment.fbs\n- MXC GA limits relevant here: privateNetworkClientServer is bidirectional, so private-network egress requires ingress.default=allow, which also allows private inbound. Ingress is only two toggles (default, hostLoopback), and hostLoopback is bidirectional. Proxying is WinHTTP HTTP(S) only. https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/networking.md\n- AppContainer 'private' means the local subnet of a trusted network plus the domain intranet (AD sites/subnets) and GPO ranges. Everything else, including other RFC1918 addresses, is 'Internet' (Microsoft Case 5: 10.1.1.1 not in the private range). Capabilities alone do not block RFC1918. https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh831418(v=ws.11) and https://learn.microsoft.com/en-us/windows/security/operating-system-security/network-security/windows-firewall/troubleshooting-uwp-firewall\n- The BFE default DACL gives Administrators GENERIC_ALL, Network Configuration Operators GR/GW/GX, and Everyone only FWPM_ACTRL_OPEN and CLASSIFY. Adding WFP filters therefore needs admin, and Microsoft runs 'netsh wfp show state' from an admin prompt. https://learn.microsoft.com/en-us/windows/win32/fwp/access-control\n- WFP sessions end on FwpmEngineClose0 or when the client process terminates, and dynamic objects are deleted with them; persistent objects live until deleted. https://learn.microsoft.com/en-us/windows/win32/fwp/object-management\n- WFP arbitration evaluates every sublayer. 'Block' overrides 'Permit', a filter block is hard by default, and a hard permit can be overridden only by a callout veto. https://learn.microsoft.com/en-us/windows/win32/fwp/filter-arbitration\n- Windows Firewall has no weighted rule ordering, explicit block rules beat any allow, and GPO/CSP AllowLocalPolicyMerge can disable locally created rules. https://learn.microsoft.com/en-us/windows/security/operating-system-security/network-security/windows-firewall/rules\n- Codex's 'elevated' Windows sandbox (now legacy behind MXC) is the production precedent. It uses an offline sandbox user, firewall block rules with LocalUserAuthorizedList 'O:LSD:(A;;CC;;;SID)', and persistent WFP filters on FWPM_CONDITION_ALE_USER_ID for ICMP, 53/853 and 445/139. It aborts setup when LocalPolicyModifyState is not OK. https://github.com/openai/codex/blob/main/codex-rs/windows-sandbox-rs/src/setup_provisioning/firewall.rs and https://github.com/openai/codex/blob/main/codex-rs/windows-sandbox-rs/src/wfp/filter_specs.rs\n- AppContainers cannot receive loopback traffic by default. Exemptions use CheckNetIsolation LoopbackExempt (-a for outbound; -is for inbound, which must keep running), and Chromium's source notes the exemption must be run elevated. https://learn.microsoft.com/en-us/windows/security/operating-system-security/network-security/windows-firewall/troubleshooting-uwp-firewall and https://chromium.googlesource.com/chromium/src/+/HEAD/content/browser/service_host/utility_sandbox_delegate_win.cc\n- SOCK_RAW requires membership of Administrators, enforced at socket creation since Vista, so a standard-user agent cannot bypass ALE filters with raw IP. https://learn.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2\n- Claude Code's built-in sandbox runs on macOS, Linux and WSL2 only; 'On native Windows, Claude Code runs commands unsandboxed.' https://code.claude.com/docs/en/sandboxing\n\n**Unverified:**\n\n- That Dnscache-mediated getaddrinfo works inside an MXC ProcessContainer with egress default deny plus allow rules. MXC's own test only shows direct nslookup to 1.1.1.1 is blocked.\n- That PSEC egress rules can narrow host-loopback destinations per port. Codex adds a 127.0.0.0/8 egress allow in proxy mode, which suggests egress rules see loopback, but MXC does not document it.\n- How WFP attributes SMB redirector (UNC path), WebClient (WebDAV), BITS and print-spooler traffic (ALE_USER_ID, APP_ID, PACKAGE_ID), and whether user- or package-scoped filters match it. Also whether AppContainers can open UNC paths at all. The 'SMB is established by the kernel (PID 4)' claim comes from Elastic's detection docs, not Microsoft.\n- Whether a dedicated sandbox user or an AppContainer can start a WSL distro or Windows Sandbox, and so get host-NATed traffic outside principal-scoped filters.\n- That WFP filters from a non-firewall provider (PSEC or a helper service) stay effective when GPO/MDM disables local firewall rule merging. This is inferred from the WFP arbitration model; Microsoft does not state it.\n- LPAC network behaviour: whether internetClient and privateNetworkClientServer gate an LPAC exactly as a normal AppContainer. MXC's leastPrivilege flag cannot combine with some PSEC features.\n- How AppContainer capability classification treats 169.254.0.0/16 and 168.63.129.16 when no WFP rules are added.\n- Whether Network Configuration Operators membership (GR/GW/GX on the BFE engine) is removed by UAC token filtering, and whether it would let a non-elevated launcher add filters for an AppContainer SID.\n- Whether the wxc-exec.exe and mxc_ffi binaries in the NuGet and npm packages are Authenticode-signed by Microsoft.\n- No learn.microsoft.com documentation was found for MXC or for CreateProcessSecurityEnvironment (processmodel.dll) as of 2026-10-12. The OS contract is known only from the FlatBuffers schema vendored in microsoft/mxc (validated on build 10.0.26690.0).\n- No learn.microsoft.com page was found stating that Dnscache sends DNS queries on applications' behalf. It is supported indirectly by the service description, the NAME_RESOLUTION_CACHE layer's ALE_USER_ID/APP_ID fields, and Codex's design.\n- Whether Get-NetFirewallRule and INetFwPolicy2 reads work for a standard, non-elevated user.\n- Zero Trust DNS release status (preview versus GA). The Learn page, dated 2025-10-30, shows only editions and licensing.\n- Whether realistic connected-subnet counts stay under MXC's 256-blocks-per-peer expansion cap; the cap is read from source, not tested.\n- Compatibility of Git for Windows (MSYS2 runtime), MSVC (cl, link, mspdbsrv) and dotnet under MXC ProcessContainer. Not tested here; Codex documents only PowerShell and cmd adjustments.\n\n### prior-art\n\n**Recommendation:** 1. Do not build a new bespoke native Windows sandbox from raw Win32 primitives. OpenAI built two (restricted token, then sandbox users with firewall and WFP), then moved Codex to Microsoft's MXC within days of the OS feature shipping, and left a long compatibility tail behind. Cursor and Zed declined the native route. Anthropic's own srt-win is still alpha.\n\n2. If DLS needs native Windows toolchains, build the Windows backend on MXC process isolation (BaseContainer via PSEC). It is the only option that combines no admin at launch, an MSRC-serviced boundary (AppContainer), allowlisted reads, per-container CIDR egress rules, UI restrictions and ConPTY. Drive it from stdlib Python via subprocess to the signed wxc-exec.exe (JSON) or via ctypes to mxc_ffi. That is a native OS-level dependency like bwrap/pasta today, not a Python import, but it must be installed admin-owned, and Microsoft frames wxc-exec as for testing.\n- Require Windows 11 24H2 or 25H2 with the Aug/Sep 2026 updates (or 26H1) and fail closed otherwise.\n- Set allowDaclMutation=false, as Codex does, and never allow audit or learning modes.\n\n3. Express the lateral-movement policy as direct-egress CIDR rules rather than MXC's proxy mode:\n- deny RFC1918, CGNAT, link-local, metadata and connected subnets, for both IPv4 and IPv6;\n- set ingress and hostLoopback to deny.\n\nProxy mode forces bidirectional host loopback and private-network access. Accept that DNS through Dnscache and SMB need separate answers, and verify behaviourally, because filter state cannot be read back without admin.\n\n4. Use srt-win's design as the reference for hosts without PSEC or as an admin-installed hardening tier: a dedicated local user, a SID-keyed WFP fence, additive ACEs, denied network and batch logon rights, ambient write-denies and self-protecting brokers. Do not copy Codex's legacy modes: unelevated leaves reads and network open, and elevated mutates ACLs persistently.\n\n5. Keep WSL2 plus the existing Linux sandbox as the supported route for Linux toolchains on Windows, with WSL interop and automount disabled.\n\n6. On 'could it live in this repo': prior art says yes. Codex keeps linux-sandbox, windows-sandbox-rs and mxc-sandbox in one workspace; srt vendors srt-win; MXC itself is multi-backend. But the Windows backend would share only the policy/config layer, launcher UX and the battery's intent. It cannot reuse bwrap.py, jail.py or shadow.py, so it is a second backend rather than a port.\n\n**Key findings:**\n\n- Microsoft Execution Containers (MXC) went GA on Windows 11 on 2026-10-07, with SDK v1.0.0 the same day. Process isolation arrived in KB5120998 (2026-08-27 optional preview, builds 26100.9278 and 26200.9278): 'a fast, lightweight boundary for responsive workloads such as coding agents'. It is AppContainer-based and needs no admin per launch. Codex and GitHub Copilot already use it; Microsoft lists Anthropic Claude Code among agents 'releasing support'. Sources: https://support.microsoft.com/en-us/servicing/os/windows-11/2026/08/kb5120998-windows-11-24h2-25h2-update ; https://blogs.windows.com/windowsdeveloper/2026/10/07/microsoft-execution-containers-policy-driven-containment-for-ai-agents/ ; https://github.com/microsoft/mxc/releases\n- OpenAI now recommends MXC for Codex on Windows and labels its own two designs 'legacy'. 'elevated' uses dedicated lower-privilege sandbox users, firewall rules and admin-approved setup. 'unelevated' uses a restricted token from the user, ACL boundaries and 'environment-level offline controls', and cannot deny reads. The MXC adapter merged 2026-09-04, about 10 months after Codex's first Windows sandbox (merged 2025-10-30). Sources: https://learn.chatgpt.com/docs/windows/windows-sandbox.md ; https://github.com/openai/codex/pull/42841 ; https://github.com/openai/codex/pull/4905\n- Codex's legacy source shows how thin the cheap option is. The unelevated token is WRITE_RESTRICTED, which 'consult[s] restricting SIDs only for writes'. Its offline mode only sets HTTP(S)_PROXY to http://127.0.0.1:9 and adds ssh/scp stubs on PATH. Even the elevated mode grants the sandbox group read on every top-level %USERPROFILE% entry except a dotfile denylist. Sources: https://github.com/openai/codex/blob/6c372fbef45f730b6d8aa727b0ec8332365588db/codex-rs/windows-sandbox-rs/src/env.rs ; https://github.com/openai/codex/blob/6c372fbef45f730b6d8aa727b0ec8332365588db/codex-rs/windows-sandbox-rs/src/setup.rs\n- The cost of building a native Windows sandbox is visible in Codex's tracker. On 2026-10-12, about 684 openai/codex issues have both 'Windows' and 'sandbox' in the title (556 open), dominated by setup/ACL-refresh failures, UNC/SMB workspaces, Schannel, per-user tools and embedded toolchains. Security bugs include an unsandboxed spawn path ([codex#14367](https://github.com/openai/codex/issues/14367)) and writes outside the workspace ([codex#18558](https://github.com/openai/codex/issues/18558)). Source: https://github.com/openai/codex/issues?q=is%3Aissue+in%3Atitle+windows+sandbox\n- Anthropic: Claude Code runs commands unsandboxed on native Windows ('The sandbox runs on macOS, Linux, and WSL2'). sandbox-runtime gained a Windows alpha in June 2026: a dedicated srt-sandbox local user launched through CreateProcessWithLogonW, a SID-keyed WFP egress fence, and additive ACEs, installed once with one UAC prompt (Apache-2.0, v0.0.79 on 2026-10-07). Sources: https://code.claude.com/docs/en/sandboxing ; https://github.com/anthropics/sandbox-runtime#windows-alpha\n- srt's alpha documents the hard Windows lessons. Restricting SIDs break Schannel and LSA RPC. The seclogon-stamped logon SID lets a child ReadProcessMemory every same-session process. The SMB redirector connects as SYSTEM, so SID-keyed WFP misses it (the red team reached \\\\127.0.0.1\\IPC$). Dnscache resolves names for the sandbox. Task Scheduler accepts task registrations from the sandbox. Stock world-writable directories need deny ACEs. Source: https://github.com/anthropics/sandbox-runtime/tree/main/vendor/srt-win-src/src (token.rs, user.rs, winsta.rs, ambient.rs, wfp.rs)\n- MXC's process-container network model allows CIDR, protocol and port egress allow and deny rules for IPv4 and IPv6 in per-container WFP, with 'An explicit block always wins over an allow'. It has no DNS-name rules, its proxying covers WinHTTP plus env-var clients only, and privateNetworkClientServer and host loopback are bidirectional. Copilot's docs say enabling local network for the Windows credential proxy 'also permits private-network access'. Sources: https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/networking.md ; https://docs.github.com/en/copilot/how-tos/cloud-and-local-sandboxes/using-local-sandboxing\n- VS Code 1.141 (2026-10-07) ships MXC-based agent sandboxing on Windows as 'Experimental'. It states the sandbox 'is not a virtual machine or user-account boundary' and 'still runs on the execution host under your account', and leaves outbound network on by default. Source: https://code.visualstudio.com/docs/agents/run/agent-sandboxing\n- Cursor and Zed declined to build native Windows sandboxes and run their Linux sandbox in WSL2. Cursor: 'Building an equivalent native Windows sandbox is significantly harder because most existing sandboxing primitives are tailored to browsers and do not support general-purpose developer tools.' Zed warns that WSL commands 'may write outside sandbox grants' on NTFS paths. Sources: https://cursor.com/blog/agent-sandboxing ; https://zed.dev/docs/ai/sandboxing\n- Gemini CLI's native Windows sandbox (March 2026) is a restricted token without restricting SIDs, Low IL, a job object and persistent icacls Low labels. Reads are not confined ('No-op for read access on Windows'), and 'network off' is a job net-rate cap of 1 that only warns if it cannot be set. Source: https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/sandbox/windows/GeminiSandbox.cs\n- Platform facts constrain any design. MSRC services the AppContainer, user, session and VM boundaries, but admin-to-kernel is explicitly not a boundary. Chromium's restricted token, job and integrity level do not stop TCP/IP; only a lowbox token without internetClient does. AppContainer's 'private network' is only local subnets plus AD- or GPO-defined ranges, so routed lab subnets count as Internet. Sources: https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria ; https://chromium.googlesource.com/chromium/src/+/main/docs/design/sandbox.md ; https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831418(v=ws.11)\n- MXC is consumable without a Rust, .NET or Node SDK. The signed executor wxc-exec.exe takes the versioned JSON request, and mxc_ffi is a C-callable library taking NUL-terminated UTF-8 JSON. Microsoft positions the executor 'for testing or when the SDK cannot be embedded'. The Tier 3 fallback mutates host DACLs and needs admin host-prep, so a consumer should refuse it (Codex sets allow_dacl_mutation=false). Sources: https://github.com/microsoft/mxc ; https://github.com/microsoft/mxc/blob/main/docs/schema.md ; https://github.com/microsoft/mxc/blob/main/docs/backends/process-container/host-prep.md\n\n**Unverified:**\n\n- The PSEC API (CreateProcessSecurityEnvironment, QueryProcessSecurityEnvironmentSupport) was not found on learn.microsoft.com. Its behaviour is known only from MXC docs and code (a FlatBuffers schema vendored from the Windows OS repo).\n- Not tested: whether an MXC/PSEC egress policy of default 'allow' plus CIDR deny rules (RFC1918, CGNAT, link-local, metadata, IPv6 ULA and link-local) with ingress and hostLoopback deny behaves as documented for TCP, UDP broadcast (EPICS Channel Access) and IPv6.\n- Whether an MXC process container (AppContainer) can reach SMB shares through the kernel redirector or use the user's Kerberos/NTLM credentials, and whether it can call CredRead or decrypt the user's DPAPI blobs. AppContainer docs only say enterpriseAuthentication is needed to 'impersonate you on the network'.\n- Minimum Windows update for MXC process isolation: MXC and Codex say 26100.9278/26200.9278 (KB5120998, 2026-08-27 preview); VS Code says KB5124008 (2026-09-08); Copilot CLI says 25H2 with KB5124010 (26200.9550). These were not reconciled.\n- How the 2025 'agent workspace' and agent accounts relate to MXC's 2026 session container / IsolationSession; I infer the latter productises the former. MXC's dev doc says the OS API was gated on an internal feature flag, which may be stale.\n- The admin requirement and network behaviour for MXC session isolation beyond 'IsolationSession cannot restrict networking'.\n- The console-input-injection risk via WriteConsoleInput from a sandboxed process that shares the user's console is inferred from the API documentation, not tested.\n- The default ACL on NTFS volume roots and secondary volumes (Authenticated Users can create folders and modify their own content) comes from a secondary source, and is consistent with srt's ambient list and [codex#18558](https://github.com/openai/codex/issues/18558).\n- Whether Codex's elevated mode is exposed to the logon-SID ReadProcessMemory issue that srt documents for CreateProcessWithLogonW.\n- Whether a PSEC-contained process (for example claude.exe or codex.exe run whole-agent inside the container) can create a nested MXC/PSEC container or AppContainer for its own command sandbox.\n- Compatibility of MSVC, the dotnet SDK, MSYS2/Git Bash and Windows Python under MXC BaseContainer. The playground limitations doc references SDK 0.4/0.5 and may be stale.\n- Release years of the MXC GitHub releases were summarised by a fetch tool; dates were confirmed from the releases Atom feed (v1.0.0 on 2026-10-07). The contents of mxc-release-binaries.zip were not downloaded or inspected.\n- Timing of Claude Code MXC support: only Microsoft's blog says it is coming; no Anthropic source found.\n- Docker Sandboxes' launch date (around March 2026) and the July 2026 Claude Cowork escape report come from secondary sources.\n- Windows sandboxing status for Goose, Aider and Windsurf/Devin Desktop: none found, but not exhaustively verified.\n- Win32 app isolation GA status: no 2025-2026 release notes found; treated as stalled preview from repo activity.\n\n", "url": "https://gist.github.com/gilesknap/15d0d71e8c6ab272380f60e05a6dcdad"}]}
