Blog
Codex CLI sandbox modes, and the part the sandbox does not cover
Codex ships a sandbox, which is more than most agents do. Two things about it are worth knowing before you tune it, and one of them is a macOS quirk that will bite anyone trying to add a boundary of their own.
Codex CLI controls three separate things. sandbox_mode decides what the agent may touch: read-only, which is the default, workspace-write, or danger-full-access. network_access is a separate switch that is off unless you turn it on, in read-only and workspace-write alike. approval_policy decides when Codex stops to ask you, with untrusted, on-request and never. They are independent, so a permissive mode with approvals off behaves nothing like the same mode with approvals on. On macOS the sandbox is enforced by the kernel through Seatbelt, which is why wrapping Codex in your own outer sandbox does not narrow it further and instead breaks the one Codex was applying.
Two settings, two different jobs
Most confusion about Codex permissions comes from treating these as one dial. They are not.
sandbox_mode is about reach on the filesystem. read-only is the default and writes nothing. workspace-write makes the working directory writable, plus /tmp and $TMPDIR when they are enabled and any writable_roots you configure. danger-full-access is what its name says.
network_access is a separate switch, and this is the part people miss: it is off unless you turn it on, and it is off in workspace-write too. A mode that lets an agent write your repository still lets it reach nothing until you say so. That default is a good one and it is worth knowing you are turning it off when you turn network on for a task that needs an API.
approval_policy is about who is watching. untrusted, on-request and never describe how often Codex pauses for a human. Turning approvals off does not widen the sandbox, and tightening the sandbox does not add approvals.
The combination that catches people out is a wide mode with network on and approvals off, because each of the three looked reasonable on its own. Decide filesystem reach, then network, then how often you want to be interrupted inside both. (Verified against the SandboxMode enum in OpenAI's own source, codex-rs/protocol/src/config_types.rs, rather than a docs page, since the modes are defined there.)
The macOS quirk: sandboxes do not nest
This one is measured rather than documented, and it is the most useful thing on this page if you were about to add your own boundary.
On macOS the enforcement is Seatbelt, the kernel sandbox applied through sandbox-exec. A profile is applied when a process starts and inherited by every descendant. What is not obvious is that profiles do not compose: once a process is running under a profile that contains any deny rule at all, applying a second sandbox inside it fails outright with sandbox_apply: Operation not permitted. There is no rule you can write in the outer profile to re-permit it. Measured on macOS 26.5.2.
So the intuitive move, wrapping your whole terminal in your own Seatbelt profile so everything inside is contained, does the opposite of what you wanted. Codex starts, tries to apply its own sandbox, and cannot. You have replaced a boundary the vendor maintains with one you wrote, and you will not get an error that says so in those words.
The pattern that works is to confine something narrower than the shell the agent starts in: bound the folders at the level above, or bound a single process that received something sensitive, and let the agent keep applying its own profile.
What no sandbox mode covers
Every mode above decides what a process may touch. None of them decides what a credential may do.
If Codex is allowed to reach the network at all, then any token in its environment can go anywhere that reach allows, inside an ordinary request, and nothing in a sandbox reads a payload. read-only mode does not help either: a token in the environment is readable without writing anything, and whatever is read becomes context sent upstream.
That is the gap worth naming, because sandbox settings feel like they answered it. They answered the blast radius question. The credential question is separate and has a separate answer, which is that the value stops being in the room.
How this works in Forkbench
Forkbench is a Mac app for running coding agents, and it runs the codex binary you installed, with your config, your sandbox_mode and your approval_policy untouched. It does not wrap the pane, for exactly the reason in the section above.
What it adds sits on either side of the sandbox, and the more important half is the credential. A key in Vault is used by the command that needs it without the value reaching the agent, so it is not in the environment for any sandbox mode to have to contain, which is the gap the section above describes. The other half is a boundary above the agent rather than around it: a Thread can be locked to a set of folders, applied as each shell starts and inherited by everything it spawns, so Codex still applies its own sandbox inside.
The honest limit on that second one: a command you authorise can still print what it was handed, and a key bound to a destination can still be used against that destination. It is containment and evidence, not isolation.
Related: Seatbelt, containers and VMs compared
Frequently asked
What are the Codex CLI sandbox modes?
Three, set with sandbox_mode: read-only, which is the default and writes nothing; workspace-write, which makes the working directory writable along with /tmp and $TMPDIR when enabled and any writable_roots you configure; and danger-full-access. Network is not part of the mode. network_access is a separate switch that is off unless you enable it, including under workspace-write. A third setting, approval_policy, decides how often Codex stops to ask you: untrusted, on-request or never.
What is the difference between sandbox_mode and approval_policy?
sandbox_mode is a boundary and approval_policy is a checkpoint. The mode decides what Codex can reach whether or not anyone is watching. The policy decides how often a human is asked before it acts inside that reach. Turning approvals off does not widen the sandbox, and narrowing the sandbox does not add approvals, so a permissive mode with approvals off is the combination to be deliberate about.
Can I wrap Codex in my own macOS sandbox?
Not usefully, and it will quietly break Codex's own. macOS Seatbelt profiles do not nest: a process already running under a profile that contains any deny rule cannot apply a second sandbox, and the attempt fails with Operation not permitted. Wrapping the pane therefore replaces the vendor's boundary with yours rather than adding to it. Confine something narrower instead, such as the folders available to the shell the agent starts in.
Does read-only mode stop Codex leaking my API keys?
No. Read-only limits writing, and a credential in the environment is readable without writing anything, so it can still become context sent to the model provider. Codex's default of network off does help, and it is the strongest thing in that list. The moment you enable network for a task that actually needs an API, the credential in the environment can leave inside an ordinary request that no sandbox inspects. Keeping the key out of the process is a different mechanism from limiting what the process may change.
Can I run Codex CLI and Claude Code on the same project?
Yes, and each keeps its own sandbox settings, since they are separate binaries with separate configs. What does not come free is coordination: two agents in one checkout will overwrite each other, so give each its own git worktree, and neither vendor can read the other's memory, so keep the plan in a file or a board that belongs to neither of them.