为 Windows 上的 Codex 构建安全高效的沙箱环境
raw/2026-07-12-codex-windows-sandbox-engineering.md An openai engineering post by David Wiesen (Member of Technical Staff, Codex engineering team since September 2025) narrating the full design history of the codex sandbox on Windows — from “no sandbox at all” through a rejected survey of native Windows primitives, a first unelevated prototype, to the shipped elevated design with dedicated sandbox users and firewall rules. The through-line: Windows offers no single OS primitive that maps to “safe autonomous coding agent” (agent-sandboxing), so the team composed SIDs, write-restricted tokens, local users, DPAPI-encrypted credentials, and Windows Firewall rules into a four-binary architecture. The wiki’s most detailed account of what the execution boundary layer of a harness costs to actually build.
Source file: raw/2026-07-12-codex-windows-sandbox-engineering.md (Chinese translation of the English original; quotes below keep the Chinese with English glosses). Seven architecture diagrams are rehosted and part of the evidence — notably the write-check table (both the regular user and the sandbox-write SID must pass), the failed-vs-working command-runner flow comparison, and the final-architecture diagram showing named-pipe IPC and the sandbox control plane (~/.codex/.sandbox, ~/.codex/.sandbox-secrets, ~/.codex/cap_sid — visible only in the diagram, not the prose).
Summary
The problem
Codex runs on developer laptops (CLI, IDE extension, desktop app) with the real user’s permissions. Its default mode promises: read almost anywhere, write only inside the workspace, no network unless approved. Enforcing that promise requires OS-level isolation — Seatbelt on macOS, seccomp/bubblewrap on Linux — but Windows ships nothing equivalent, so Windows users were stuck choosing between approving nearly every command (including reads) or Full Access mode with no oversight.
Rejected: the native Windows options
| Option | Why it looked right | Why it was rejected |
|---|---|---|
| AppContainer | Real OS boundary, capability-based | Built for apps that declare their needs up front; Codex drives open-ended dev workflows (shells, Git, Python, build tools, arbitrary binaries) |
| Windows Sandbox | Disposable lightweight VM, strongest isolation | Codex must act on the user’s actual checkout/tools/env, not a throwaway desktop; not available on Windows Home SKUs |
| MIC integrity labels | Non-admin path with real OS enforcement | Relabeling a workspace low-integrity means any low-integrity process can write there — turns the user’s checkout into a host-wide low-integrity sink |
Prototype 1: the “unelevated sandbox”
Goal: no admin prompts ever. Two mechanisms:
- File writes — enforced. Setup creates a synthetic SID (
sandbox-write), grants it write/execute/delete ACLs on the workspace and configuredwritable_roots, explicitly denies it on carve-outs (<cwd>/.git,<cwd>/.codex,<cwd>/.agents), then launches commands under a write-restricted token whose restricted SID list is[Everyone, logon-session SID, sandbox-write]. A write succeeds only if both the regular user and at least one restricted SID are allowed — so the ACLs define exactly where the sandbox can modify the filesystem. - Network — advisory only. Windows Firewall needs admin, so the prototype “poisons the obvious escape hatches”: proxy env vars pointed at a dead endpoint (
HTTPS_PROXY=http://127.0.0.1:9,ALL_PROXY,GIT_HTTPS_PROXY),GIT_SSH_COMMAND=cmd /c exit 1, plus adenybindirectory prepended to PATH with stub SSH/SCP scripts and a reorderedPATHEXT.
Tradeoffs: setup ACLs can be slow on large workspace topologies; real ACLs leave a (mild) footprint on the host; ACL-based semantics are expensive to change (vs. regenerating a .sbpl file on macOS). All tolerable — except the network story: any process that ignores env vars or opens sockets directly walks straight through. That one flaw killed the design.
Prototype 2: the shipped “elevated sandbox”
To make firewall rules stick, sandboxed commands must run as a separate principal. Windows can’t match a firewall rule to “any token whose restricted SID list contains our synthetic SID”, nor per-invocation program rules — so Codex creates two local users at setup:
CodexSandboxOffline— targeted by a block-all-outbound firewall ruleCodexSandboxOnline— not targeted (for runs where network is approved)
The restricted token machinery stays the same (write_restricted, [Everyone, Logon, Synthetic]), but the token’s principal is now a sandbox user instead of the real user. Consequences cascade:
- First-class elevated setup step (dedicated binary,
codex-windows-sandbox-setup.exe): create the SID; create both users; store their credentials DPAPI-encrypted where the sandbox users can’t read them; create/validate the firewall rule. Because sandbox users can’t read other users’ profiles by default, setup also grants best-effort read ACLs on common roots (C:\Users\<real-user>,C:\Windows,C:\Program Files,C:\Program Files (x86),C:\ProgramData) — run asynchronously since per-directory ACL installation is expensive. - A command-runner binary (
codex-command-runner.exe): the intended single-process flow (real user →LogonUserW→CreateRestrictedToken→CreateProcessAsUserW) fails at a privilege wall —codex.execan mint the restricted token but can’t reliably spawn with it from the real-user side. So the flow is split:codex.execallsCreateProcessWithLogonWto launch the runner as the sandbox user (no restricted token yet); inside the runner,OpenProcessToken+GetTokenInformationextract the sandbox logon SID,CreateRestrictedTokenbuilds the final token, andCreateProcessAsUserWspawns the real child — all on the sandbox-user side of the boundary. Parent and runner speak length-prefixed JSON frames over a named pipe (spawn_request/spawn_ready/output— per the final architecture diagram).
Final architecture, four layers: codex.exe → codex-windows-sandbox-setup.exe (elevated setup) → codex-command-runner.exe (token minting + spawn) → child processes.
Key claims
- Every Codex command is sandboxed from the start, and every descendant process inherits the same boundary — restrictions propagate down the process tree. (agent-sandboxing)
- Codex’s default mode: read almost anywhere, write only in the workspace, no network unless explicitly enabled — and it needs a sandbox that actually enforces this, not policy text. (agent-sandboxing, harness)
- macOS and Linux get OS primitives (Seatbelt; seccomp/bubblewrap); Windows has no out-of-the-box equivalent, so OpenAI implemented its own. (agent-sandboxing)
- On macOS the sandbox semantics live in a dynamically generated
.sbplSeatbelt profile — cheap to change; the Windows ACL approach makes semantic changes slow and heavyweight. (agent-sandboxing) - Env-var network suppression is advisory: it catches proxy-honoring tools but “在设计之初就无法抵御恶意对抗代码” (was never designed to hold up against adversarial code) — and even well-intentioned binaries bypass it by shipping their own socket code. This alone justified the elevated redesign. (prompt-injection)
- Windows Firewall cannot target a restricted token’s non-principal identity, and program-path rules can’t distinguish this sandboxed
python.exefrom any other — hence the dedicated-user design. - Write carve-outs inside writable roots (
.git,.codex,.agents) are explicitly denied — the agent can edit your code but not your Git metadata or its own config/instructions. (agent-sandboxing) - Sandbox-user credentials are stored DPAPI-encrypted where the sandbox users themselves cannot read them — the sandbox must not be able to unlock itself.
- Setup logic lives in its own binary to cross the UAC boundary only when needed, keep
codex.exean unelevated harness on all platforms, and decouple long-running setup from the main process lifetime. - Lesson 1: “Windows 并没有直接为我们提供一个能够完美对应’安全自主编程智能体’的原语” (Windows did not hand us one primitive that cleanly maps to “safe autonomous coding agent”) — the shipped design is a hybrid of prototypes that each solved part of the problem.
- Lesson 2: coding-agent security is a different beast from classic application security — the workload is open-ended developer workflows, and the engineering is balancing compatibility with agentic workloads against real enforcement. (agent-sandboxing, harness)
Notable quotes
每一个 Codex 命令从一开始就处于沙箱之中,且其所有子进程 (descendant process) 也都保持在相同的边界内。 (Every Codex command is sandboxed from the start, and every descendant process stays inside the same boundary.)
这种做法虽然拦截了大量由常规工具驱动的流量,但它仍然只是建议性的。任何进程都可以忽略环境变量、绕过 PATH,或者直接建立套接字 (socket) 进行连接 — 这带来很大风险。 (That caught a lot of normal tool-driven traffic, but it was still only advisory. A process could ignore the environment, bypass PATH, or just open sockets directly — too risky.)
我们并不想阻止 443 端口,而是想阻止这个特定受限进程树的任意出站访问。 (We didn’t want to block port 443; we wanted to block arbitrary outbound access for this specific restricted process tree.)
编程智能体的安全问题与更传统的应用程序安全有着本质的不同。Codex 必须适用于真实的开发者工作流。我们的工程重心,始终是在兼容智能体工作负载与保障真实强制力之间找到平衡。 (Security for a coding agent is a different beast than more classic application security. Codex has to work for real developer workflows. The engineering work was about balancing compatibility with agentic workloads against real enforcement.)
Open questions
- Read access is best-effort by design. The elevated sandbox grants read ACLs asynchronously to a hand-picked directory list; reads outside that list (custom tool installs, other drives) presumably fail until approved or ACL’d. How often this bites in practice — and what the approval UX looks like — isn’t covered.
- The sandbox control plane is diagram-only.
~/.codex/.sandbox,~/.codex/.sandbox-secrets,~/.codex/cap_sidand the named-pipe JSON protocol appear in the final architecture diagram but are never explained in prose. The IPC attack surface (can a sandboxed child talk to the pipe?) is unaddressed. CodexSandboxOnlinescoping. When network is approved, the online user has no firewall rule at all — is egress scoped (allowlisted domains?) or fully open? The post doesn’t say.- Cross-agent comparison. How claude-code handles the same Windows gap (no Seatbelt/seccomp equivalent) is not in the wiki yet — worth a source if Anthropic publishes one.
- Threat-model boundary. The post frames the sandbox against accidental damage and data exfiltration, but doesn’t state whether it’s claimed to hold against a deliberately adversarial model (vs. adversarial code the model runs). The unelevated design explicitly wasn’t; the elevated design’s claim strength is left implicit.
Pointers
- agent-sandboxing — the concept page distilled from this source: sandbox as the enforced execution boundary for coding agents; per-OS primitives; the design axes this post surfaces (enforcement strength, elevation cost, semantics changeability, setup latency).
- codex — this is the wiki’s first source about the shipped external Codex product surface (CLI / IDE extension / desktop app), complementing 2026-04-27-harness-engineering-codex-agent-first (the team’s internal development environment) and 2026-04-27-codex-context-compaction-investigation (server-side API internals).
- harness — the execution boundary row of the four-part harness table, instantiated at OS level; this post is the wiki’s best evidence of how much engineering that row costs when the OS doesn’t cooperate.
- prompt-injection — network suppression is the enforcement-level backstop for the exfiltration sink: even a successfully injected agent can’t upload data if the process tree has no outbound network.