Generated from
compatibility/codex.json,
the same record leastgrant doctor reads. Last verified 2026-09-02.
Codex CLI
the real agent ran with LeastGrant installed and enforcement was observed happening.
How it attaches
Command handlers for `PreToolUse`, `PermissionRequest` and `PostToolUse` in ~/.codex/hooks.json. Codex binds trust to a hash of the handler definition, so upgrading re-flags it until re-trusted in /hooks.
Install it with leastgrant install codex. Configuration is written to
~/.codex/hooks.json, alongside anything
already there — LeastGrant never removes a hook it did not add.
What a verdict does here
The three verdicts do not travel equally well. This is the table that decides whether LeastGrant is a prompt, a veto, or a suggestion on this agent.
| verdict | lands? | what actually happens |
|---|---|---|
| allow | ignoredsource | Only deny is enforced on PreToolUse. An allow is accepted and changes nothing — the call proceeds to Codex's own approval logic. Allow is only enforced on PermissionRequest. |
| ask | nonesource | There is no ask verdict in any mode. On PreToolUse, permissionDecision 'ask' is parsed, rejected as invalid, and the call then runs anyway. LeastGrant converts a floored ask into a deny where no human can be reached. |
| deny | yesprobe | Works in every mode including --dangerously-bypass-approvals-and-sandbox. A deny with a blank reason is rejected as invalid, so the reason string is load-bearing. |
Interactive and unattended
This agent tells the hook which permission mode it is in — permission_mode, required, top-level.
Modes it reports: default, dontAsk, bypassPermissions.
An ask reaches a person in none of them.
Nothing. permission_mode is derived only from the approval policy and never from the sandbox mode, so `default` does not imply a human will be asked. `codex exec` hard-sets the policy to Never.
When the hook breaks
The question nobody asks until it matters: if LeastGrant crashes, times out, or cannot start, does the tool call still run?
- On crash — the call runs anyway. (source) Every failure path sets HookRunStatus::Failed and leaves the call to proceed.
- On timeout — the call runs anyway, after 600s by default.
- Can it be made to fail closed? — no.
What it can see
A verdict is only worth as much as the set of actions it is asked about. gated means LeastGrant is consulted before the thing happens; seen after means it is told afterwards and can at best withhold the result; not covered means the action happens with LeastGrant never hearing about it.
| tool class | coverage | detail |
|---|---|---|
| shell commands | gatedprobe | |
| file reads | gatedprobe | |
| file writes | gatedprobe | |
| deletions | gatedsource | |
| MCP calls | gatedsource | |
| subagent spawn | unknownunknown | |
| network / web | unknownunknown |
What has actually been run
Four different things, deliberately not collapsed into one badge. Reproducing an agent's invocation is not running the agent; a passing conformance suite says our side is right and nothing about whether the host ever calls us.
- Live agent test — Real `codex exec` sessions driven end to end: a credential read and a write to LeastGrant’s own records were both blocked, while `node --version` and `git status` ran untouched. 0.152.0 · win32 · 2026-09-02
- Real transport probed — Codex runs hook commands through PowerShell on Windows; the quoting failure that made the hook never start was found by watching a real session print "PreToolUse Failed" and run the command anyway. 0.152.0 · win32 · 2026-09-02
- Contract read from the shipped build — Events, payload fields, decision parsing and the four kill switches read from the shipped codex.exe and codex-rs sources. 0.152.0 · win32 · 2026-09-02
- Conformance suite — Driven by test/conformance.test.ts. 0.152.0 · win32 · 2026-09-02
Not exercised on darwin, linux.
What it cannot do
Not a disclaimer. The point of everything above is that this list exists and is specific.
- No ask verdict exists at all. On Codex, LeastGrant is a veto rather than a prompt.
- A handler declared `"async": true` cannot apply control effects: its deny, its exit-2 block and its updatedInput are all silently ignored. A single wrong word in the config is a total, unsignalled bypass.
- Hook trust is bound to a hash of the definition, so upgrading or reinstalling re-flags the hook and it stops running until re-trusted in /hooks.
- On Windows the trust hash covers only `commandWindows` and on POSIX only `command`, so one hooks.json has different hashes on different machines.
- `codex exec` has no trust review and no warning: an untrusted or modified handler is simply never added to the handler set, and nothing says so.
- Fails open on crash and on timeout.
- `[features] hooks = false` and `allow_managed_hooks_only = true` disable user hooks entirely.
- PermissionRequest has never fired in a real session. `codex exec` runs unattended, where by definition nothing prompts, so that path is contract-tested only.
- Verified on Windows only.