Skip to content

Codex enterprise governance rests on two controls. ChatGPT workspace settings decide who may use Codex at all, and an admin-owned requirements.toml constrains sign-in, approval policies, sandbox modes, web search, MCP servers, plugin marketplaces and command rules on every machine. Users cannot override those constraints, and codex doctor names the file that enforced each one.

This page is for the tech lead or platform engineer who owns Codex for more than one team, and the CTO who signs off on the rollout. Your security review asks for proof that no laptop runs Codex with full disk access, an unapproved MCP server or a personal account. A wiki page that says “we don’t allow that” is not proof. You need a file every machine obeys and a check that fails when it stops being obeyed.

Scope: this page covers Codex only. For one policy rendered into Claude Code, Codex, Cursor and GitHub Copilot, see enforcing one policy across every coding agent. Shared team defaults (.codex/config.toml, AGENTS.md, rules files, skills) live in Codex team workflows. Read Codex configuration first if config.toml layering is new to you.

What you’ll walk away with from Codex governance

Section titled “What you’ll walk away with from Codex governance”
  • A map of which Codex settings users can override and which they cannot.
  • A requirements.toml baseline that loads cleanly in codex-cli 0.157.1, with the traps in each key.
  • A six-step rollout order, from the policy table to fleet distribution.
  • A CI canary that proves the policy is enforced, without a person inspecting a single laptop.
  • An audit-evidence map: which question each log or report answers, and how far each one is verified.
  • Three copy-paste prompts that draft the policy, build the canary, and report drift.

Which Codex settings can a developer override?

Section titled “Which Codex settings can a developer override?”

Codex separates defaults from constraints. OpenAI’s own guidance, in the documentation skill bundled with Codex, reads: “Keep config.toml defaults, requirements.toml constraints, and managed or administrator policy separate.” The table shows where each layer lives and who wins.

LayerWhere it livesOwnerCan a developer override it?Use it for
ChatGPT workspace settingsThe workspace admin consoleWorkspace adminsNoWho may use Codex locally and in the cloud; cloud environments; connectors
requirements.toml/etc/codex/requirements.toml on Linux, macOS MDM managed preferences, or an enterprise-managed layer delivered from the workspaceSecurity or platform teamNo. A disallowed value falls back to the first allowed value, with a startup warningFloors: sign-in, approvals, sandbox, web search, MCP, marketplaces, command rules
System config.toml/etc/codex/config.tomlPlatform teamYesFleet defaults such as the [otel] exporter
User and project config.toml~/.codex/config.toml, .codex/config.toml in a trusted repositoryDevelopers and teamsYesPreferences and team conventions

Two gaps shape the rest of this page. In codex-cli 0.157.1, requirements.toml has no model-name allowlist and no [otel] key (checked against config_requirements.rs at tag rust-v0.157.1 on 2026-09-26). So telemetry is a default a developer can switch off, and model choice is governed by the workspace or a gateway, as described in where the model runs. You can still force the route: model_provider in requirements.toml pins the provider (for example, your gateway) and overrides user and session config, and model_catalog_json pins the model catalog, checked in config_requirements.rs at rust-v0.157.1. [models.new_thread] in requirements.toml sets the default model for new threads; it does not restrict the picker.

The order matters. Access control comes before the policy file, because a policy file cannot constrain someone who signs in with an API key outside your workspace. Verification comes before distribution, because a broken requirements.toml stops Codex on every machine that receives it.

  1. Write the policy as a table before writing any TOML. One row per control: the risk it addresses, the risk class from governance and autonomy, the enforcement point, and the owner. Controls with no enforcement point are trust-based, and the table should say so. This table is what the CTO signs off, and it is the input to the first prompt below.

    ControlRisk classEnforced byOwner
    Only workspace accounts may sign inHighallowed_login_methods, allowed_chatgpt_workspacesSecurity
    No full disk access on laptopsHighallowed_sandbox_modesPlatform
    No unattended runs without approvalsMediumallowed_approval_policiesPlatform
    Only reviewed MCP serversHigh[mcp_servers.*.identity]Security
    Plugins only from the internal marketplaceMedium[marketplaces]Platform
    No force-push or package publish by the agentHigh[rules] prefix_rulesPlatform
    Session telemetry reaches the collectorMediumSystem config.toml (a default, not enforced)Platform
  2. Restrict who can use Codex in the workspace. In the ChatGPT workspace admin settings, enable Codex locally and in the cloud only for the groups in your pilot, and widen access group by group. OpenAI’s admin pages could not be read for this revision (2026-09-26), so this page does not name the toggles. Check the current labels in OpenAI’s enterprise admin guide.

  3. Pin sign-in to your workspace. Set allowed_login_methods = ["chatgpt"] and allowed_chatgpt_workspaces to your workspace ID. Without these two keys, a developer can sign in with a personal API key, and that session runs outside your workspace settings.

  4. Write requirements.toml from the table. Start from the baseline in the next section, keep it in a policy repository, and put a version and an owner in the first comment line.

  5. Prove the file in CI before it ships. Run the canary described in how to prove the policy is enforced on every change to the policy repository. Security approves the pull request; the canary decides whether it can merge.

  6. Distribute the file and watch for drift. Push it with your device management tool: a file at /etc/codex/requirements.toml on Linux, managed preferences on macOS, or the enterprise-managed layer from the workspace where your plan provides it. When several sources set requirements, Codex composes them, and every warning or error names the source that set the value. A weekly drift report (the third prompt) catches machines where the file is missing or outdated.

