Guide · Codex CLI, IDE extension and desktop app · Checked against the docs on 29 October 2026
Codex sandbox, explained from the config file.
Short answer. Codex runs every command it starts inside an operating-system sandbox: Seatbelt on macOS, bubblewrap and seccomp on Linux and WSL2, MXC or a sandbox user on Windows. sandbox_mode sets the boundary and approval_policy sets when Codex stops to ask. In a git repository the default is workspace-write with on-request approvals and no network. Most of the risk comes from three switches: --yolo, network without a domain allowlist, and secrets left in the environment.
How does the Codex sandbox work?
The sandbox applies to local work in the Codex CLI, the IDE extension and the ChatGPT desktop app. It wraps each shell command Codex runs and everything that command spawns, so git, npm or a test runner inherit the same limits. Two settings work together: the sandbox decides what a command can technically touch, and the approval policy decides when Codex must pause and ask before going further.
| Platform | Mechanism | Setup | Watch for |
|---|---|---|---|
| macOS | Seatbelt, the sandbox framework built into macOS | Nothing to install. | codex sandbox --log-denials prints what the sandbox refused after a test command. |
| Linux, WSL2 | bubblewrap for the filesystem view, then no_new_privs and a seccomp filter; network cut with a separate namespace | sudo apt install bubblewrap or sudo dnf install bubblewrap. Codex falls back to a bundled helper that needs unprivileged user namespaces. | Ubuntu 24.04 may block user namespaces through AppArmor. WSL1 is not supported: sandboxed commands are rejected. |
| Windows (PowerShell) | MXC native process isolation, with elevated (dedicated sandbox users, firewall rules) and unelevated (restricted token) as legacy fallbacks | MXC needs Windows 11 24H2 build 26100.9278 or 25H2 build 26200.9278. Opt in with prefer_mxc = true under [features]. | unelevated has weaker network isolation and no denied read paths. |
On Linux the design is worth knowing in detail because it explains what leaks. The helper mounts the whole filesystem read-only (--ro-bind / /), binds each writable root on top, then re-applies .git, the resolved gitdir: target and .codex as read-only. It always creates a new user namespace and, by default, a PID namespace. With network restricted it adds --unshare-net, and the seccomp filter blocks network syscalls and io_uring, which could otherwise bypass the socket rules. Codex prints a warning at startup when bwrap is missing or user namespaces fail. It is a warning, not a stop, so read your startup output.
To see the boundary from outside the agent, run a command through it yourself with codex sandbox, for example codex sandbox -- touch ../outside.txt. The write should fail. On macOS, --log-denials lists every refusal.
Which sandbox mode should you use?
sandbox_mode | Files | Network | Use it for |
|---|---|---|---|
| read-only | Reads files. No edits and no commands without approval. | Off | Reviewing an unknown repository, CI jobs that only analyse. |
| workspace-write | Reads, edits inside the workspace and runs routine commands there. Protected paths stay read-only. | Off by default | Daily local work. The default in a version-controlled folder. |
| danger-full-access | Any file your account can reach. | On | A throwaway VM or container that is itself the boundary. |
Codex picks the mode from the folder. A version-controlled folder starts in Auto (workspace-write plus on-request). A folder without version control starts read-only, and Codex can also stay read-only until you trust the directory in onboarding or with /permissions. The desktop app and IDE name the same choices Ask for approval, Approve for me (eligible prompts go to a reviewer agent) and Full access.
config.tomlthe three keys that decide most behaviour# ~/.codex/config.toml
sandbox_mode = "workspace-write" # read-only | workspace-write | danger-full-access
approval_policy = "on-request" # on-request | never | { granular = { ... } }
web_search = "cached" # disabled | cached | indexed | live| Preset | Flags |
|---|---|
| Auto (default in a git repository) | --sandbox workspace-write --ask-for-approval on-request |
| Read-only browsing | --sandbox read-only --ask-for-approval on-request |
| Read-only CI job | --sandbox read-only --ask-for-approval never |
| Auto-review | --sandbox workspace-write --ask-for-approval on-request -c approvals_reviewer=auto_review |
| Dangerous full access | --dangerously-bypass-approvals-and-sandbox (alias --yolo) |
If a tool needs one more folder, add it with sandbox_workspace_write.writable_roots rather than switching to full access. For a second project, OpenAI's guidance is to open a separate project or worktree instead of widening the first one.
What do the approval policies change?
on-request: commands the sandbox allows run without a prompt. Anything outside the sandbox, or that needs the network, asks first. This is the recommended setting for interactive work.never: Codex never asks and stays inside the chosen sandbox. Withread-onlyit is the safe CI combination. Withdanger-full-accessnothing stands between the model and your machine.- Granular:
approval_policy = { granular = { ... } }keeps chosen prompt categories interactive (sandbox approvals, rules, MCP elicitations,request_permissions, skill approval) and auto-rejects the rest. untrustedis retired. Codex no longer acceptsapproval_policy = "untrusted", and leaving it in a config, profile or managed default can stop the client from starting.on-failureis deprecated. For the old strict behaviour, settrust_level = "untrusted"on the project instead.
approvals_reviewer = "auto_review" sends eligible prompts to a reviewer model instead of you. It only sees actions that already needed approval, it denies critical-risk actions, and it fails closed when its own prompt or parse fails. It does not move the sandbox boundary, and admins can forbid it with allowed_approvals_reviewers.
What stays protected in workspace-write?
Inside every writable root, these paths stay read-only recursively, so a prompt-injected command cannot rewrite your history, your agent config or your cloud profile:
.git, whether a directory or a pointer file, plus the Git directory it resolves to.codexand.agents, when they are directories.aws, when it is a directory
Two writable areas are easy to forget: /tmp and $TMPDIR. Remove them with exclude_slash_tmp = true and exclude_tmpdir_env_var = true under [sandbox_workspace_write] if your tools do not need them. And note what the list covers: .aws is write-protected, not hidden. Reads are a separate question, covered below.
How do you turn on network access safely?
Network is off in workspace-write. Turning it on with network_access = true opens every destination. Pair it with the network proxy, which applies domain rules: exact hosts match only themselves, *.example.com matches subdomains but not the apex, **.example.com matches both, and a deny beats an allow.
config.tomlnetwork on, package registries only[sandbox_workspace_write]
network_access = true
[features.network_proxy]
enabled = true
domains = { "registry.npmjs.org" = "allow", "pypi.org" = "allow", "files.pythonhosted.org" = "allow" }The same rules can live in a named permissions profile, which keeps one profile per kind of task. The proxy feature still has to be on for the profile's domain rules to apply.
config.tomlpermissions profile variantdefault_permissions = "project-edit" [features] network_proxy = true [permissions.project-edit] extends = ":workspace" [permissions.project-edit.network] enabled = true [permissions.project-edit.network.domains] "registry.npmjs.org" = "allow"
- Local destinations.
allow_local_bindingdefaults tofalse, which blocks loopback, link-local and private addresses. Keep it there unless a local service is the point of the task. - DNS rebinding. The proxy's DNS checks are best effort. OpenAI's documentation says they reduce the risk without removing it.
- What the proxy does not see. Web search, app and MCP connections, browser activity and the client's own model requests bypass it. A domain allowlist does not limit a connected MCP server.
- Web search. The default
cachedmode reads an OpenAI-maintained index rather than live pages.live(same as--search) fetches live content, and becomes the default under--yolo. Treat every result as untrusted input: fetched text can carry instructions. - Two flags to refuse.
dangerously_allow_non_loopback_proxyanddangerously_allow_all_unix_socketswiden the trust boundary and belong only in tightly controlled environments.
How does the Codex sandbox work on Windows?
Native Windows has three implementations, and the one you get changes what is enforced:
| Implementation | How it isolates | Limits |
|---|---|---|
| mxc | Native process isolation. No admin setup, extra accounts or firewall rules. | Needs Windows 11 24H2 or 25H2 at build .9278. Child processes stop when the foreground command exits, so detached dev servers do not survive. |
| elevated | Admin-approved setup creates lower-privilege sandbox users, filesystem boundaries, firewall rules and local policy. | Some enterprise devices block the setup. Error 1385 means a logon right is missing. |
| unelevated | A restricted token from your own user, ACL boundaries, environment-level offline controls. | Weaker network isolation, no denied read paths. A fallback only. |
Test MXC on one command with codex -c windows.sandbox=mxc sandbox ... (Codex CLI 0.162.0 or later): MXC_OK and exit code 0 mean it works. Admins pin the choice in requirements.toml, for example allowed_sandbox_implementations = ["elevated"] under [windows] to forbid the unelevated fallback. Inside WSL2, Codex uses the Linux sandbox described above. If Codex warns that a folder is writable by Everyone, fix the ACL: the sandbox cannot fully protect a folder that is already open to every account.
What has escaped the Codex sandbox before?
The openai/codex advisory page lists one advisory, and NVD lists a second CVE reported outside it. Both teach something about where the boundary sits.
| CVE | What happened | Fixed in |
|---|---|---|
| CVE-2025-59532 | Codex could treat a working directory generated by the model as the sandbox's writable root, which allowed file writes and command execution outside the workspace. The network restriction was not affected. CVSS 4.0 score 8.6, published 19 September 2025. | npm 0.39.0, IDE extension 0.4.12 |
| CVE-2025-61260 | A repository's .env could point CODEX_HOME at a project-local .codex/config.toml, whose MCP server entry ran its command as soon as the user started codex in that folder, without confirmation. NVD lists CVSS 3.1 at 9.8. | Versions after 0.23.0 |
The first one shows that the sandbox is only as good as the inputs used to build it. The second shows a route around it entirely: configuration read from the repository started a process before any sandboxed command ran. Keep Codex current (0.165.0 on npm when we checked), and treat a repository you did not write as untrusted configuration as well as untrusted code.
What does the sandbox not cover?
- Reads. The sandbox restricts writes far more than reads. On Linux everything is mounted read-only, which means readable, including
~/.sshand cloud credentials. A permissions profile can mark a pathnone. Deny-read globs are evaluated when each process starts, so a matching file created later stays readable to that process. - Secrets in the environment.
shell_environment_policy.ignore_default_excludesdefaults totrue, which keeps variables whose names contain KEY, SECRET or TOKEN in the environment of every command. Set it tofalse, and setinherit = "core"so commands start from a minimal environment. - What you approve. An approved escalation runs outside the boundary. Read the command and its arguments, not the summary.
- Cloud tasks. Codex Cloud (Legacy) runs in isolated containers with a setup phase that can reach the network and an agent phase that is offline by default. Policies for local and cloud execution are separate: allowing one grants nothing in the other.
- Content that steers the agent. The sandbox limits the damage of a bad command. It does not stop the model from wanting to run one after reading a poisoned README, issue or web page. That is indirect prompt injection, OWASP ASI01.
What does a hardened Codex config look like?
A starting point for a developer machine. It keeps the default boundary, closes the two writable temp areas you rarely need, strips secrets from command environments and marks third-party repositories untrusted.
~/.codex/config.tomlhardened local defaultssandbox_mode = "workspace-write" approval_policy = "on-request" web_search = "cached" [sandbox_workspace_write] network_access = false exclude_slash_tmp = true [shell_environment_policy] inherit = "core" # all | core | none ignore_default_excludes = false # strip variables named *KEY*, *SECRET*, *TOKEN* # A repository you did not write: no project .codex/ config, hooks or rules, # and commands need approval unless a rule allows them. [projects."/home/me/src/third-party-repo"] trust_level = "untrusted"
For a team, set the same choices centrally. Managed configuration takes precedence over the user's file and exposes allow-lists such as allowed_approval_policies, allowed_web_search_modes and allowed_approvals_reviewers, so a developer cannot switch to never plus full access on a company laptop. Turn on OpenTelemetry export (off by default) with log_user_prompt = false if you want an audit trail without storing prompts.
Then check it from outside the agent: run codex sandbox with a write outside the workspace, a curl to a host that is not allowed, and an env | grep -i token. All three should come back empty or refused.
