Approvals & Sandboxing
When Codex asks vs where it can act — two separate controls
- Explain why sandbox (WHERE) and approval (WHEN) are independent controls, not two names for the same thing
- Choose the right sandbox mode — read-only, workspace-write, or danger-full-access — for a given task
- Choose the right approval policy — untrusted, on-request, or never — and know that on-failure is deprecated
- Map the three UI presets (Read Only, Auto, Full Access) onto their underlying sandbox + approval pairs
- Explain why network is OFF by default in workspace-write and how localhost is blocked as a DNS-rebinding defense
- Describe how Codex enforces the sandbox on macOS, Linux/WSL2, and Windows — and that it refuses to run when it can't enforce policy
Codex's safety model rests on two controls that people constantly confuse: the sandbox decides WHERE Codex can read, write, and reach the network (the OS enforces it), and the approval policy decides WHEN Codex must stop and ask you before acting. This lesson teaches the three sandbox modes, the three approval policies, the UI presets that pair them, the crucial network-off-by-default rule in workspace-write, and the OS mechanisms that actually enforce the boundary.
- 1The mental model: two knobs, not one
- 2Sandbox modes: WHERE Codex can act
- 3Approval policies: WHEN Codex must ask
- 4The UI presets: convenient pairings
- 5The network-off rule (the gotcha in workspace-write)
- 6Who actually enforces the wall: the OS layer
The mental model: two knobs, not one
Almost everyone new to Codex collapses safety into a single dial — "how locked down is it?" That instinct will trip you up. Codex separates safety into two independent controls, and you set each one on its own:
- Sandbox = WHERE. This is an OS-enforced boundary on what Codex's commands can read, write, and whether they can reach the network. The operating system itself stops a command from escaping it — it isn't the model politely declining.
- Approval = WHEN. This is the policy for when Codex must stop and ask you before it runs something. It governs the conversation, not the kernel.
Think of it like a new contractor working in your house. The sandbox is which rooms they're physically allowed into and whether they get a key to the front gate (the building enforces that — locked doors). Approvals are the rule for when they have to knock and ask you first before doing a thing. You can have a contractor confined to one room who still asks before every action, or one with a full set of keys who never asks. The where and the when are genuinely separate.
Why does this matter? Because the two combine into very different safety postures, and the dangerous mistakes come from changing the wrong knob. Loosening approvals so Codex stops nagging you is fine if the sandbox still confines it. Dropping the sandbox to silence the same prompts is how people accidentally hand an agent the keys to the whole machine. Keep the two ideas separate in your head and the rest of this lesson clicks into place.
Key insight
One line to remember
Sandbox = WHERE Codex can act (OS-enforced read/write/network). Approval = WHEN Codex must stop and ask. They are related but not interchangeable — you set them independently.
Sandbox modes: WHERE Codex can act
The sandbox is set with --sandbox / -s on the command line, or sandbox_mode in ~/.codex/config.toml. There are exactly three modes:
Mode (--sandbox value) | What it allows | What it blocks |
|---|---|---|
read-only | Inspect and read files, answer questions about the code. | Editing files or running state-changing commands (without approval); no network. |
workspace-write | Read files, edit inside the workspace, and run routine local commands within that boundary. The default low-friction mode for local work. | Writes outside the workspace roots; network is OFF by default (you must opt in — see the network section). |
danger-full-access | No sandbox at all — full filesystem and full network access. | Nothing. "Removes the filesystem and network boundaries" — use only when you truly want Codex to act with full access. |
In config form:
sandbox_mode = "workspace-write" # or "read-only" / "danger-full-access"The mental progression is consultant → editor → root. read-only is a consultant who can look but not touch. workspace-write is an editor who can change things in your project but can't wander off into the rest of the machine or phone home. danger-full-access removes the walls entirely.
The key boundary in workspace-write is the workspace root — Codex can freely edit files inside it but is blocked from writing elsewhere. That's what makes it the everyday default: enough freedom to actually do the work, with a hard wall around the blast radius.
Tip
Start tight, loosen deliberately
workspace-write is the sweet spot for local coding. Drop to read-only when you just want analysis or a code review, and reserve danger-full-access for throwaway tasks inside a disposable environment — never reach for it just to stop approval prompts.
Approval policies: WHEN Codex must ask
The approval policy is set with --ask-for-approval / -a on the command line, or approval_policy in config. The accepted values on the current CLI are untrusted, on-request, and never:
| Policy | Behavior |
|---|---|
untrusted | Auto-runs only known-safe read operations; asks for approval before any command that could change state or trigger external execution. The most cautious posture. |
on-request | Codex works inside the sandbox on its own and asks only when it needs to go beyond the boundary — e.g. write outside the workspace or use the network. The recommended interactive default. |
never | Never prompts. Operates strictly within the configured sandbox constraints. The right choice for non-interactive runs (codex exec, CI). |
In config form:
approval_policy = "on-request"Notice how on-request leans on the sandbox: Codex doesn't pester you for every edit because the sandbox already confines it; it only surfaces a prompt when it wants to step outside that confinement. That's the whole reason the two controls are designed to work together.
There's also a granular object form (approval_policy = { granular = { ... } }) for fine-tuned control over which categories of action prompt — but the three string values above cover almost every situation; reach for the granular form only when you have a specific need and verify its exact shape against the live config reference.
Watch out
on-failure is deprecated
Older guides mention a fourth policy, on-failure (ask only after a sandboxed command fails). It is deprecated and newer CLIs may reject it. Use on-request for interactive work and never for non-interactive runs instead.
The UI presets: convenient pairings
In an interactive session you usually don't set the two knobs by hand — you pick a preset from the picker. Each preset is simply a sandbox mode + approval policy combined, so once you understand the two controls, the presets are obvious:
| Preset | Sandbox | Approval | What it feels like |
|---|---|---|---|
| Read Only | read-only | on-request | Consultative. Browse, read, and answer questions; asks before any edit, command, or network use. |
| Auto (default) | workspace-write | on-request | The daily driver. Reads, edits, and runs commands in the workspace; asks before editing outside it or touching the network. |
| Full Access | danger-full-access | (no approvals) | No sandbox, no prompts — works across the whole machine including the network without asking. "Not recommended"; use sparingly on trusted tasks only. |
Auto is the launch default and where you'll live most of the time. The explicit flag form of Auto is just the two knobs spelled out:
codex --sandbox workspace-write --ask-for-approval on-requestSo a preset isn't a fourth concept to memorize — it's a named shortcut for a (where, when) pair. Knowing that lets you read any preset's safety properties off its two components, and it's why you can always fall back to the explicit flags when you want a combination the presets don't offer.
Example
Reading a preset off its parts
"Read Only" = read-only sandbox + on-request approval. So: it physically can't edit or hit the network (sandbox), and it'll ask before doing anything beyond reading (approval). The two parts fully explain the behavior — no magic.
The network-off rule (the gotcha in workspace-write)
Here is the single most surprising thing about workspace-write, and the one that catches everyone: network access is DISABLED by default. Codex can edit your files all day, but npm install, pip install, curl, or any command that reaches the internet will be blocked until you explicitly opt in. This is deliberate — the network is the main escape route for a prompt-injection or a runaway command, so it's off unless you say otherwise.
The legacy sandbox_mode schema opts in with a single boolean plus optional extra writable roots:
[sandbox_workspace_write]
network_access = true # default is false
writable_roots = ["/Users/you/.pyenv/shims"] # extra writable dirs
exclude_tmpdir_env_var = false # is $TMPDIR writable?
exclude_slash_tmp = false # is /tmp writable?The newer permission-profile schema expresses network as a per-domain allowlist instead of one on/off switch, so you can permit api.openai.com while denying a tracking host:
[permissions.profile-name.network]
enabled = true
[permissions.profile-name.network.domains]
"api.openai.com" = "allow"
"*.example.com" = "allow"
"tracking.example.com" = "deny" # deny overrides allowTwo rules worth burning in: deny always wins over allow on a conflict, and even when you enable the network, Codex blocks literal local targets like localhost and 127.0.0.1 by default as a DNS-rebinding defense — you have to explicitly allowlist them. Enabling network access changes how it's enforced; it does not, by itself, hand Codex the open internet.
Watch out
"It can't install dependencies!"
If a Codex task fails on npm install or pip install in workspace-write, it isn't broken — the network is off by default. Opt in with network_access = true (or a domain allowlist), and remember localhost/127.0.0.1 stay blocked until you allowlist them explicitly.
Who actually enforces the wall: the OS layer
The sandbox is only meaningful because the operating system enforces it — not the model's good intentions. Codex uses the platform-native mechanism for each OS, and crucially it refuses to run a command unsandboxed if the platform can't enforce the requested policy. It does not silently fall back to no sandbox — a vital property to trust.
| Platform | Enforcement mechanism |
|---|---|
| macOS | Built-in Seatbelt — commands run via sandbox-exec with a Seatbelt profile matching the sandbox mode. Rarely needs setup. |
| Linux / WSL2 | bubblewrap (bwrap) + seccomp by default, with Landlock as a compatibility fallback. Needs the bubblewrap package and unprivileged user namespaces. |
| Windows (native) | Native PowerShell sandbox configured under [windows]: elevated (strongest) or unelevated (fallback). Running in WSL2 uses the Linux sandbox instead. |
The Windows native sandbox is comparatively new — older material says Windows is WSL-only, so don't carry that assumption in. The two Windows modes trade isolation for privilege:
[windows]
sandbox = "elevated" # preferred; or "unelevated"- Elevated uses dedicated lower-privilege sandbox users, filesystem permission boundaries, and firewall rules — the strongest isolation.
- Unelevated is a fallback using a restricted token derived from your user and ACL-based boundaries; network isolation is weaker and it can't enforce every read/write carve-out.
The practical upshot: if you see a Linux/WSL2 error like "couldn't set up sandbox," it's almost always missing bubblewrap or restricted user namespaces — and Codex stopping is the correct behavior, because it would rather refuse than run your commands without the boundary you asked for.
Key insight
Refuses rather than runs unsafe
Codex's fail-closed design is the safety guarantee that makes the sandbox trustworthy: if the OS can't enforce the policy you requested, Codex won't run the command at all rather than quietly dropping the boundary. A sandbox that silently degraded would be no sandbox.
Try it: Feel the two controls — and prove the network is off
Goal: internalize that sandbox and approval are separate, and see the workspace-write network rule with your own eyes.
1) Start in Read Only. In a small repo, launch codex and pick the Read Only preset (or run codex --sandbox read-only --ask-for-approval on-request). Ask it to summarize what this project does. Notice it reads freely but, if you ask it to edit a file, it stops and asks — that's on-request approval over a read-only sandbox.
2) Switch to Auto and edit. Move to the Auto preset (--sandbox workspace-write --ask-for-approval on-request). Ask Codex to make a tiny edit — e.g. add a comment to a file. It edits without prompting, because the change is inside the workspace and the sandbox already confines it. This is the two controls cooperating.
3) Hit the network wall. Still in Auto, ask Codex to run something that needs the internet — npm install <small-package> or curl https://example.com. Watch it fail or get blocked. Write one sentence explaining why: network is OFF by default in workspace-write.
4) Open the network — deliberately. In ~/.codex/config.toml, add:
[sandbox_workspace_write]
network_access = trueRe-run the network command and confirm it now works. Then try to reach http://localhost:<port> of a local dev server and note that it's still blocked by default (DNS-rebinding defense) until you allowlist it.
5) Reflect (3 sentences). (a) Which knob did you change in step 4 — sandbox or approval? (b) Why is dropping to danger-full-access the wrong fix for the step-3 failure? (c) Where would you set -a never, and why only there?
Stretch: Check the live CLI reference and confirm the current approval enum and whether on-failure / --full-auto are still listed — these rot between releases.
Key takeaways
- 1Codex has two independent safety controls: the sandbox (WHERE it can read/write/network, OS-enforced) and the approval policy (WHEN it must stop and ask you).
- 2Three sandbox modes: read-only (inspect only), workspace-write (edit in the workspace, the local default), and danger-full-access (no boundaries).
- 3Three approval policies: untrusted (ask before any state change), on-request (recommended interactive default — ask only to go beyond the sandbox), and never (non-interactive); on-failure is deprecated.
- 4The three UI presets are just pairings: Read Only = read-only + on-request, Auto (default) = workspace-write + on-request, Full Access = danger-full-access + no approvals.
- 5In workspace-write the network is OFF by default and must be opted into; even then, localhost/127.0.0.1 are blocked by default as a DNS-rebinding defense (deny overrides allow).
- 6The OS enforces the sandbox — macOS Seatbelt, Linux/WSL2 bubblewrap+seccomp (Landlock fallback), Windows native elevated/unelevated — and Codex refuses to run rather than run unsandboxed when it can't enforce policy.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.A teammate says, "I switched Codex to never ask for approval, so now it has full access to my whole machine." What's wrong with that statement?
2.You're running Codex in the default Auto preset (workspace-write + on-request) and a task fails the moment it runs `npm install`. What is the most likely cause?
3.Which statement correctly maps a UI preset to its underlying controls?
4.On a Linux machine, Codex prints "couldn't set up sandbox" and refuses to run your command. What does this tell you about Codex's design?
Go deeper
Hand-picked sources to keep learning
The three sandbox modes and how the OS-enforced boundary works.
Approval policies, the UI presets, and the security model that ties them together.
Verify the live values for --sandbox / --ask-for-approval (approval enum and --full-auto status rot).
Per-domain network allowlists, OS enforcement (incl. Landlock), and the newer permission-profile schema.
The [sandbox_workspace_write] network_access / writable_roots keys and the granular approval form.
The elevated vs unelevated Windows sandbox modes and WSL2 behavior.