Repository navigation
Provide a supported way for local IDE/wrapper installers to request trust for installed hooks #21615
Description
What version of Codex CLI is running?
0.129.0
What platform is your computer?
Darwin 25.3.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
Orca terminal, running Codex CLI inside Orca
What issue are you seeing?
I maintain a local IDE/wrapper integration that runs Codex CLI and installs Codex hooks into ~/.codex/hooks.json so the wrapper can observe session/tool/turn lifecycle events.
After hook trust enforcement was added, those wrapper-installed hooks do not run until the user manually opens /hooks and approves each hook entry. In our case this blocks wrapper integration entirely until the user reviews several hooks by hand. The same review wall returns whenever the wrapper updates its hook command (e.g. install path moves between dev/prod, or an upgrade changes the wrapper script).
The issue is not that the trust gate is wrong. The problem is that there does not appear to be a supported programmatic way for a non-privileged installer to request user-consented trust for hooks it has installed. The available auto-trusted paths require system/MDM-managed configuration, while the user-writable path ~/.codex/hooks.json always hits manual review.
This isn't theoretical: the trust hash is reproducible from the open-source implementation, so the practical alternative for integrators today is to write Codex's internal trust state directly with [hooks.state."<key>"] trusted_hash = "..." entries in ~/.codex/config.toml. A supported API would be better than having each integrator ship its own copy of that private logic.
Expected: Codex provides a supported CLI/API path for local wrappers to request or complete user-consented trust for an already-installed hook entry.
Actual: wrappers either require the user to manually approve every installed hook in /hooks, or they edit Codex's internal trust state directly.
What steps can reproduce the bug?
Thread id: not applicable.
-
Install or run Codex 0.129.0 or newer.
-
Add an unmanaged hook to
~/.codex/hooks.json.Example:
{ "hooks": { "SessionStart": [ { "matcher": "", "hooks": [ { "type": "command", "command": "node /path/to/local-wrapper-hook.js SessionStart" } ] } ], "PreToolUse": [ { "matcher": "", "hooks": [ { "type": "command", "command": "node /path/to/local-wrapper-hook.js PreToolUse" } ] } ] } } -
Start Codex normally inside a local IDE/wrapper or terminal.
-
Trigger an event covered by the hook, such as starting a session or running a tool call.
-
Observe that the hook does not run because Codex requires manual approval through
/hooks. -
Open
/hooksand approve the hook manually. -
Trigger the same event again and observe that the hook now runs.
-
Change the hook command identity, for example by changing the script path or generated command.
-
Start Codex again and observe that the hook requires manual review again.
This is reproducible with any local wrapper that installs user-level hooks into ~/.codex/hooks.json. The important part is that the installer is user-level and cannot write to system-managed config such as /etc/codex/config.toml without requiring root.
What is the expected behavior?
Codex should provide a supported, stable way for a local installer to request user consent for a hook entry that already exists in Codex's discovered hook set.
For example, a CLI/API along these lines would solve the integrator case:
codex hooks trust request \
--source ~/.codex/hooks.json \
--event PreToolUse \
--command 'node /path/to/wrapper-hook.js PreToolUse' \
--jsonIdentifying entries by their content (event + command + optional matcher) rather than positional --group/--handler indices avoids fragility when other tools edit hooks.json between writes and shifts those indices.
Expected properties:
- Idempotent.
- Computes trust using Codex's own current normalization rules.
- Refuses to trust a hook that does not already exist in
hooks.json. - Produces machine-readable output for wrappers.
- Returns enough state for wrappers to surface "pending approval" clearly to users.
If maintainers want an approval command, it should still be user-consented, for example by opening the same Codex-owned confirmation path as /hooks:
codex hooks trust approve --interactive \
--source ~/.codex/hooks.json \
--event PreToolUse \
--command 'node /path/to/wrapper-hook.js PreToolUse' \
--jsonThat would avoid an unconditional auto-grant while still giving wrappers a supported path, instead of requiring each integrator to reproduce Codex's internal trust hash and edit ~/.codex/config.toml directly.
Additional information
This is not a request to remove or weaken hook trust enforcement. The trust gate is useful, and the current behavior makes sense for manually edited user config.
The gap is specifically for user-installed local tools that wrap Codex and need hooks as an integration surface. Those tools are not system administrators, so requiring /etc/codex or MDM-managed config is not a practical install path. At the same time, asking users to manually approve several generated hooks on every command/path change creates a poor setup and upgrade experience.
We have verified that reproducing Codex's current trust hash and writing to ~/.codex/config.toml works as a workaround, including for versions before 0.129.0 where the trust gate is absent. The concern is that this workaround depends on private implementation details and will likely be copied by multiple wrappers unless Codex exposes a supported trust/request API.
I'm happy to test or send a PR if maintainers are interested in a specific direction.
Activity
oxysoft commentedon May 8, 2026
Indexed this hook ticket in the umbrella tracker: #21753
Goal: collect the scattered Codex hook requests and bugs into one parity matrix for Full Claude Code Hook Parity (29+), while preserving this issue as the detailed thread for its specific behavior.
mkjt2 commentedon May 15, 2026
Hard agree. The fact that a hook's position changing in the broad hook list loses trust is a problem.
docs(codex): document hook-trust gate + how to approve (#74)
fix(sandbox): bypass Codex hook trust so provisioned hooks actually r…
Trust the hooks via Codex's own review prompt, which we already had
Document /hooks as the way to trust the collector
fix(codex): marker 정리 rc 전파·그룹 잔존 정리·표본 진단 소비 — 리뷰 반영
test(codex): supervised live fixture 실효화 + REQUIRE_NONEMPTY postcondi…
fix(hooks): prove guards actually deny, and close the root-clone gap …
jsirish commentedon Aug 26, 2026
Confirming this works and adding a data point: verified on macOS, codex-cli v0.149.0.
Copied a
[hooks.state."<plugin>@<marketplace>:hooks/hooks.json:<event>:0:0"] trusted_hash = "..."block (originally granted via an interactivecodex -p <profile> execsession that went through the normal directory-trust prompt) from a profile-layered config file (~/.codex/<profile>.config.toml) into the base~/.codex/config.toml— the file the Desktop app reads exclusively, since it has no--profilemechanism of its own. Also had to setfeatures.plugin_hooks = truein that same base config's[features]table (unclear if this was strictly necessary or redundant with the copy — didn't isolate the two variables).After that, automatic hook-driven capture started firing correctly in two different real projects, confirmed live in Codex Desktop itself (not just headless
codex exec) — matches what #37362 describes as a real gap in Desktop's own trust review flow.+1 on a real
codex hooks trust request-style subcommand. Hand-copying trust hashes between config layers works today (the hash is portable since it's content-based, not tied to how it was originally granted), but it's clearly not meant to be the supported path, and it's easy to imagine it breaking silently on a future release.docs(#52): document the unsupported Codex hook-trust workaround
fix(codex): report hook trust honestly, and stop pointing desktop use…
chore(release): 2.20.0 [skip ci]
fix(codex): establish hook trust from `node9 init`, so the install st…
chore(release): 2.23.1 [skip ci]
mib00038 commentedon Oct 2, 2026
Another concrete case for this. We install PreCompact/PostCompact command hooks from a launcher. Changing only a handler's
timeout(8 → 45) putstimeoutinto the normalised config that is hashed (hooks/src/engine/discovery.rs:559-565,:775-791), so every host's existing trust becomesModified(:805-808) and the hooks are skipped until a person re-trusts them in/hookson each machine. A supported, user-consented way for an installer to request trust for the exact handler it just wrote would remove that manual step per host.glmn commentedon Oct 7, 2026
Another data point: the same wall applies to hooks that are not installed anywhere. We pass hooks for a single run only, through the session-flags layer (
codex -c 'hooks={...}'), so nothing is written to the user's~/.codex. Those hooks are still reviewed like user hooks, so the only way to start without the review screen (and without--dangerously-bypass-hook-trust, which would also run a repository's untrusted hooks) is to reproduce Codex's canonical-JSON SHA-256 and passhooks.state."<key>".trusted_hashin the same-clayer. That works (checked againstcodex app-serverhooks/liston 0.153.4), but it depends on private details: the key format, the normalisation inhooks/src/engine/discovery.rs, and the position-based keys (#49399).For a host that builds the command line itself, a supported way to say "the hooks in this layer were authored by the launcher" would remove the need for that copy. For example, hooks supplied via
-con the command line could count as trusted for that run only, or a--trust-session-hooksflag could apply only to hooks from the session-flags layer and never to project or plugin hooks.mib00038 commentedon Oct 13, 2026
A follow-up to the position-keyed trust point raised in #49399, which was closed as not planned. It belongs here, because it is the installer side of the same problem.
We just cleaned up a host (codex-cli 0.161.0, Linux) where an older installer had left two stale handler groups around the current one in
~/.codex/hooks.json, forSessionStart,PreCompactandPostCompact. Removing the stale entries is a pure deletion: the remaining handler's definition did not change. Because trust is keyed by<source>:<event>:<group_index>:<handler_index>, the current handler moved from index 1 to index 0. Its existing trust no longer applied, so every event needed a person in/hooksbefore the hooks would run again.Two consequences for installers:
hooks/listis the obvious read, but we found no supported read-only connection to an existing app-server, and attaching through a client replaces that client. So after the review, the only confirmation we had was the person reading the/hooksscreen.Keying trust on the handler's definition, rather than on its position, would make all three cases in this thread safe: the edits by hand in #49399, an installer's in-place updates, and cleanup by deletion. A read-only way to list hook trust state without starting a thread would cover verification.