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 mode | Can read | Can write | Network | Fits |
|---|---|---|---|---|
read-only | The repository | Nothing | Off by default | Code review, exploration, answering questions |
workspace-write | The workspace plus system files | Inside the workspace and its temp dir | Off by default | Normal development on a task branch |
danger-full-access | Everything | Everything | On | A 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-writeis 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 policy | Behaviour | Use when |
|---|---|---|
untrusted | Asks before anything beyond reading | First contact with an unfamiliar repository |
on-request | Asks only when it needs to leave the sandbox | The everyday default |
on-failure | Runs, retries with escalation if it fails | Batch jobs where a failure is cheap to redo |
never | Never asks; the sandbox is the only limit | Only 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.txtonce 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 -qis a better grant thanpython, 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.
Related
What Codex is and how to set it up Security, cost and team practices
Last refreshed 2026-09-18.