How do you secure OpenAI Codex?
To secure OpenAI Codex, install hooks on PreToolUse, PermissionRequest, and UserPromptSubmit that send each tool call and prompt to a policy service before Codex acts. A hook is a command that Codex runs at a fixed point in its work, and it can refuse the action. Deliver the hooks through requirements.toml, macOS mobile device management (MDM), or ChatGPT cloud-managed requirements, and set allow_managed_hooks_only = true so that developers can't disable or replace them. If the wrapper script can't get an answer from the policy service, it returns the Codex denial for that event and exits with code 2, so the action is denied rather than allowed.
Codex runs shell commands through exec_command, edits files with apply_patch, calls MCP servers (Model Context Protocol servers that give the agent extra tools), and starts subagents, all with the developer's access. Instructions in AGENTS.md shape what the model tries to do, but they don't refuse a tool call. A hook runs before the tool and can deny it. For more information about that distinction, see AGENTS.md is not a security control.
In practice, you do this with Arcjet coding agent hooks. A wrapper script sends each Codex tool call and prompt to Arcjet, and Arcjet decides before the tool runs, from the hooks that Codex already fires, with no SDK and no code change. The same policy runs on Claude Code, GitHub Copilot, and Cursor, so you don't maintain per-vendor rules, and every session and decision is recorded in the Arcjet Console.
This guide covers how the wrapper reaches Arcjet, how to deploy it so that it stays on, what it enforces, and what to pair it with. For controls across all four agents, see coding agent security.
How do Codex hooks call a policy service?
Codex runs hooks as commands and has no built-in HTTP hook type. Codex writes the hook payload to the command's standard input and reads the result from standard output.
To reach a remote policy service, install a small wrapper script next to the hook configuration. The Arcjet script reads the payload from stdin, posts it to https://decide.arcjet.com/v1/agent-hooks/codex with the event as a query parameter, and prints the response. It authenticates with the ARCJET_KEY environment variable. The machine that runs Codex needs curl. On Windows, a sibling arcjet-hook.ps1 does the same job, and the hook entry points command_windows at it.
On any transport failure, non-2xx response, or response that isn't JSON, the wrapper prints the Codex denial shape for that event and exits with code 2. Exit 2 is what Codex treats as a deny on PreToolUse and UserPromptSubmit. That makes the wrapper the fail-closed boundary: if the policy service can't answer, the action is denied instead of allowed. A wrong key denies every Codex prompt and tool call, so verify the key before a wide rollout.
Codex command hooks inherit the process environment. Export ARCJET_KEY where Codex runs, or inject it with configuration management, and never commit a literal key.
Where should you deploy Codex hooks?
Codex loads hooks from two kinds of source. Managed requirements are set by an administrator, and users can't override them. Developers control user, project, and plugin files. Managed hooks rank above every hook that a developer can write.
| Mechanism | Where the files go | What it reaches | Who can remove it |
|---|---|---|---|
System |
| That device: CLI, app, and IDE extension | A local administrator only |
| MDM managed preferences | macOS | The same surfaces, redeployable on a schedule | A local administrator only |
| Cloud-managed requirements | The ChatGPT Business or Enterprise managed-config page | Every session that signs in with an eligible ChatGPT credential | Only by switching provider |
| Repository settings |
| Sessions in that repository, after the project | Any developer |
Codex doesn't distribute hook scripts. Your MDM or device-management tooling must install the script under managed_dir so that the absolute paths in requirements.toml resolve.
A repository file is useful for cloud sessions and for teams without MDM, but it isn't a control on its own. Project-local hooks load only when the project .codex/ layer is trusted, and Codex skips a non-managed command hook until someone reviews and trusts the exact definition in /hooks.
Can developers disable managed Codex hooks?
No, not from inside Codex. Managed hooks from requirements.toml, MDM, or cloud-managed requirements are trusted by policy and can't be disabled from the /hooks browser, so a developer can't remove the Arcjet hook without administrator access.
Two keys at the top of the managed file complete the lockdown. The following excerpt shows them with the PreToolUse entry:
allow_managed_hooks_only = true
[features]hooks = true
[hooks]managed_dir = "/etc/codex/hooks"windows_managed_dir = 'C:\ProgramData\OpenAI\Codex\hooks'
[[hooks.PreToolUse]][[hooks.PreToolUse.hooks]]type = "command"command = "sh /etc/codex/hooks/arcjet-hook.sh codex pre-tool-use"command_windows = 'powershell -NoProfile -File C:\ProgramData\OpenAI\Codex\hooks\arcjet-hook.ps1 -Vendor codex -Event pre-tool-use'timeout = 5The following list describes what each lockdown key does:
allow_managed_hooks_only = truestops a developer's own hooks from running alongside yours. It's valid only inrequirements.toml; putting it inconfig.tomldoesn't enable managed-hooks-only mode.[features].hooks = truepins hooks on, so a localhooks = falsecan't turn off the managed entries.
The full template in the Codex install guide repeats the entry for PermissionRequest and UserPromptSubmit, and adds recording entries for PostToolUse, Stop, SessionStart, SessionEnd, SubagentStart, SubagentStop, and PreCompact.
What can a Codex hook enforce?
Three Codex events can refuse an action:
PreToolUseruns before a local tool call. It is the control to rely on for tool calls.PermissionRequestruns when Codex asks for approval. It doesn't fire for calls that never ask, so treat it as a second check rather than the primary one.UserPromptSubmitruns before a prompt reaches the model. Codex honors a prompt denial.
Leave out matcher. A matcher that lists only Bash or apply_patch leaves MCP tools and other local function tools outside the policy. Set timeout on every entry, because the Codex default for most hooks is 600 seconds. SessionEnd supports at most 3 seconds. Arcjet evaluates the policy at the edge in over 300 data centers, so the added latency on each event stays small.
Arcjet maps Codex tool names to a tool_kind: exec_command is shell, apply_patch is file_write, spawn_agent is agent, and update_plan is other. It also matches the shared names such as Bash, Read, and Write. A rule over the kind applies to Codex, Claude Code, Copilot, and Cursor, so you maintain one policy for all four agents. You write policies in Rego for fine-grained control, or start from the starter policies in the Arcjet Console. Dry run shows what a rule would deny before it goes live, and changes take effect in real time. For examples, see block dangerous commands, stop coding agents reading secrets, and restrict MCP servers.
Why should an allow be an empty response?
Codex treats permissionDecision: "allow" as a grant that skips its own permission flow, and an allow that carries updatedInput can also rewrite the tool call. A policy service that answers allow explicitly turns a security control into a way to skip the Codex approvals.
Arcjet answers an allow with an empty object and a deny in the event's own shape. On PreToolUse, that is hookSpecificOutput with permissionDecision: "deny". On UserPromptSubmit, it is a top-level decision: "block". The denial reason names the rule IDs that fired and nothing else.
How do you secure Codex cloud sessions?
A session on Codex on the web runs in an environment on a clone of the repository, not on the developer's machine. It reads your organization's cloud-managed requirements, and a committed .codex/hooks.json after the project layer is trusted.
Codex environments block agent internet access by default. For the hook to reach the policy service, turn agent internet access on, add decide.arcjet.com to the domain allowlist, and allow POST. A methods allowlist that keeps only GET, HEAD, and OPTIONS blocks the hook. Cloud-managed requirements don't add a domain to an environment's network allowlist, so a developer who runs a session in their own environment has to allow the domain there.
Can you restrict which models Codex uses?
Yes. Codex has no model-switch hook, but UserPromptSubmit, PreToolUse, and PermissionRequest carry the selected model. An Arcjet allowed-models policy refuses the prompt and each tool call while a model outside the list is selected. For the policy, see restrict models in coding agents.
What do you pair with Arcjet on Codex?
Codex hooks apply to the sessions that load them. Pair Arcjet with the following controls for the paths that Codex's hooks don't reach:
- Personal accounts and unmanaged API keys. A developer who signs in to Codex with a personal ChatGPT account or an unmanaged API key bypasses your workspace. Pin sign-in on the device with
allowed_login_methodsandallowed_chatgpt_workspacesin local requirements, and restrict ChatGPT on the network with a workspace header. For the steps, see block personal AI accounts on work laptops. - Hosted tools. Hosted tools such as
WebSearchdon't use Codex's local function-tool hook path, soPreToolUsenever sees them. A tool-call policy can't refuse a hosted search, and an outbound-domain allowlist written as a hook policy doesn't cover it, so treat hosted search as outside the hook policy when you decide what Codex may do. - Local administrators. A local administrator can still edit or remove the system
requirements.toml, so on devices where developers have local administrator rights, the control applies to the managed device, not the person. - Other agents on the same laptop. Install Arcjet hooks for Claude Code, Copilot, and Cursor as well, with the same policies.
Where does Arcjet fit in securing Codex?
With Arcjet, Codex's tool calls and prompts are checked against your policy before they run, and the wrapper denies the action if Arcjet can't answer. The following capabilities apply to Codex:
- One policy across Codex, Claude Code, GitHub Copilot, and Cursor, decided from the hooks the agents already fire.
- Delivery as managed requirements, so developers can't disable or replace the hooks without administrator access.
- Rego policies, starter policies in the Arcjet Console, and dry run before a rule goes live.
- Every session and decision in the Arcjet Console, with export to Datadog, Splunk, SentinelOne, Panther, and Amazon S3 (Enterprise plan) for detection and alerting.
To get started, follow the Codex install guide. To verify the install, run /hooks in Codex and confirm that the entries appear. Then ask Codex to run a harmless command, and check that the tool call appears in the site's Activity in the Arcjet Console. From there, publish the coding-agent.destructive-command starter policy in dry run and read what it would deny.
Frequently asked questions
Can a developer turn off managed Codex hooks?
Not from inside Codex. Hooks from requirements.toml, MDM, or cloud-managed requirements are trusted by policy and can't be disabled from /hooks. Set allow_managed_hooks_only = true and pin [features].hooks = true in requirements.toml. A local administrator can still edit the system file.
What happens if the policy service is down when Codex calls a hook?
The Arcjet wrapper prints the Codex denial shape and exits with code 2 on any transport failure, non-2xx response, or non-JSON response. Codex treats exit 2 as a deny on PreToolUse and UserPromptSubmit, so the call doesn't run.
Does a Codex PreToolUse hook see web searches?
No. Hosted tools such as WebSearch don't use the Codex local function-tool hook path, so PreToolUse never sees them and a tool-call policy can't refuse them.
Do I need MDM to deploy Codex hooks?
Usually, or equivalent device tooling. ChatGPT Business or Enterprise cloud-managed requirements deliver the hook configuration to every session that signs in with an eligible ChatGPT credential, but Codex doesn't distribute the hook scripts themselves. MDM or other device-management tooling must install the script on each machine. A committed repository copy works for cloud sessions, but developers can remove it.
AI runtime security in your code
Protect your AI agent workflows with Arcjet
Install Arcjet hooks for Codex through managed requirements and enforce your policies on every prompt and tool call.