The Forge · 10 min mission

Codex CLI & config.toml: Setup That Sticks

Configure approvals, sandboxing, profiles, and MCP — once, correctly.

cliconfigurationsecurityFact-checked 2026-06-13
On this page

Codex is OpenAI's coding agent that runs locally on your machine — it reads your code, edits files, and runs commands from the terminal rather than from a chat window in a browser. The CLI is fast to install and friendly on day one, but its real personality lives in a single file: ~/.codex/config.toml. That file decides how much the agent can touch, when it stops to ask you, and which tools it can reach.

This guide takes you from a clean install to a setup that sticks — defaults you can trust on a Tuesday afternoon, profiles you can switch into when the job changes, and a security mindset that keeps a convenient install from becoming a stolen credential. Everything here is checked against OpenAI's official Codex documentation.

Why config.toml is the whole game

Most agent mistakes are not "the model was dumb." They are "the agent had more permission than the task needed." Codex separates two ideas that beginners tend to blur: the sandbox, which is the hard technical boundary on what the agent can do, and the approval policy, which decides when it pauses to ask. config.toml is where you set both. Get this file right once and every future session inherits sane behavior; get it wrong and you are either babysitting approvals all day or handing the agent the keys to your whole filesystem.

Install from an official source — and only an official source

  1. Pick one official installer

    Use the npm package npm install -g @openai/codex, Homebrew brew install --cask codex, or the standalone script curl -fsSL https://chatgpt.com/codex/install.sh | sh. These are the methods OpenAI documents. The package name is exactly @openai/codex — the scope matters (see the security section on why).

  2. Authenticate on first run

    Launch codex in any project directory. The first run prompts you to sign in with your ChatGPT account (Plus, Pro, Business, Edu, and Enterprise plans include Codex) or with an API key.

  3. Let the config file be created

    Codex stores user-level configuration at ~/.codex/config.toml. You can also keep a project-scoped .codex/config.toml in a repo. If the file does not exist yet, create it — every setting in this guide lives there.

install, sign in, run
… scroll to run this session
A clean first run. Codex signs you in, then drops you into an interactive session pointed at the current directory.

Sandbox modes: what the agent can do

The sandbox is the boundary Codex cannot cross even if it decides to. There are three modes, set with the sandbox_mode key:

read-only — Codex can inspect files but cannot edit them or run commands without approval. This is the safe default for exploring an unfamiliar repo, code review, or any session where you only want answers, not changes.

workspace-write — Codex can read files, edit within the workspace, and run routine local commands inside that boundary. This is the low-friction mode for everyday development. Crucially, network access is off by default here: in the default permission posture, Codex asks before using the internet or reaching beyond the workspace. You opt network back in explicitly (shown below), which is exactly the kind of decision you want to make on purpose rather than by accident.

danger-full-access — Codex runs with no sandbox restrictions at all: the filesystem and network boundaries are removed. The name is a warning, not decoration. Reserve it for throwaway containers and CI runners you fully control, never your primary machine.

Approval policy: when it stops to ask

The sandbox decides capability; approval_policy decides interruption. The documented values are untrusted (Codex runs only commands it considers trusted and asks for everything else), on-request (the agent runs what it can within the sandbox and pauses to ask when it wants to step outside the boundary), and never (it never pauses to ask — pair this only with a sandbox tight enough that "never asking" is still safe). Codex also supports a granular object form of approval_policy for fine-grained control, but the three string values cover the cases most people need.

The mental model: sandbox is the fence, approval is the doorbell. A tight fence with a loud doorbell (read-only + on-request) is cautious. A wide fence with no doorbell (danger-full-access + never) is how people lose an afternoon to an agent that rm -rf'd the wrong directory.

