AIFreeAPI Logo

Codex Sandbox and config.toml: Permissions Without Full Access

A
9 min readAI Development Tools

The sandbox controls where commands can act; approvals control when Codex must stop and ask. Diagnose that boundary before granting broader host access.

Codex configuration panel separating project scope, sandbox permissions, approval decisions, and one-off overrides

Codex's sandbox is the enforced boundary for local commands. It determines which files a command may modify and whether that command can use the network. The approval policy answers a different question: when must Codex pause and ask before crossing a boundary? Changing one does not silently change the other. OpenAI's Sandbox documentation also says spawned tools such as git, package managers, and test runners inherit the same boundary.

For ordinary local development, workspace-write with on-request is the useful baseline: routine work can continue inside the project, while a genuine boundary crossing can trigger an approval. read-only suits inspection. danger-full-access removes filesystem and network sandbox restrictions; it is a deliberate trust decision, not a generic cure for a failed install or a missing writable path. Likewise, approval_policy = "never" means “do not ask,” not “grant full access.”

The safe configuration workflow starts by identifying the blocked control, then changing the smallest layer that owns it. Personal defaults belong in ~/.codex/config.toml. A trusted repository can add .codex/config.toml for project behavior. Profiles and CLI flags serve reusable or one-off modes. This prevents a permission problem from turning into a simultaneous model, provider, network, and MCP rewrite.

Before editing anything, make a backup of the file you will touch. Do not delete the entire .codex directory to recover from one bad table: it may also contain credentials, profiles, rules, or other working state.

Put the setting in the layer that owns it

Codex resolves ordinary values from highest to lowest precedence:

  1. CLI flags and --config overrides;
  2. trusted-project .codex/config.toml files, with the closest directory winning;
  3. the file selected with --profile;
  4. ~/.codex/config.toml;
  5. /etc/codex/config.toml on Unix, when present;
  6. built-in defaults.

That order comes from OpenAI's configuration precedence reference. It explains why editing the user file may appear to do nothing: a trusted project or command-line value can be winning above it.

The layer choice is more important than the syntax:

Setting intentBest homeWhy
Your normal model, reasoning effort, notifications, or personal MCP serversUser configThese defaults should follow you across projects
A repository's safe execution boundary or shared project behaviorTrusted project configThe rule travels with that repository
A reusable “deep review” or “read-only audit” modeSeparate profile fileIt changes a coherent set of values without rewriting your base
A temporary experimentCLI flag or -c key=valueIt disappears after the run and cannot silently become policy
A non-bypassable organizational restrictionAdmin-managed requirementsLocal files are defaults, not enforcement

There is an important exception. Project config cannot override machine-local provider, authentication, profile-selection, notification, or telemetry classes, including model_provider, model_providers, profile, and otel. OpenAI lists the current reserved keys in the configuration reference. Put those values in user-level configuration instead of fighting the project layer.

Codex configuration precedence from CLI overrides through project, profile, user, system, and built-in layers
Codex configuration precedence from CLI overrides through project, profile, user, system, and built-in layers

Pick a sandbox boundary before tuning anything else

A practical personal file can be this small:

toml
approval_policy = "on-request" sandbox_mode = "workspace-write"

The mode and policy form two axes:

ControlDecision it makesWhat it does not prove
sandbox_modeWhich filesystem and network resources commands can reachWhether Codex will ask at a boundary
approval_policyWhen a command must pause for approvalThat the requested action becomes technically possible
approvals_reviewerWho reviews eligible approval promptsA change to the sandbox itself

OpenAI currently documents three common sandbox modes. read-only permits inspection but restricts edits and commands. workspace-write supports routine work inside the workspace. danger-full-access removes sandbox restrictions. The common approval policies are untrusted, on-request, and never. Use the current permissions guide rather than assuming every client version displays the same labels: the CLI exposes /permissions, while the IDE and desktop app use the control below the composer.

If the job needs one additional repository, extend the writable area without opening the whole host:

toml
sandbox_mode = "workspace-write" approval_policy = "on-request" [sandbox_workspace_write] writable_roots = ["/absolute/path/to/second-repo"] network_access = false

Use a specific absolute path. Adding a home directory or drive root defeats the value of a narrow boundary. The network_access value controls outbound access for commands inside the workspace-write sandbox. It is not the same control as web search, apps, MCP servers, or a remote browser. A working browser lookup therefore does not prove that npm install, a test downloader, or your application process can reach the internet.

The same mode has different platform prerequisites

The trust model is consistent, but enforcement is platform-native. OpenAI's current sandbox prerequisites identify different first failure points:

Host pathEnforcement routeCheck before changing permissions
macOSBuilt-in Seatbelt frameworkWhether the target path is inside the allowed workspace
Native WindowsCodex native Windows sandboxSandbox setup, device policy, and native compatibility
WSL2Linux sandbox implementationWSL2 plus bubblewrap availability
Linuxbubblewrap and OS isolationbwrap, user namespaces, and AppArmor policy

On Ubuntu or Debian, install the distribution package first:

bash
sudo apt install bubblewrap

On Fedora:

bash
sudo dnf install bubblewrap

If Codex still reports that it cannot create the required user namespace, follow the distribution-specific AppArmor guidance in the official page before disabling a system-wide restriction. Containerized Linux adds another boundary: Docker may block the namespace, setuid bwrap, or seccomp operations Codex needs. Only after the outer container provides the intended isolation does it make sense to consider a broader Codex mode inside that container. The same setting on the bare host is a different risk decision.