A requirements.toml baseline that loads in Codex 0.157.1

Section titled “A requirements.toml baseline that loads in Codex 0.157.1”

This file loads without errors in codex-cli 0.157.1. Replace CHATGPT_WORKSPACE_ID with your workspace ID and acme with your plugin marketplace.

# /etc/codex/requirements.toml (agent-policy v1.0, owner: platform-security)
allowed_login_methods = ["chatgpt"]
allowed_chatgpt_workspaces = ["CHATGPT_WORKSPACE_ID"]
# The first entry is the fallback when a user's config asks for anything else.
allowed_approval_policies = ["on-request", "untrusted"]
allowed_sandbox_modes = ["read-only", "workspace-write"]
allowed_web_search_modes = ["cached"]
[mcp_servers.github.identity]
url = "https://api.githubcopilot.com/mcp/"
[mcp_servers.linear.identity]
url = "https://mcp.linear.app/mcp"
[marketplaces]
restrict_to_allowed_sources = true
[marketplaces.allowed_sources.acme]
source = "git"
url = "https://github.com/acme/agent-plugins.git"
[rules]
prefix_rules = [
{ pattern = [{ token = "git" }, { token = "push" }, { token = "--force" }], decision = "forbidden", justification = "Force-push is for humans" },
{ pattern = [{ token = "npm" }, { token = "publish" }], decision = "forbidden", justification = "Releases go through CI" },
{ pattern = [{ token = "terraform" }, { token = "apply" }], decision = "prompt", justification = "Infrastructure changes need a named approver" },
]

What each key does in practice, checked against the 0.157.1 binary and source on 2026-09-26:

  • List order is policy. When a user’s config asks for a disallowed value, Codex falls back to the first entry in the allowed list. With ["on-request", "untrusted"], approval_policy = "never" becomes on-request; reverse the list and it becomes untrusted.
  • allowed_sandbox_modes must include read-only, or Codex rejects the requirement. The constraint also applies to permission profiles, not only to the legacy --sandbox flag. A request for danger-full-access falls back to read-only.
  • Web search disabled is always allowed. allowed_web_search_modes = ["cached"] permits cached and disabled and blocks live.
  • MCP servers match on name and identity. A server that is not listed shows disabled: requirements in codex mcp list. The approved name matters as much as the URL, so publish the exact names developers must use.
  • Rules accept only prompt or forbidden. Codex merges requirement rules with user and project rules and keeps the most restrictive result, so decision = "allow" is refused.
  • A refused file stops Codex. One allow rule, or any other invalid entry, turns into Error loading config.toml: invalid rules in requirements (set by /etc/codex/requirements.toml), and Codex does not start. This is why step 5 comes before step 6.

Two stricter keys belong only in the policy you ship to machines (or device groups) that work on regulated data. allow_managed_hooks_only = true ignores user, project and session hooks, which also switches off the hooks your teams use as quality gates. enforce_residency = "us" pins data residency, and us is the only value 0.157.1 accepts. Model defaults stay out of this file unless you have a reason: Codex ships with GPT-6.1 Sol as its default (since CLI 0.159.1), and the models hub is where model choices and prices are compared.

How do you prove the policy is enforced without inspecting laptops?

Section titled “How do you prove the policy is enforced without inspecting laptops?”

A policy is enforced when a hostile configuration fails to get past it. Build that configuration once, run it against every change to the policy, and let CI give the verdict. codex doctor needs no credentials for these checks: it reads the config layers, applies the requirements and prints every startup warning with its source.

A developer config that tries every forbidden setting:

policy/codex/tests/hostile-config.toml
approval_policy = "never"
sandbox_mode = "danger-full-access"
web_search = "live"
[mcp_servers.unapproved-server]
command = "npx"
args = ["-y", "some-mcp"]

The check, which passes only when the approval, sandbox, web-search and MCP constraints hold, and fails if the file does not load:

#!/usr/bin/env bash
# policy/codex/check.sh — runs in CI; needs no Codex credentials
set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)"
export CODEX_HOME="$(mktemp -d)"
cp "$here/tests/hostile-config.toml" "$CODEX_HOME/config.toml"
report="$(codex doctor --no-color 2>&1 || true)"
if grep -q "config could not be loaded" <<<"$report"; then
echo "FAIL: requirements.toml does not load"; exit 1
fi
for key in approval_policy sandbox_mode web_search_mode; do
grep -q "invalid value for \`$key\`" <<<"$report" \
|| { echo "FAIL: $key is not constrained"; exit 1; }
done
codex mcp list 2>/dev/null | grep -Eq '^unapproved-server .*disabled: requirements' \
|| { echo "FAIL: unapproved MCP server is not disabled"; exit 1; }
echo "PASS: policy enforced"

