AI Catchup

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

By 7 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. The only exception is enterprise managed allowed_permission_profiles, which overrides older sandbox settings.
  • Three built-ins: :read-only, :workspace, and :danger-full-access. Custom profiles live under [permissions.<name>]; default_permissions selects the active one.
  • Filesystem precedence is deny > write > read. Narrow deny rules survive even when a broader path is writable -- the canonical example is **/*.env = deny inside :workspace_roots.
  • Network is default-block. With 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+.
  • Scope is local commands only. Profiles do not govern MCP, app connectors, browser/computer-use, or cloud. 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 -- mixing the two is not supported. The single exception is enterprise managed allowed_permission_profiles, which overrides older sandbox settings entirely.

Three built-in profiles ship out of the box:

Built-inBehavior
:read-onlyBlocks local command modifications
:workspacePermits writes within active workspace roots and the system temp directory
: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: Deny Wins

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 at equal specificity 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 ** pattern, so a recursive deny glob doesn't trigger an unbounded filesystem scan. Path targets include symbolic roots like :workspace_roots, :tmpdir, and :minimal, as well as absolute and home-relative paths. Note one portability caveat from the docs: deny-read globs are portable, but read/write globs may require exact paths or subtree rules depending on platform.

Network Rules: Default-Block, Per-Domain

Network access is off until you turn it on (network.enabled = true), and even then the model is allow-listed: with no allow entries, outbound connections 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:

  • 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.
  • 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.
[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. You cannot extend :danger-full-access -- there is no least-privilege story to build on top of "no restrictions."

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. Managed allowed_permission_profiles requires Codex 0.138.0 or later and overrides older sandbox settings entirely.

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, whether those writes persist, and which outbound destinations are reachable through the proxy. They do not govern app connectors, MCP servers, browser and computer-use surfaces, or Codex cloud environment settings. Each of those has its own controls. 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, bubblewrap plus seccomp (Landlock fallback) on Linux and WSL, sandbox users with filesystem ACLs and firewall rules on elevated Windows, a weaker fallback on unelevated Windows. The constant across all of them: when the platform sandbox cannot enforce the selected policy, Codex refuses to run the command rather than silently running it unsandboxed. 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. Set default_permissions = ":workspace" so writes are scoped to workspace roots.
  2. Add a custom profile that extends :workspace and adds "**/*.env" = "deny" under :workspace_roots, with glob_scan_max_depth set.
  3. Enable network only for the domains the build actually needs ("**.github.com" = "allow"), leaving the default block in place for everything else.

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
Non-interactive automation identityNoProgrammatic access tokens

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.

FAQ

See the structured FAQ in the schema header for question-level details: what permission profiles are, the three built-ins, whether they compose with old sandbox settings, how fail-closed admin allowlists work, and what profiles do not control.

Sources

Keep building the workspace playbook

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' permits writes within active workspace roots and the system temp directory, 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. The one exception is enterprise managed allowed_permission_profiles, which overrides older sandbox settings entirely.

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. Managed allowed_permission_profiles requires Codex 0.138.0 or later.

What do permission profiles not control?

Profiles govern local sandboxed command execution only. They do not govern app connectors, MCP servers, browser and computer-use surfaces, or Codex cloud environment settings -- those use their own controls. If the platform sandbox cannot enforce a selected policy, Codex refuses to run the command rather than silently running it unsandboxed.

Get the weekly AI Catchup

Tools, practices, and what matters -- in your inbox every Monday.