Use profiles for modes, not inline duplicates

Profiles are separate configuration layers:

toml
# ~/.codex/deep-review.config.toml model_reasoning_effort = "xhigh" approval_policy = "on-request" sandbox_mode = "read-only"

Run it with:

bash
codex --profile deep-review

The profile file only needs values that differ from your base. Project config and CLI flags can still override it. Profile formats can change between client versions, so confirm the current layout under Advanced Config: Profiles before copying an older inline [profiles.name] example.

Treat MCP as a process or connection, not just TOML

An MCP table can be syntactically valid while its process fails immediately. For a local STDIO server, keep the command, arguments, and forwarded environment explicit:

toml
[mcp_servers.docs] command = "docs-server" args = ["--mode", "read-only"] env_vars = ["DOCS_TOKEN"] startup_timeout_sec = 20 tool_timeout_sec = 60 enabled = true

For a streamable HTTP server, use a URL and refer to a token by environment-variable name:

toml
[mcp_servers.issue_tracker] url = "https://mcp.example.internal/mcp" bearer_token_env_var = "ISSUE_TRACKER_TOKEN" enabled = true

Do not paste real tokens into an article, repository config, or support message. The official MCP configuration guide documents STDIO and streamable HTTP transports, OAuth, environment-based headers, allow/deny tool lists, timeouts, and required.

After an edit, use the commands exposed by your installed version:

bash
codex mcp list codex mcp --help

For OAuth-capable servers, the CLI also provides codex mcp login SERVER_NAME. A server appearing in the list proves that Codex parsed its definition; it does not prove the executable exists, the URL is reachable, authentication succeeds, or every tool behaves correctly.

Verify one boundary at a time

Use a short ladder rather than one “does Codex work?” test:

  1. File boundary: confirm you edited the intended user, project, or profile file.
  2. TOML boundary: ask the current CLI to reject unknown configuration where supported.
  3. Precedence boundary: remove one-off flags and check whether a trusted project or profile is overriding the value.
  4. Filesystem boundary: confirm the target is inside the workspace or an explicit writable root.
  5. Approval and command-network boundary: check whether a prompt can surface and whether the subprocess has outbound access.
  6. Platform boundary: confirm native Windows, WSL2, bubblewrap, AppArmor, or the outer container can establish the sandbox.
  7. Provider and MCP boundary: test auth, endpoint, server startup, and tool calls separately.

Recent Codex CLI builds expose --strict-config and a doctor command. Check your installed help before relying on them:

bash
codex --version codex --help codex doctor --help

Where available, a useful read-only pass is:

bash
codex --strict-config doctor --summary

--strict-config is meant to fail on unrecognized fields, while doctor checks local installation, configuration, auth, and runtime health. If your version does not expose either option, use the exact commands shown by its --help and the configuration reference that matches that release.

Diagnostic ladder separating file, TOML, precedence, permission, provider, and MCP failures
Diagnostic ladder separating file, TOML, precedence, permission, provider, and MCP failures

Diagnose the symptom instead of rewriting the file

SymptomMost likely boundaryFirst useful check
A value is ignored only in one repoTrust or project precedenceConfirm the repo is trusted and inspect closer .codex/config.toml layers
A provider block works in CLI but not IDEEnvironment visibilityConfirm the IDE process can see the named environment variable
“Unknown field” or startup parse failureVersion or TOMLCompare the key with the current reference and run strict config if available
Codex reads a repo but cannot edit itSandbox mode or workspace rootCheck the active permissions and exact target path
Package install or API-backed test cannot connectSandboxed command networkCheck network_access, proxy/DNS, and approval separately from web search
Native Windows reports sandbox setup failureWindows sandbox route or device policyConfirm native versus WSL2 setup before changing access mode
Model changed but requests still use another routeProvider ownership or higher overrideCheck model_provider, profile, project layer, and CLI flags separately
MCP is listed but unavailableProcess, URL, auth, or timeoutRun codex mcp list, then verify the executable/URL and credential source
A risky mode will not enable on a work machineManaged requirementsAsk whether organizational requirements.toml restricts that value

On managed Business or Enterprise machines, requirements.toml can constrain approval policies, permission profiles, web-search modes, MCP allowlists, plugins, and feature flags. A user value that conflicts with an enforced rule can fall back to an allowed value with a notice. That behavior is documented in Admin-enforced requirements. Do not “fix” it by moving the same forbidden value to another local layer.

A safe change sequence

For routine work, use this order:

  1. identify the one behavior you want to change;
  2. choose user, trusted project, profile, or one-off scope;
  3. back up only that file;
  4. edit one coherent block;
  5. parse and inspect with the installed CLI;
  6. prove read access, then one workspace edit, then a project-owned test;
  7. test only the extra directory or network access the job actually needs;
  8. if it fails, return to the first unproven boundary;
  9. keep the change only after the expected behavior is visible.

That sequence separates four very different outcomes: a config layer did not load, the sandbox correctly denied an action, the approval policy did not allow a prompt, or the host could not establish the sandbox. It is much faster than debugging a full-access template that changed every control at once.

If the real decision is whether Codex is the right coding-agent surface rather than how to configure it, use the separate Claude Code vs Codex comparison. If your provider is specifically Microsoft Foundry, follow the dedicated Azure OpenAI for Codex route instead of mixing an Azure deployment block into a general baseline.