KeyValueEffect
sandbox_mode"read-only"Inspect files only; edits and commands need approval
sandbox_mode"workspace-write"Edit inside the workspace and run local commands; network off by default
sandbox_mode"danger-full-access"No filesystem or network boundary at all — containers only
approval_policy"untrusted"Run only trusted commands; ask for everything else
approval_policy"on-request"Work within the sandbox; pause to ask before stepping outside it
approval_policy"never"Never pause to ask — only safe with a tight sandbox
The two independent dials in config.toml. Pick a sandbox for capability and an approval policy for interruption — they compose.
~/.codex/config.toml — a conservative everyday baseline
toml
# Default model and behavior for every session
model = "gpt-5.5"
approval_policy = "on-request"
sandbox_mode = "workspace-write"
 
# Editing is allowed in the workspace, but the agent must ask
# before it reaches the network. Opt in explicitly when needed.
[sandbox_workspace_write]
network_access = false
# writable_roots = ["/extra/path/the/agent/may/write/to"]

The [sandbox_workspace_write] table

When you choose workspace-write, the [sandbox_workspace_write] table lets you tune the edges of that boundary. The most consequential key is network_access (a boolean): set it to true only when the task genuinely needs the internet — installing dependencies, calling an API — and prefer to grant it inside a dedicated profile rather than your global default. The table also accepts writable_roots, an array of additional paths the agent may write to beyond the workspace, plus toggles like exclude_slash_tmp and exclude_tmpdir_env_var for controlling whether temporary directories stay writable. Keep this table small. Every path you add and every boolean you flip to true is a piece of the fence you just removed.

Profiles: a posture per file

You rarely want a single permission posture. Reviewing a stranger's pull request wants read-only; an unattended CI job wants no approval prompts; refactoring your own repo wants the comfortable middle. Profiles let you keep each of these postures in its own file and switch between them per invocation.

You define each profile in its own file at ~/.codex/NAME.config.toml, using the same top-level keys you would put in config.tomlnot nested under a [profiles.NAME] table. Select one at launch with --profile NAME (or the shorthand -p NAME): Codex loads ~/.codex/config.toml first, then overlays the chosen profile file on top. Settings resolve by precedence — command-line arguments win over the active profile, which wins over the top-level config.toml defaults — so your top-level keys are the "if I say nothing" behavior, and each profile is a deliberate override you opt into by name.

(Heads up if you are migrating an older setup: as of Codex 0.134.0 the in-config [profiles.NAME] table syntax was removed — --profile no longer reads it — so move each block into its own ~/.codex/NAME.config.toml file.)

one profile file per job — top-level keys, no [profiles.NAME] table
toml
# ~/.codex/config.toml — the cautious default when you pass no flags
approval_policy = "on-request"
sandbox_mode = "workspace-write"
 
# ~/.codex/review.config.toml — inspect a branch you do not trust, change nothing
approval_policy = "untrusted"
sandbox_mode = "read-only"
 
# ~/.codex/ci.config.toml — unattended runner, no human to answer prompts
approval_policy = "never"
sandbox_mode = "workspace-write"
 
# ~/.codex/deps.config.toml — refactor that needs to hit the network on purpose
sandbox_mode = "workspace-write"
[sandbox_workspace_write]
network_access = true
 
# invoke a posture by name:  codex --profile review   (or  codex -p ci)

MCP servers in Codex

Codex can call external tools through the Model Context Protocol (MCP) — the same open standard other agents use to reach databases, browsers, issue trackers, and custom internal tools. You wire a server in with a [mcp_servers.NAME] table. The keys are the launch recipe: command is the executable, args is an array of arguments, env is a table of environment variables to pass, and startup_timeout_sec bounds how long Codex waits for the server to come up.

A practical note that doubles as a security note: an MCP server is a program Codex will execute on your behalf. Adding one is closer to installing a plugin than editing a setting. Pin it to a specific, trusted package, read what it does, and pass secrets through env rather than hard-coding them in arguments that might land in logs.

A setup that sticks

Put it together and the workflow is calm. Your top-level config.toml is conservative — workspace-write plus on-request, network off. Named profiles capture the exceptions: review for untrusted code, ci for unattended runs, deps for the rare network task. MCP servers are added deliberately, one trusted package at a time, with secrets in env. And every install comes from @openai/codex or another method OpenAI documents — never a lookalike. The payoff is that you stop thinking about permissions during the work: you decided the boundaries once, in a file you can read top to bottom, and now the agent operates inside them by default.

~/.codex/config.toml — registering an MCP server
toml
[mcp_servers.docs]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/notes"]
startup_timeout_sec = 20
 
[mcp_servers.github]
command = "github-mcp-server"
args = ["--read-only"]
env = { GITHUB_TOKEN = "ghp_…" }

Commands and flags: the file is only half the story

config.toml sets the standing posture; the command line sets the posture for this one run. Most of what you tune in the file has a flag that overrides it for a single invocation, and a handful of flags have no file equivalent at all. [V] Every flag below is a documented global option on the codex command — combine them freely, and remember that anything you pass here outranks every file.

FlagLong formWhat it does
-m--modelPick the model (and reasoning level) for this run — overrides model [V]
-s--sandboxSet the sandbox for this run: read-only, workspace-write, or danger-full-access — overrides sandbox_mode [V]
-a--ask-for-approvalSet the approval mode (untrusted / on-request / never) — overrides approval_policy [V]
-C--cdRun as if started from a different directory — sets the workspace root [V]
-i--imageAttach an image (screenshot, design spec) to the prompt [V]
-p--profileLoad a named profile file ~/.codex/NAME.config.toml on top of config.toml [V]
-c--configOverride an arbitrary config key inline, e.g. -c approval_policy=never — highest precedence of all [V]
--add-dirGrant the session an additional writable directory beyond the workspace [V]
--ossRun against a local open-source model instead of an OpenAI-hosted one [V]
--searchEnable the web-search tool for this session [V]
--remoteRun the task on Codex cloud infrastructure rather than locally [V]
--yoloDangerously bypass *both* approvals and the sandbox — equivalent to danger-full-access + never, containers only [V]
Global flags on the `codex` command. Short forms on the left; each maps to a config.toml key or to a run-only behavior with no file equivalent.

Inline overrides and the precedence ladder

The -c / --config flag is the escape hatch: it sets any config key for one run using TOML syntax, with dot notation for nested tables. [V] The value is parsed as TOML, so strings need TOML quoting — which is why a string value often carries an inner pair of quotes:

  • codex -c approval_policy=never -c sandbox_mode=workspace-write — flip both dials for a single run.
  • codex --config model='"gpt-5.4"' — the inner "…" is the TOML string; the outer '…' keeps your shell from eating it. [V]
  • codex -c sandbox_workspace_write.network_access=true — dotted path reaches into a table. [V]
  • codex --config 'shell_environment_policy.include_only=["PATH","HOME"]' — values can be arrays too. [V]

When the same key is set in more than one place, Codex resolves it top-down and the first hit wins. [V] From highest priority to lowest:

  1. CLI flags and -c overrides — what you typed this run.
  2. Project config.codex/config.toml files from the repo root down to your cwd (closest wins; trusted projects only).
  3. Profile file selected with --profile NAME (~/.codex/NAME.config.toml).
  4. User config~/.codex/config.toml.
  5. System config/etc/codex/config.toml on Unix, if present.
  6. Built-in defaults.

The practical read: a flag always beats the file, a trusted project's .codex/config.toml beats your personal profile, and your global defaults are the floor that everything else stands on. Note the trust gate on layer 2 — an untrusted project's .codex/ directory is skipped entirely (see trust_level below), so a hostile repo cannot silently lower your guardrails by shipping its own config.

Sessions: resume, fork, replay

Every interactive run is recorded as a rollout transcript — newline-delimited JSON written under ~/.codex/sessions/. [V] That history is what makes a session resumable instead of disposable:

  • codex resume — pick from recent sessions interactively. [V]
  • codex resume --last — jump straight back into the most recent one. [V]
  • codex resume --last --all — widen the search beyond the current directory to every recorded session. [V]
  • codex resume <SESSION_ID> — reopen a specific session by its id. [V]
  • codex fork — branch from an existing session so you can explore an alternative path without overwriting the original transcript. [V]

Because the transcript is plain JSONL on disk, it is also auditable: you can read exactly what the agent did, which is the other half of why keeping sessions tight (and out of untrusted hands) matters.

KeyTypeWhat it controls
model_context_windownumberContext-window tokens available to the active model [V]
model_auto_compact_token_limitnumberToken threshold that triggers automatic history compaction; unset uses the model default [V]
[history] max_bytesnumberCaps history.jsonl size by dropping the oldest entries [V]
[history] persistencesave-all \| noneWhether session transcripts are saved to history.jsonl at all [V]
[shell_environment_policy] inheritall \| core \| noneBaseline environment the agent inherits when it spawns a subprocess [V]
[shell_environment_policy] excludearray<glob>Variables stripped *after* the defaults; defaults already drop names matching KEY/SECRET/TOKEN [V]
notifyarray<string>External command Codex invokes for notifications, passed a JSON payload [V]
projects.<path>.trust_leveltrusted \| untrustedMarks a path trusted; an untrusted project skips all project-scoped .codex/ layers (config, hooks, rules) [V]
Long-tail config.toml keys worth knowing once your setup matures. All documented in the configuration reference.

Permission profiles: the modern alternative to sandbox_mode

sandbox_mode plus [sandbox_workspace_write] is the original, coarse model — one fence with a few toggles. The newer permission profiles replace it with a named policy that combines filesystem rules (what commands may read or write) and network rules (which destinations they may reach), expressed as glob-matched allow / write / read / deny entries where the most specific rule wins and deny beats write beats read. [V]

You turn the system on by setting default_permissions to a profile name. Three presets are built in: :read-only (execution can read but not write), :workspace (writes allowed inside the active workspace roots and temp dirs), and :danger-full-access (local sandbox removed). [V] Custom profiles live under [permissions.<name>] and should extends a preset so baseline protections carry forward instead of being restated. [V]

The one rule that trips people up: permission profiles do not compose with the old sandbox keys. [V] Configure either default_permissions + [permissions], or sandbox_mode / [sandbox_workspace_write] — never both in the same config. Pick the model you want and commit to it.

Permission simulator

Permission simulator

Pick an action and a permission mode. The engine runs the same deny → ask → allow precedence Claude Code uses and reveals the verdict — with the one rule that fired.

Action

What is Claude about to do?

Permission mode

How freely is Claude allowed to act?

permission check
defaultMode
Edit config.ts
mode = default
ASKPauses for your approval

Editing a file is a modification — default mode prompts you to approve it first.

ask · Edit (first use)
Toggle filesystem and network rules and watch which operations a profile would allow, write, or deny — the deny-beats-write-beats-read resolution made visible.
A senior profile: extend :workspace, deny secrets, allowlist two hosts
toml
# ~/.codex/config.toml — opt into the permission model (not sandbox_mode)
default_permissions = "locked-edit"
 
# Inherit :workspace, then carve out the dangerous bits
[permissions.locked-edit]
extends = ":workspace"
 
# Writable workspace, but never let the agent read or write secret files
[permissions.locked-edit.filesystem.":workspace_roots"]
"." = "write"
"**/*.env" = "deny"
 
# Network is off until you name the exact hosts that are allowed
[permissions.locked-edit.network]
enabled = true
 
[permissions.locked-edit.network.domains]
"api.openai.com" = "allow"
"*.github.com" = "allow"
# everything not listed is denied by omission
~/.codex/requirements.toml — lock the policy so it cannot be loosened
toml
# requirements.toml is the enforcement layer. List the profiles a machine is
# allowed to use; once present, any profile NOT listed is denied — including
# omitted built-ins and profiles added in future Codex versions.
allowed_permission_profiles = ["locked-edit", ":read-only"]
 
# Refuse hooks unless they come from managed, admin-approved configuration.
allow_managed_hooks_only = true

Knowledge check

You want Codex to refactor your own repo and run the test suite, but you do NOT want it touching the network or running unattended. Which config.toml pairing fits best?

Reach the end and this star joins your charted sky.