Skip to content
Agentic Control Plane

Codex CLI hook governance: what works today (and what doesn't)

David Crowe · 8 min read
codex openai cli hooks governance reference

OpenAI’s Codex CLI has a PreToolUse / PostToolUse hook surface modeled on Claude Code’s. Same architectural shape: scripts run before and after tool dispatch, the script’s stdout decides the outcome, the agent never sees what’s intercepted. Familiar to anyone who’s wired up Claude Code hooks.

It also has one limitation worth being upfront about: as of writing, hooks reliably fire for shell (Bash) tool calls but not for apply_patch file edits or most MCP tool calls — tracked upstream at openai/codex#16732. For agents that mostly run shell commands, hook-based governance is straightforward. For agents that lean on apply_patch or rich MCP toolsets, hook coverage has real holes today. This post walks through what works, what doesn’t, and what the working integration looks like in practice.

Where hooks live

Codex CLI reads hook configuration from ~/.codex/hooks.json. Same per-user shape as Claude Code’s ~/.claude/settings.json, different filename. The contents register a script for each event:

{
  "hooks": {
    "PreToolUse":  [{ "command": "node /Users/me/.acp/govern.mjs --pre" }],
    "PostToolUse": [{ "command": "node /Users/me/.acp/govern.mjs --post" }]
  }
}

Each entry’s command is the executable Codex spawns when the event fires. Multiple entries per event are allowed and run in declaration order.

The hook script can be any executable that reads JSON from stdin and writes JSON to stdout — Node, Python, Bash with jq, whatever. Codex doesn’t care about the runtime as long as the contract holds.

For full payload schema details (field names, types, supported decision values), the canonical reference is OpenAI’s Codex agent-approvals & security docs. Don’t trust third-party reproductions; the upstream docs are accurate and current.

What hooks reliably govern today

For shell (Bash) tool calls, Codex hooks work the way you’d expect — every shell command the agent dispatches passes through PreToolUse before execution. Your hook can:

  • Approve the call (it runs as-is)
  • Block it (the agent sees tool_error: <reason> and adapts)
  • Modify the input (e.g., resolve relative paths, normalize args, redact secrets from the command line)
  • Ask for human confirmation (degrades to block in --full-auto mode where there’s no human at the keyboard)

PostToolUse fires after the call returns, with the stdout/stderr/exit-code captured for audit. Output redaction at this stage keeps secret-shaped output from reaching the model’s context.

For an agent that’s primarily shell-mediated — running tests, executing builds, calling curl to APIs, managing files via cat/sed/mv — this is sufficient governance.

What hooks don’t reliably govern today

Two known gaps from openai/codex#16732:

  1. apply_patch edits. Codex’s native diff-application tool. PreToolUse hooks don’t reliably fire for these calls today, so a hook-only governance layer doesn’t see structured patches the agent applies.
  2. Most MCP tool calls. Calls dispatched through Codex’s MCP integration also have intermittent hook coverage today.

For agents that rely heavily on apply_patch or MCP toolsets, this is a real limitation. Two practical workarounds while the upstream issue is open:

  • Express edits as shell. Instead of relying on apply_patch, instruct the agent to use sed, cat > file <<EOF, or git apply via shell. These commands route through the shell tool, which PreToolUse does cover. The Framework Scout reference agent in our examples repo does exactly this.
  • Govern at the MCP server boundary. For MCP-mediated calls, run the MCP server itself behind a control plane (e.g., point Codex at mcp.agenticcontrolplane.com/mcp for hosted-MCP governance). The interception happens server-side rather than client-side, so it’s not affected by the Codex hook gap.

These workarounds aren’t permanent; the upstream issue may resolve, at which point hook coverage will expand. Until then, they’re how production deployments handle the gap.

The --full-auto flag — strictly preferable to --dangerously-bypass-approvals-and-sandbox

For unattended Codex deployments (CI agents, scheduled jobs, headless coding agents), the right flag is codex exec --full-auto, not --dangerously-bypass-approvals-and-sandbox. OpenAI’s own documentation labels the latter “Elevated Risk / not recommended.”

--full-auto (= --sandbox workspace-write + non-interactive execution):

  • Suppresses the Y/N approval prompt — the agent runs without per-step confirmation
  • Keeps hooks firing — same PreToolUse / PostToolUse invocations as interactive mode
  • Sandboxes filesystem writes to the workspace
  • Blocks network access by default

For agents that need outbound network (calling APIs, hitting external services), add -c sandbox_workspace_write.network_access=true to grant network while keeping filesystem sandboxing intact. From the Codex CLI scout:

codex exec \
  --full-auto \
  -c 'sandbox_workspace_write.network_access=true' \
  "$(cat scout.prompt.md)"

This is the governance differentiator vs. Claude Code worth knowing: where Claude Code’s --dangerously-skip-permissions disables hooks entirely, Codex’s --full-auto keeps them running. Unattended agent deployments retain audit + policy enforcement; only the human-in-loop confirmation prompt is suppressed.

Installing the ACP Codex plugin (one command)

The fastest path to a working hook config is the ACP installer for Codex. From the Codex CLI starter:

curl -sS https://cloud.agenticcontrolplane.com/install/codex | bash

The installer:

  1. Detects your Codex installation
  2. Writes ~/.acp/govern.mjs (the same hook script Claude Code uses, with ACP_CLIENT=codex set)
  3. Registers it under ~/.codex/hooks.json for both PreToolUse and PostToolUse
  4. Walks you through workspace provisioning + API key save

Restart Codex after install. Every shell tool call now flows through ACP’s /govern/tool-use endpoint before dispatch, with audit rows landing under client=codex in your dashboard.

For environments where you’d rather build your own integration: the protocol is documented at /integrations/codex and the running code is in the starter folder linked above.

Tier-aware policy

The hook payload includes Codex’s current approval mode (interactive, auto, full-auto). Map this to ACP’s tier model for tier-aware policy:

Codex mode ACP tier Typical policy
interactive interactive Permissive — human is watching
auto subagent Restrictive — no human, session-bound
full-auto background Most restrictive — no human, no session anchor

A typical policy block for destructive shell verbs:

{
  "tools": {
    "Bash.curl":    { "background": "deny", "subagent": "ask",  "interactive": "allow" },
    "Bash.rm":      { "background": "deny", "subagent": "ask",  "interactive": "allow" },
    "Bash.kubectl": { "background": "deny", "subagent": "deny", "interactive": "ask"   },
    "Bash.aws":     { "background": "deny", "subagent": "deny", "interactive": "ask"   }
  }
}

Sub-command classification (Bash.curl vs Bash.rm) happens in the hook by parsing the leading token of the shell command. ACP’s ~/.acp/govern.mjs does this automatically.

Known limitation worth flagging

The ACP Codex plugin currently sends agent_tier but not agent_name in its hook payload — events land in the dashboard under client=codex without an agent-name breakdown. Same gap exists in the Claude Code plugin. Functional impact is limited (per-tool policy still works, audit still records), but per-agent attribution is sparser than for SDK-based integrations like the Anthropic Agent SDK or the OpenAI Agents SDK starter.

Batch-fix candidate; not a blocker for governance.

Comparing to Claude Code hooks

Both clients implement the hook pattern. Where they differ:

Dimension Claude Code Codex CLI
Hook config path ~/.claude/settings.json ~/.codex/hooks.json
Hook events PreToolUse, PostToolUse PreToolUse, PostToolUse
Coverage of native tools All tools (Bash, Edit, Read, Write, MCP) shell reliably; apply_patch and most MCP have gaps today (issue #16732)
Unattended-mode flag --dangerously-skip-permissions (disables hooks) --full-auto (keeps hooks running) — strictly preferable for governed deployments
Hook timeout ~4s default ~30s (giving hooks more headroom for remote policy lookups)

Both hook surfaces are usable for governance. Codex’s hook-keeping behavior in --full-auto is a meaningful win for unattended deployments. The apply_patch / MCP gap is a meaningful constraint for agents that rely heavily on those tools.

Practical takeaways

  1. Use --full-auto for unattended Codex agents, not --dangerously-bypass-approvals-and-sandbox. Hooks keep running, governance stays enforced, network can be selectively granted.
  2. Pre-fer shell-mediated tool patterns for agents that need governance coverage today. Express file edits via shell when possible; the hook reliably catches Bash-routed calls.
  3. Govern MCP servers at the server boundary rather than the Codex client boundary, given the current hook gap on MCP dispatch.
  4. Watch openai/codex#16732 for upstream resolution. The gap isn’t permanent; it’s a current limitation.

Where to read more

Get the next post
Agentic governance, AgentGovBench updates, the occasional incident post-mortem. One email per post. No marketing fluff.
Share: Twitter LinkedIn
Related posts

← back to blog