Skip to main content

OpenAI Built Codex a Windows Sandbox Out of SIDs and Firewall Rules: What It Teaches a Linux-First PaaS About Isolating Agent Code

11 min readDora NodaDora Noda
Share
On this page

Windows never shipped a primitive that means "safe autonomous coding agent," so OpenAI spent months assembling one from spare parts. That is the blunt summary of Building a safe, effective sandbox to enable Codex on Windows, the May 13, 2026 engineering write-up by Codex engineer David Wiesen — and its closing line deserves to travel beyond the Windows world: "Windows did not hand us one primitive that cleanly maps to 'safe autonomous coding agent.' We composed several tools and concepts to build something coherent."

Before this work, Windows Codex users picked between two bad options: approve nearly every command the agent wanted to run, or enable Full Access and let it roam the machine unsupervised. OpenAI could not just skip the platform either. In Stack Overflow's 2025 survey of more than 49,000 developers, Windows was the most-used primary OS among professionals at 49.5%, ahead of macOS at 32.9% and Ubuntu at 27.7%. Nearly half your agent users sit on the OS with the weakest sandbox story. That is why this post is worth a Linux-first platform team's time: the constraints are Windows-shaped, but the lessons are about isolating untrusted agent code anywhere.

Here is the map everything else hangs off — what the same agent's sandbox is actually made of on each OS:

PlatformCodex sandbox primitivesHow policy changes
macOSSeatbelt via sandbox-exec, generated .sb policyRegenerate the policy file, cheap
LinuxLandlock + seccomp by default, bubblewrap optionalRe-exec the helper, cheap
WindowsSynthetic SID + write-restricted token + two dedicated users + firewall rules + two helper binariesRe-apply ACLs across the workspace, slow

Linux and macOS each handed OpenAI something close to a sandbox-shaped primitive. Windows handed them identity, access-control lists, and a firewall — and the post is the story of bending those into a cage. The two tiers that resulted, and the three dead ends that preceded them, are the most detailed public account of what agent isolation costs on an OS that never designed for it.

The three dead ends​

Wiesen's team evaluated the obvious Windows answers first and rejected all three. Each rejection is really a statement of what agent sandboxing requires, so they are worth one table:

CandidateWhat it isWhy it failed
AppContainerWindows' native capability-based sandboxBuilt for apps that know up front exactly what they need; an agent driving shells, Git, Python, and package managers is the opposite shape
Windows SandboxMicrosoft's disposable lightweight VMCodex must act on the user's real checkout, not a throwaway desktop needing host/guest bridging — and it is not even available on Windows Home SKUs
MIC integrity labelingRun at low integrity, relabel writable rootsRelabeling the workspace low-integrity makes it writable by low-integrity processes in general — the user's checkout becomes a low-trust sink for the whole host

Notice the shape of the requirements hiding in that table. An agent sandbox must confine open-ended developer behavior, must operate on the real filesystem rather than a copy, and must not weaken the host's trust model to do it. Keep those three properties in mind; every Linux sandbox decision in this blog's gVisor vs Kata vs Firecracker rundown is the same triangle with different corners.

Tier 1: the unelevated sandbox — identity as the cage​

The first working prototype needed no administrator privileges. Its core insight: Windows lets you create synthetic SIDs — security identifiers that appear in ACLs without belonging to any real user — so the sandbox minted one called sandbox-write and built the whole filesystem policy around it.

Enforcement came from a write-restricted token, a process token variant that adds a second access check to every write. A write succeeds only if both the normal user identity is allowed and at least one SID in the token's restricted list is granted access. Codex launched agent commands under a token whose restricted list was [Everyone, logon-session SID, sandbox-write], granted sandbox-write write/execute/delete on the working directory plus configured writable_roots, and explicitly denied it the "read-only within writable" paths: <cwd>/.git, <cwd>/.codex, and <cwd>/.agents. Reads stayed as permissive as the real user; writes went nowhere the synthetic SID was not invited.

Network suppression was the compromise. Without elevation there was no access to Windows Firewall, so the prototype poisoned the environment instead: proxy variables pointed at a dead endpoint (HTTPS_PROXY=http://127.0.0.1:9, plus ALL_PROXY and GIT_HTTPS_PROXY), GIT_SSH_COMMAND was set to fail immediately, and a denybin directory of stub SSH/SCP scripts was prepended to PATH. That catches normal tool-driven traffic. It is also, in the post's own word, advisory: any process can ignore the environment, bypass PATH, or open sockets directly.

The retrospective lists four costs: ACL setup is slow on big workspace trees, real ACLs get stamped onto the developer's machine, changing sandbox semantics means re-applying ACLs rather than regenerating a policy file the way macOS regenerates its Seatbelt profile — and network protection that cannot survive adversarial code. The first three are inherent to the approach. The fourth killed it.

Tier 2: the elevated sandbox — the firewall demands a principal​

The redesign accepts one elevation: an admin-approved setup step. Everything else follows from a single Windows limitation — firewall rules cannot match a restricted token's synthetic identity. The post enumerates the dead ends precisely: a rule cannot target "any token carrying our SID in its restricted list"; a binary-scoped rule covers codex.exe but not the Git or Python processes the agent spawns; port or address rules are the wrong policy entirely (nobody wants to block 443, they want to block this process tree's outbound access). To firewall the sandbox, the sandbox had to become a principal — a separate user.

So setup now creates two local users: CodexSandboxOffline, targeted by firewall rules blocking all outbound traffic, and CodexSandboxOnline for work allowed to reach the network. Their credentials are stored locally, encrypted with DPAPI where the sandbox users themselves cannot read them. Because a fresh user cannot even read the developer's profile directory, setup also grants read ACLs on the common roots (C:\Users\<name>, C:\Windows, both Program Files trees, C:\ProgramData) — asynchronously, so the blocking setup step does not wait on the slowest part.

