Codex
Installing the Agent Firewall for OpenAI Codex, hook trust, and what its hooks cannot see
The Codex adapter translates OpenAI Codex's hook events (the CLI and the IDE extension, local runs) to the firewall's normalized actions and its answers back. The policy is the same engine Claude Code uses.
Tested with Codex 0.162.1. Every event shape was captured live: Codex was installed into a temporary npm prefix and run with a throwaway HOME and CODEX_HOME against a local mock model and a local MCP server, with a capture hook saving each event. No real account or key was used.
Install
launchsafe-firewall install --agent codex # $CODEX_HOME/hooks.json (default ~/.codex/hooks.json)
launchsafe-firewall install --agent codex --scope project # <project>/.codex/hooks.json
launchsafe-firewall doctorinstall puts one hook group per event first in each list (matcher * on tool events), keeps your own hooks, saves the original file under ~/.launchsafe-firewall/backups/, records the install and is idempotent. uninstall --agent codex removes exactly the firewall's entries; --restore-backup puts the file back as it was, after saving the current file as hooks.json.before-restore.TIMESTAMP.
Only the tool events get a matcher. Codex 0.162.1 skips a UserPromptSubmit group that has any matcher, silently (captured). Installs from before the fix wrote "*", so on Codex the firewall never saw a prompt: no task scope and no destinations you named. doctor fails on such a file ("Codex prompt hook"); run install --agent codex again.
Hook trust
Codex runs a hook from hooks.json only once you have trusted it, and skips an untrusted hook silently (no warning; the tool runs). A trusted hook whose command, timeout or matcher changes is skipped the same way. Trust is recorded in $CODEX_HOME/config.toml under hooks.state.
The firewall reproduces Codex's hash exactly, so install, which you run yourself in a terminal, records their trust. uninstall and approver init|rotate update it too, and doctor fails when a recorded hash no longer matches a hook as written. The agent itself may neither grant nor revoke trust: a change to hooks.state is denied (FW-TAMPER). You can also review the hooks in Codex with /hooks.
Managed deployment
launchsafe-firewall install --managed --agent codex --lock-other-hooks --print | sudo tee /etc/codex/requirements.tomlrequirements.toml carries the hooks and allow_managed_hooks_only = true, so no user or project hook runs alongside them and none can be removed by the user. Codex's documentation says managed hooks are trusted by policy; this could not be tested without root. doctor reports whether /etc/codex/requirements.toml registers the firewall.
What the adapter maps
| Codex event | Firewall |
|---|---|
PreToolUse | The decision |
PostToolUse | Taint, private data and fingerprints from the tool response |
UserPromptSubmit | The task scope and the destinations you named (not a subagent's prompt, which the parent model writes) |
SessionStart | The session-start agent-configuration scan |
SubagentStart and SubagentStop | Linkage when a parent session id is reported (0.162.1 keeps the parent's session_id, so nothing is needed) |
PermissionRequest, Stop, SessionEnd, PreCompact, PostCompact, Interrupt | Ignored (the firewall decides in PreToolUse) |
| Codex tool | Judged as |
|---|---|
Bash (the model's exec_command) | A shell command. An argv array from older versions is joined with shell quoting |
apply_patch | Parsed per file: an added file is a write, an updated file an edit of its hunks (settings, auto-run and auto-approval files are checked against their current content), a delete a removal, a move a move. The whole patch gets one verdict and one approval, and a held patch shows every file in queue show. A patch that does not parse is held (FW-UNCLASSIFIABLE). Run through the shell, it is judged the same way |
mcp__<server>__<tool> | As is; the server's source comes from [mcp_servers.<name>] in the user's or the project's config.toml |
spawn_agent, wait_agent, multi_agent_*, update_plan | Safe (the agent talking to itself) |
view_image | A read |
| Anything else | An unknown tool: held for a person (FW-UNKNOWN-TOOL), its arguments judged, and its result treated as outside content |
Codex matches an apply_patch chunk's context again after trimming whitespace and folding punctuation. That was not captured, so the firewall judges the result at every place the chunk could match (the most severe wins), and as the worst case when it matches nowhere.
Output protocol (observed)
PreToolUseblocks with an explicitpermissionDecision: "deny"and a reason (the model reads "Command blocked by PreToolUse hook: ..."), or exit 2 with text on stderr.permissionDecision: "ask"is unsupported: Codex logs a failure and runs the tool. The firewall never sends it. An action that needs a person is held in the queue and the agent gets thelaunchsafe-firewall queue approve <id>command.- Exit 2 with empty stderr, exit 1, a crash, non-JSON output and a timeout all let the tool run. The wrapper prints a line before
exit 2, so a failure of the firewall or of Node blocks. A hook that times out (30 s) still fails open. PostToolUsecontext reaches the model as a separate message. Codex cannot replace a built-in tool's output, so secrets in output are not redacted.
Unattended runs
codex exec forces the approval policy to never, and every event then says permission_mode: "bypassPermissions". The adapter treats that, codex exec or -a never in the process ancestry, and approval_policy = "never" in config.toml as unattended: held actions wait in the queue, and the message says nobody was there.
Until a prompt arrives (an install with the old matcher, or a hook that failed), the session has no task scope, so today's rules apply, and no named destinations, so a fetch after outside content is held rather than allowed.
What its hooks cannot do
- Codex's hosted web search and
tool_searchnever reach a hook. - The hook gets the shell command only, not the model's
workdir: relative paths are judged against the session's directory. - There is no
ConfigChangeevent: a change tohooks.jsonorconfig.tomlis judged when the agent makes it through a tool, not when something else edits the file. - A trusted project's
.codex/config.tomlwith[features] hooks = falseswitches off every hook, yours included (captured).doctorfails on it, and the agent writing it is denied (FW-TAMPER). - Codex cloud tasks do not run local command hooks.
- A hook timeout lets the tool run.