LeastGrant

Generated from compatibility/codex.json, the same record leastgrant doctor reads. Last verified 2026-09-02.

all agents

Codex CLI

Veto only LIVE VERIFIED 0.152.0 win32

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.

verdictlands?what actually happens
allowignoredsourceOnly 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.
asknonesourceThere 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.
denyyesprobeWorks 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 classcoveragedetail
shell commandsgatedprobe
file readsgatedprobe
file writesgatedprobe
deletionsgatedsource
MCP callsgatedsource
subagent spawnunknownunknown
network / webunknownunknown

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.