Approval modes and sandboxing

Read-only, workspace-write and full-access sandboxes, approval policies from untrusted to never, and how to pair the two without handing over your machine.

The sandbox decides what can be touched

Sandbox modeCan readCan writeNetworkFits
read-onlyThe repositoryNothingOff by defaultCode review, exploration, answering questions
workspace-writeThe workspace plus system filesInside the workspace and its temp dirOff by defaultNormal development on a task branch
danger-full-accessEverythingEverythingOnA disposable container and nothing else
# ~/.codex/config.toml
# The safest default that is still useful for a normal task.
sandbox_mode = "workspace-write"
approval_policy = "on-request"

# allow writes outside the workspace for one narrow case, and nothing else.
# Paths are absolute; keep the list short and review it as carefully as code.
[sandbox_workspace_write]
network_access = false
writable_roots = ["/tmp/build-cache"]

# Never commit a config that looks like this to a shared repo and call it a default:
# sandbox_mode = "danger-full-access"
# approval_policy = "never"
  • The sandbox is a boundary on the filesystem and the network, not a security product. It stops mistakes much more reliably than it stops a determined attack.
  • workspace-write is the right default for development: edits land in your project, and anything outside it requires an explicit decision.
  • Network access is off by default in workspace-write, which is why a package install fails inside a run. That failure is the feature working, not a bug to route around with a wider sandbox.
  • On macOS the sandbox uses the platform's seatbelt mechanism; on Linux it uses a container-style boundary. Behaviour differs slightly between platforms, so test the specific command you are automating.

The approval policy decides when it asks

Approval policyBehaviourUse when
untrustedAsks before anything beyond readingFirst contact with an unfamiliar repository
on-requestAsks only when it needs to leave the sandboxThe everyday default
on-failureRuns, retries with escalation if it failsBatch jobs where a failure is cheap to redo
neverNever asks; the sandbox is the only limitOnly inside a disposable, credential-free environment
# choose per invocation instead of editing a global config
codex --sandbox read-only --ask-for-approval untrusted "summarise how authentication works in this repo"
codex --sandbox workspace-write --ask-for-approval on-request "add the missing index to the orders migration"
codex --sandbox danger-full-access --ask-for-approval never   -C /tmp/scratch-container "generate a throwaway benchmark script"   # inside a container

# inspect what is actually in force before trusting it
codex --help | head -40
codex exec --help | head -40
⚠️
The dangerous combination is a wide sandbox with approval_policy = "never": nothing is checked and nothing is reversible. If you need unattended automation, get it inside a container with read-only mounts for anything sensitive, not by relaxing the sandbox on your own machine.

Escalation and its side effects

  • Escalation is per command. Approving pip install -r requirements.txt once does not approve every future install, but approving a broad command repeatedly is how a wide permission becomes the de facto default.
  • Prefer allowing a narrow command over allowing a whole class. pytest -q is a better grant than python, which can do anything.
  • Writing outside the workspace is the most common escalation request: a lock file in a parent directory, a build cache, a global tool config. Decide deliberately whether that path belongs in writable_roots.
  • Git operations are the ones that matter most. A read-only sandbox can inspect history; pushing, force-pushing and deleting branches should always be a human action.
  • If a run keeps asking for the same escalation, that is a signal to fix the task definition or the environment, not to widen the sandbox.
# make the escalation unnecessary by preparing the environment first
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt          # done by you, outside the agent run
pre-commit install --install-hooks       # hooks cached before the agent works

# now the agent's task needs no network and no writes outside the workspace
codex --sandbox workspace-write --ask-for-approval on-request \
  "run the test suite, fix the failing assertions in tests/test_reports.py, \
   do not add dependencies and do not modify tests other than that file"

FAQ

Is workspace-write safe enough for real work?
It is the appropriate default: the blast radius is your repository, which is under version control. The residual risks are a deleted uncommitted change and an unnecessary dependency, both of which are recoverable if you start from a clean tree.
Should I keep full access on a separate machine?
Yes, if you use it at all. A container or a disposable VM with no credentials, no mounted home directory and no access to production systems turns full access into a reasonable option instead of a reckless one.

What Codex is and how to set it up Security, cost and team practices

Last refreshed 2026-09-18.