The login, marketplace and rules keys are proven to load but are not exercised by this canary. List them in GAPS.md, or test them by hand at pilot: sign in with a personal account, add a plugin from an unlisted marketplace, and run a forbidden command.

Run it on every pull request to the policy repository. The job needs no secrets, checks out with read-only permissions, and pins the Codex version so a CLI upgrade is its own reviewed change:

.github/workflows/codex-policy.yml
name: codex-policy
on:
pull_request:
paths: ["policy/codex/**"]
permissions:
contents: read
jobs:
canary:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<full-commit-sha> # v7
with:
persist-credentials: false
- run: npm install -g @openai/codex@0.157.1
- run: sudo install -D -m 0644 policy/codex/requirements.toml /etc/codex/requirements.toml
- run: bash policy/codex/check.sh

In the policy repository, pin every action by its full commit SHA, not by a tag, because a tag such as v7 can be moved to different code. <full-commit-sha> above is a placeholder: replace it with the 40-character SHA of the actions/checkout release you reviewed, and keep the tag as a comment (# v7) so a reader can see which release it is.

The same script fails when the file is missing, so it doubles as a smoke test on a freshly imaged laptop. On a developer machine, /debug-config in the Codex TUI shows the config layer stack and the requirements in force, and codex doctor --json produces a redacted report whose config.load check lists the startup warnings. Your device management tool can collect that report, plus a sha256 of /etc/codex/requirements.toml, for the drift check.

Sign-off follows the table in step 1: security approves the policy pull request, the canary decides the merge, platform owns distribution, and the tech lead reviews the weekly drift report. Nobody reads a laptop’s config by hand.

The canary only decides the merge if a pull request cannot rewrite it. On the pull_request trigger GitHub runs the workflow, check.sh and the hostile config from the pull request itself, so a contributor can weaken all three in the same change. Put /.github/, policy/codex/check.sh and policy/codex/tests/ under CODEOWNERS owned by security, and require that review through branch protection or a ruleset; otherwise the check is advisory.

Auditors ask four questions. Each has a different source, and the sources are not equally verified.

QuestionSourceVerification status (2026-09-26)
What policy was in force on this machine?The versioned requirements.toml in git, plus codex doctor --json from the machineVerified in codex-cli 0.157.1
What did the sessions do?OpenTelemetry logs, traces and metrics from the [otel] table, sent to your collector. log_user_prompt defaults to falseVerified in the Codex source; setup in agent telemetry
Who used Codex, where, and how much?The Codex analytics dashboard and Analytics API for Enterprise workspacesSecondary: help.openai.com, seen in search extracts on 2026-09-26; endpoint paths not verified
What were the prompts and responses, for eDiscovery or DLP?The Compliance APISecondary: help.openai.com, seen in search extracts on 2026-09-26; fields and endpoints not verified

Treat the secondary rows as leads to confirm with your OpenAI account team before an auditor relies on them. The OpenTelemetry route is the one you control end to end, with the gap already noted: it is a default, not a constraint, so compare collector coverage against workspace seat counts every month. For the organization-wide audit pipeline and retention, see observing the agents. For per-tool data retention and zero data retention terms, see data privacy and enterprise policies.

Run these in Codex, in the policy repository.

When Codex governance breaks, and how to recover

Section titled “When Codex governance breaks, and how to recover”
  • Codex stops starting on every machine after a policy push. The file has an invalid entry, most often decision = "allow" in [rules] or an unknown key inside a strict table such as [marketplaces]. Roll back through your device management tool, then add the canary so the next broken file fails in CI instead.
  • A constraint silently does nothing. A misspelled top-level key, such as allowed_sandbox_mode without the final s, is ignored in 0.157.1: the file loads and the constraint is simply absent. Only a test that tries the forbidden value catches this, which is what the canary does for the keys it exercises.
  • A developer reports “I set never and it still asks for approval.” The policy is working. The startup warning shows the value Codex fell back to and the file that forced it. Publish the policy table where developers can read it, so the fallback is not a surprise.
  • An approved MCP server shows disabled: requirements. The developer registered it under a different name from the one in requirements.toml. Publish the exact names, or ship the server entries in the system config.toml so the names match by default.
  • Web search or full access still works on some laptops. Those machines do not have the current file. Check the drift report for a missing or mismatched policy hash, and fix device management coverage before adding more rules.
  • Telemetry goes quiet for one user. [otel] is a default, and a user config can override it; 0.157.1 cannot enforce it. Compare collector coverage with workspace seats monthly, and treat a gap as a conversation, not a breach.
  • Your regulated-data policy broke the team’s hooks. allow_managed_hooks_only = true ignores every project hook, including test and lint gates. Move the gates you need into managed hooks, or ship the stricter file only to the device group that handles regulated repositories.
  • A cloud task did something the local policy forbids. The local file does not reach Codex cloud. Tighten workspace access and the environment’s settings, as described in Codex cloud environments.