Spawning across the user boundary needed its own binary. codex.exe cannot reliably call CreateProcessAsUserW for another user from the real-user side — a privilege wall — so a new codex-command-runner.exe splits the flow in two. The harness launches the runner as the sandbox user via CreateProcessWithLogonW; then, on the sandbox-user side of the boundary, the runner opens its own token, builds the restricted token with CreateRestrictedToken, and spawns the real child.

Setup lives in a third binary, codex-windows-sandbox-setup.exe, so the UAC boundary is crossed only when needed and codex.exe stays a normal unelevated harness. Final architecture, four layers: harness, setup binary, command runner, child process. Nothing in it is simple, and the post's claim is that every piece earned its place against a real failure.

Three lessons for a container-first PaaS​

None of this runs on a Linux fleet, and that is the point — the failures are portable even though the primitives are not.

1. Enforce by workload identity, not by binary path. The firewall episode is the clearest version of a mistake platforms keep making: scoping a network rule to what binary is running instead of which workload it belongs to. OpenAI could block codex.exe all day and the agent's spawned Python would sail through.

The fix was making the sandbox a first-class principal and scoping rules to it. On a Linux PaaS the equivalent is scoping egress to workload identity — a Kubernetes NetworkPolicy selecting the sandbox's pods, a per-tenant egress gateway, SPIFFE-style attested identity — never to "processes running this image." If your sandbox's network rule would break the moment the agent execs a different binary, you have the unelevated prototype's firewall problem with better marketing.

2. Advisory network controls fail open against agents. Proxy environment variables stop well-behaved tools and nothing else; the post is explicit that even good-intentioned binaries bypass them by opening sockets directly. Every PaaS has an analogue: a NO_PROXY-style convention, an HTTP proxy agents are "supposed" to use, an egress allowlist enforced only in the SDK wrapper. Against an agent that runs arbitrary code — which is the entire product — anything the workload can route around is documentation, not enforcement. Real egress control lives below the workload's freedom to choose: packet filter, transparent proxy, or network namespace it cannot leave.

3. Sandbox semantics must be cheap to regenerate. The unelevated tier's quiet tax was that every policy change meant re-applying ACLs across a workspace tree, where macOS just regenerates a policy file. Platforms pay the same tax whenever sandbox policy is stamped into slow-to-change state: baked into images, hand-applied per namespace, or spread across firewall rules nobody regenerates. The Windows lesson is to keep the sandbox definition in one place that can be re-rendered and re-applied mechanically — policy-as-code with a fast reconcile loop, whether that is OPA bundles, Cilium policies, or a regenerated seccomp profile. If changing what the sandbox means requires touching every workload by hand, the sandbox will quietly mean last year's thing.

The scope question, answered: do you need a Windows tier?​

That leaves the concrete roadmap question: does an AI-agent-first PaaS need a second, Windows-shaped sandbox tier, or is "we only run Linux containers" a scope boundary worth stating explicitly? After the evidence above, the answer splits into three facts and a verdict.

First, the "but our developers are on Windows" pressure is weaker than the 49.5% suggests, because developing on Windows no longer means deploying from Windows primitives. As this blog covered yesterday, Microsoft's WSL Containers preview gives Windows 11 a built-in OCI runtime — wslc build produces plain OCI images a registry cannot distinguish from Docker-built ones. The Windows developer's output is a Linux container; the PaaS never sees Windows.

Second, the sandbox market's own answer to Windows-shaped execution is full VMs, not a second set of OS primitives. Daytona — the one provider in this blog's Cursor eight-backend rundown with a Windows class — ships windows-small/medium/large as VM-only sandboxes from a prebuilt Windows snapshot. Nobody is building a multi-tenant restricted-token fleet; the economics say VM boundary or nothing.

Third, Kubernetes does offer Windows node pools, but they carry OS-version coupling a Linux fleet never thinks about: AKS retired Windows Server 2019 node pools on March 1, 2026, blocks them on Kubernetes 1.33+, and will delete the images in April 2027. A Windows tier is not just second primitives — it is a second OS lifecycle to operate, with Microsoft's retirement calendar as your upgrade driver.

The verdict: Linux-only is a defensible scope boundary — but state it explicitly instead of assuming it by omission. Put "agent execution runs on Linux containers; Windows-targeted builds are out of scope" in the docs where a prospect can find it, with the reason attached: the primitives do not transfer, the market answers Windows with VMs, and the remaining must-run-Windows workloads (legacy .NET Framework builds, Windows-only test matrices) are a shrinking set that deserves its own VM-based lane if it ever arrives, not a second sandbox architecture maintained in parallel. What would flip the answer is a demand signal, not a technology signal: signed tenants blocked on Windows execution, at which point the correct response is Daytona-style VM sandboxes behind the same queue — not restricted tokens on your fleet.

OpenAI's team learned that agent security is a different beast from classic application security because the agent must be useful — confined, but operating on the real thing. That tension is OS-independent. Whether your cage is made of SIDs or seccomp profiles, the questions are the same: is it scoped to the workload's identity, is the network boundary real, and can you change the policy faster than the agent changes its behavior? Answer those three on Linux and you have absorbed everything the Windows post has to teach.

Running agents on machines you own means choosing the isolation boundary deliberately — Linux containers, stated plainly, enforced for real. Bex.co is the open-source, AI-native Render alternative: push a git repo, get a running HTTPS service on your own hardware. Star the repo on GitHub or deploy your first app today.

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide