AI Catchup

Codex Permission Profiles: Least-Privilege Controls for Local Agent Work

By 11 min read

OpenAI shipped Codex permission profiles in beta -- reusable, inheritable policies that replace the coarse sandbox_mode/sandbox_workspace_write combo. A profile binds OS-enforced filesystem read/write/deny rules (down to **/*.env) to per-domain network and Unix-socket rules. Enterprise admins get fail-closed allowlists via requirements.toml. Profiles govern local sandboxed command execution only, not MCP servers, app connectors, browser, or cloud.

OpenAI shipped a replacement for Codex's coarse sandbox modes: reusable, inheritable permission profiles that bind OS-enforced filesystem read/write/deny rules -- down to **/*.env -- to per-domain network and Unix-socket policies, plus fail-closed admin allowlists. A profile is a single named policy that describes least-privilege boundaries for the local commands Codex runs on your behalf. The feature is beta and under active development, and it governs local sandboxed command execution only -- not MCP servers, app connectors, the browser, or Codex cloud.

This is the policy layer that sits underneath the automation surface we covered in Codex hooks and programmatic access tokens, and it complements the per-site browser controls in the Codex Chrome extension. For where Codex's local execution model sits relative to its peers, see our Codex CLI vs Claude Code vs Cursor architecture comparison.

Key Takeaways

  • Profiles replace coarse sandbox modes. A permission profile supersedes the older sandbox_mode + sandbox_workspace_write combination when you want one reusable, inheritable policy for filesystem and network behavior.
  • They do not compose with the old settings. Configure either default_permissions + [permissions] or the legacy sandbox settings -- not both. If sandbox_mode appears in any loaded config file, or you pass --sandbox, Codex uses the older settings instead of default_permissions. The exception is managed allowed_permission_profiles, which makes Codex use permission profiles.
  • Three built-ins: :read-only, :workspace, and :danger-full-access. Custom profiles live under [permissions.<name>]; default_permissions selects the active one.
  • On the same path, filesystem precedence is deny > write > read; a more specific path overrides a broader one. Narrow deny rules survive even when a broader path is writable -- the canonical example is **/*.env = deny inside :workspace_roots.
  • Network filtering needs the proxy. network.enabled = true grants access but does not start the network proxy; domain rules are enforced only when features.network_proxy = true (or an admin-managed [experimental_network] requirement) is active. With the proxy on and no allow entries, outbound is blocked; deny overrides allow; wildcard allow (*) is opt-in; localhost and private IPs require explicit allowlisting; Unix sockets (for example Docker) are an explicit escape hatch.
  • Fail-closed admin allowlists. allowed_permission_profiles in requirements.toml denies any omitted profile, including built-ins and future profiles. Requires Codex 0.138.0+; Codex 0.137.0 and earlier ignore the list.
  • Scope is local commands only. Profiles do not govern MCP, connectors, browser/computer-use, cloud, or the hosted web search tool. If the sandbox can't enforce a policy, Codex refuses rather than running unsandboxed.

What a Permission Profile Is

A permission profile is a named policy that combines filesystem rules and network rules into one reusable, inheritable unit. Per the official docs, profiles replace the older combination of sandbox_mode and sandbox_workspace_write when you want a single reusable definition of how Codex's local commands may touch the filesystem and the network.

The critical constraint up front: profiles do not compose with the legacy settings. You pick one system. Configure default_permissions plus [permissions.<name>] sections, or configure sandbox_mode/sandbox_workspace_write. As of September 29, 2026, the docs spell out what happens when both are present: if sandbox_mode appears in any loaded config file, you pass --sandbox, or the selected config profile sets sandbox_mode, Codex uses those older sandbox settings instead of default_permissions. Check for this first when a profile seems to be ignored. The single exception is managed allowed_permission_profiles, which "makes Codex use permission profiles"; OpenAI says to remove sandbox_mode and [sandbox_workspace_write] before deploying a managed allowlist.

Three built-in profiles ship out of the box:

Built-inBehavior
:read-onlyKeeps local command execution read-only
:workspaceAllows writes inside the active workspace roots and system temp directories
:danger-full-accessRemoves local sandbox restrictions -- use only when broad access is intentional

You select the active profile by setting default_permissions to a built-in or to a custom profile defined under [permissions.<name>].

Filesystem Rules: Specific Paths First, Then Deny

Filesystem entries use three access levels, and their precedence is the most important thing to internalize. read allows viewing files and listing directories. write allows read plus create, rename, and delete. deny blocks both. More specific paths override broader ones, and when two entries target the same path deny wins over write, which wins over read.

The practical payoff is that a narrow deny rule stays in force even when a broader path is readable or writable. The docs' headline example is keeping secrets out of reach while a workspace is otherwise writable:

[permissions.project-edit.filesystem]
glob_scan_max_depth = 3

[permissions.project-edit.filesystem.":workspace_roots"]
"**/*.env" = "deny"

glob_scan_max_depth (minimum 1) bounds how far Codex pre-expands an unbounded ** deny pattern before the sandbox starts on Linux, WSL, and native Windows. Path targets include symbolic roots like :root, :workspace_roots, :tmpdir, :slash_tmp, and :minimal, as well as absolute and home-relative paths. Note one portability caveat from the docs: deny globs are supported as deny-read rules, but read or write globs are less portable on Linux, WSL, and native Windows, so the docs recommend exact paths or subtree rules there.

Network Rules: Default-Block, Per-Domain

Network access is off until you turn it on (network.enabled = true), but that switch does not start the network proxy: with the proxy off, commands get direct, unrestricted network access and the profile's domain rules are not enforced. To enforce them, also set features.network_proxy = true in config.toml (or have an administrator enable it via [experimental_network] in requirements.toml). With the proxy active, the model is allow-listed: with no allow entries, external destinations are blocked. Domain patterns support exact hosts (example.com), subdomain wildcards (*.example.com), apex-plus-subdomain (**.example.com), and a global * that deny rules can narrow. As with the filesystem, deny overrides allow.

Two guards matter for anyone wiring profiles into real workflows:

  • With the proxy active, local and private targets are blocked by default. localhost and private IPs need explicit allowlisting; resolving allowlisted hostnames to local addresses additionally requires allow_local_binding = true. This stops a sandboxed command from quietly reaching your other local services. Windows MXC is the exception: it requires allow_local_binding = true, permits connections to services on host loopback, and removes the proxy's additional private-network destination checks.
  • Unix sockets are an explicit, sparing escape hatch. Local integrations like Docker can be permitted through network.unix_sockets, for example "/var/run/docker.sock" = "allow". The docs frame these as something to use sparingly, since a socket grant can be a wide door.
[features]
network_proxy = true

[permissions.project-edit.network]
enabled = true

[permissions.project-edit.network.domains]
"**.github.com" = "allow"

[permissions.project-edit.network.unix_sockets]
"/var/run/docker.sock" = "allow"

Inheritance and Enterprise Allowlists

Profiles use config-layer inheritance. Higher-precedence config layers can add to or replace entries under the same profile name, and a profile can extends a built-in (:read-only or :workspace) or another named profile. The docs recommend extending a built-in over starting from scratch so baseline protections carry forward: extending :workspace keeps the workspace root's .codex directory "read-only unless you explicitly override it." You cannot extend :danger-full-access, and Codex also rejects unknown parents and inheritance cycles.

The enterprise lever is allowed_permission_profiles in a managed requirements.toml. Admins define which profiles exist and restrict which ones users may select. The behavior is fail-closed: once allowed_permission_profiles is present, any profile not on the list is denied -- including omitted built-ins and any profiles added in future Codex versions. That is the security-correct default: a new profile nobody has vetted is unavailable until an admin adds it, rather than silently selectable. Permission-profile allowlists require Codex 0.138.0 or later; per the managed configuration docs, Codex 0.137.0 and earlier ignore allowed_permission_profiles and managed default_permissions, so OpenAI says to deploy them only after every managed client runs a supporting release. For a mixed-version rollout, the managed allowed_sandbox_modes requirement can stay as a temporary compatibility constraint. Set default_permissions explicitly in the managed file: if it is omitted, the local runtime defaults to :workspace only when both :workspace and :read-only are explicitly allowed.

Scope and Enforcement: Read the Caveats

Two caveats are load-bearing and easy to overclaim past.

Profiles govern local sandboxed command execution -- and only that. They control which files local commands can read or write and which outbound destinations are reachable through the proxy. A write-capable profile can create persistent changes. They do not govern connectors, MCP servers, browser and computer-use surfaces, Codex cloud environment settings, or approved escalations. Each of those has its own controls. The network proxy likewise filters only traffic from sandboxed local commands: the profile's domain allowlist does not apply to the hosted web search tool or to Codex's own service traffic. If you need to constrain what an MCP server can do, the permission profile is the wrong layer.

Enforcement is OS-backed, and Codex fails closed. The mechanism varies by platform -- Seatbelt on macOS, and bubblewrap plus seccomp (Landlock fallback) on Linux and WSL. On native Windows, the docs now list MXC (Microsoft Execution Containers) first: it uses process isolation for filesystem and network permissions and is the recommended sandbox on compatible devices where policy permits it. The elevated sandbox (dedicated lower-privilege sandbox users, filesystem permission boundaries, and firewall rules) is now the preferred legacy fallback, and unelevated is a legacy fallback with weaker network isolation. The desktop app prefers MXC automatically for consumer accounts when the device supports it; in enterprise deployments and the standalone CLI you turn the same behavior on with features.prefer_mxc = true. On macOS, when the platform sandbox cannot enforce the selected policy, Codex refuses to run the command rather than silently running it unsandboxed; on Linux and WSL the docs say unsupported split policies are refused, and on unelevated Windows unsupported policies are refused. That refusal is the feature. Codex is supported locally on macOS, Linux, WSL, and native Windows.

How a Reader Uses This

Pattern 1: Lock Secrets Out of an Otherwise-Writable Workspace

A developer who wants Codex to freely edit a repo but never read credentials:

  1. Define a custom profile that extends :workspace, so writes are scoped to workspace roots and system temp directories, and add "**/*.env" = "deny" under :workspace_roots, with glob_scan_max_depth set.
  2. Set default_permissions to that profile's name, and remove any sandbox_mode setting so Codex does not fall back to the older sandbox settings.
  3. Set features.network_proxy = true, then enable network only for the domains the build actually needs ("**.github.com" = "allow"), leaving the proxy's default block in place for everything else. Without the proxy, the domain rules are not enforced.

Pattern 2: Org-Wide Fail-Closed Baseline

A platform or security team standardizing Codex across an enterprise:

  1. Define the approved profiles centrally and list them in allowed_permission_profiles in managed requirements.toml.
  2. Omit :danger-full-access from the list so it is denied org-wide by default.
  3. Roll out on Codex 0.138.0+, and rely on fail-closed semantics: any future profile is unavailable until explicitly vetted and added.

When to Reach for a Profile vs Another Control

You want to constrain...Permission profileOther control
Local command filesystem accessYes--
Local command outbound networkYes--
What an MCP server can doNoMCP server controls
Signed-in browser actionsNoComputer Use / Chrome extension settings
Cloud environment behaviorNoCodex cloud environment settings

How to Change or Widen Permissions

As of September 29, 2026, OpenAI documents these routes for switching modes or granting full access:

GoalHowSource
Change the mode in the CLIEnter /permissionsPermissions
Full local access with a permission profiledefault_permissions = ":danger-full-access"Permission profiles
Full access with the older sandbox settings--sandbox danger-full-accessAgent approvals & security
No sandbox and no approvals--dangerously-bypass-approvals-and-sandbox (alias --yolo), labeled "not recommended"Agent approvals & security

On a managed machine, an allowed_permission_profiles list that omits :danger-full-access denies it.

Why This Matters

Coarse sandbox modes forced a blunt choice: read-only, workspace-write, or full access. Permission profiles turn that into a composable, OS-enforced policy you can tune per task and inherit across config layers -- and, for enterprises, lock down with a fail-closed allowlist. The **/*.env = deny example is the tell: the design goal is least privilege that survives broad grants, so a writable workspace doesn't implicitly mean a readable secrets file.

The honest framing is the one OpenAI uses: this is beta, it may change, and it covers local command execution rather than every Codex surface. Treat it as the strongest available lever for the local sandbox, not a single switch that secures the whole agent.

Corrections and Updates

  • October 9, 2026: This page named dedicated sandbox users on elevated Windows as the Windows enforcement path, with unelevated as the fallback; OpenAI's docs now put MXC process isolation first and call elevated and unelevated legacy fallbacks.
  • September 29, 2026: The Sources list named the author of the launch post as "Thomas Sottiaux". The post does not carry that name, and it is now cited by its account handle, @thsottiaux. The page also said managed allowed_permission_profiles "overrides older sandbox settings entirely"; OpenAI's docs say it makes Codex use permission profiles and that the older settings should be removed before deploying it.

Sources

Related coverage

Frequently Asked Questions

What are Codex permission profiles?

A permission profile is a named, reusable policy that combines filesystem rules (read/write/deny) with network rules to set least-privilege boundaries for the local commands Codex runs on your behalf. Profiles replace the older sandbox_mode plus sandbox_workspace_write combination when you want one reusable definition of filesystem and network behavior. The feature is in beta and under active development.

What built-in profiles ship with Codex?

Three: ':read-only' blocks local modifications, ':workspace' allows writes inside the active workspace roots and system temp directories, and ':danger-full-access' removes local sandbox restrictions. You set 'default_permissions' to one of these or to a custom profile defined under [permissions.<name>]. You can extend ':read-only', ':workspace', or another named profile, but you cannot extend ':danger-full-access'.

Do permission profiles compose with the old sandbox settings?

No. Profiles do not compose with sandbox_mode and sandbox_workspace_write. Configure either default_permissions plus [permissions] or the older sandbox settings, not both. As of September 29, 2026, the docs say that if sandbox_mode appears in any loaded config file, or you pass --sandbox, Codex uses the older sandbox settings instead of default_permissions. The exception is managed allowed_permission_profiles, which makes Codex use permission profiles; OpenAI says to remove sandbox_mode and [sandbox_workspace_write] before deploying that allowlist.

How do the fail-closed admin allowlists work?

Enterprise admins define profiles and restrict which ones users may select via managed allowed_permission_profiles in requirements.toml. Once that list is present, any omitted profile is denied -- including omitted built-ins and any future profiles. That is the fail-closed behavior: new or unlisted profiles default to unavailable rather than allowed. Permission-profile allowlists require Codex 0.138.0 or later; Codex 0.137.0 and earlier ignore allowed_permission_profiles and managed default_permissions.

What do permission profiles not control?

Profiles govern local sandboxed command execution only. They do not govern connectors, MCP servers, browser and computer-use surfaces, Codex cloud environment settings, or approved escalations -- those use their own controls. The profile's domain allowlist also does not apply to the hosted web search tool. On macOS, if the platform sandbox cannot enforce a selected policy, Codex refuses to run the command rather than silently running it unsandboxed; on Linux and WSL unsupported split policies are refused, and on unelevated Windows unsupported policies are refused.

How do I give Codex full permissions or change the permission mode?

In the Codex CLI, enter /permissions to change the permission mode. For a permission profile, set default_permissions to ':danger-full-access', which removes local sandbox restrictions. With the older settings, OpenAI documents --sandbox danger-full-access, with the advice to use caution, and the --dangerously-bypass-approvals-and-sandbox flag (alias --yolo), which it labels not recommended. A managed allowed_permission_profiles list that omits ':danger-full-access' denies it.

Get the weekly AI Catchup

Tools, practices, and what matters, in your inbox every week.