Skip to main content

Claude Code / Codex integration

Claude Code and Codex both support native PreToolUse and PostToolUse hooks — the exact points Belay needs to gate a tool call before it runs and to sanitize output after it returns. Belay offers two integration styles for these hooks: hook (a persistent socket client) and gate (a one-shot stdin/stdout verdict).

When to use

  • Use hook for the normal, resident setup: a long-running daemon holds the policy engine, and each tool call is a fast round-trip over the local socket. This is what belay protect claude-code wires up.
  • Use gate when you need a self-contained, one-shot verdict with no persistent daemon — for example a minimal pipe-mode integration or a constrained environment where running a resident service is not an option.

How the hooks map

Hook eventWhat Belay does
PreToolUseEvaluates the pending tool call against the deterministic rule catalog and returns a verdict: allow, ask, or deny. This is the enforcement point.
PostToolUseRe-runs over the tool's output and redacts secret-shaped strings (API keys, tokens, key material) before they reach the agent's context or your logs.
note

Only PreToolUse carries the enforcement verdict. PostToolUse is about output redaction — it never un-blocks a call that was already denied.

The verdict priority is fixed: Deny > Ask > Allow. A deny can never be downgraded by the dev-toolchain allowlist.

Style 1 — hook (persistent socket client)

hook is a thin client that reads the hook event JSON on stdin and forwards it to the resident daemon over its local socket. The daemon holds the compiled-in rules, so the client stays tiny and the decision stays sub-100ms.

The command string that Claude Code / Codex invoke is installed into settings.json as:

belay hook pretooluse # PreToolUse gating (verdict)
belay hook posttooluse # PostToolUse secret redaction

You normally do not wire this by hand — belay protect claude-code (or codex) installs the hook entry into the agent's settings.json for you:

belay protect claude-code

The daemon must be running for hook to reach it. Run it directly with belay daemon, or install it as a boot-start service:

sudo belay install-service --enable
tip

install-service also re-points the installed Claude Code hook at the staged binary, so the hook keeps working across rebuilds. See CLI overview.

Style 2 — gate (one-shot stdin -> stdout verdict)

gate is the pipe-mode equivalent: it reads a single hook event as JSON on stdin, evaluates it in-process, and writes the verdict to stdout — no persistent socket, no resident daemon.

echo '<hook-event-json>' | belay gate

Because it is self-contained, gate is convenient for constrained or ephemeral environments. The trade-off is that it does not share a long-lived engine or the daemon's live session-correlation state across calls the way hook + daemon do.

Which should I use?

hookgate
Requires resident daemonYesNo
TransportLocal socketstdin / stdout pipe
Session correlation across callsYes (via the daemon)Per-invocation
Best forNormal desktop / VPS setupsMinimal or ephemeral pipe integrations

For almost everyone, hook is the right choice and protect sets it up automatically. Reach for gate only when a resident daemon is not viable.

Explain & advise on every verdict

Every verdict Belay returns carries a plain-English explanation — a curated summary / what / why_risky / normal_use / suggested_action written per rule — so an ask prompt (in the terminal, the desktop app, or a chat channel) tells you why a call is risky and what a normal use of it looks like.

An optional AI explainer (OFF by default, feature-gated, BYOK) can enrich that text via a local Ollama model or a cloud provider. The AI output is advisory only: it never makes or changes a decision, secrets and host paths are redacted before any send, and any error falls back silently to the curated explanation. Configure it in the setup wizard or the desktop app's AI Explanations tab.

Approvals over chat

When a verdict is ask, Belay can send the approval prompt to a messaging channel and take your Allow / Deny reply back — two-way on Telegram, Discord, WhatsApp, Matrix, Mattermost, and Slack. Only enrolled approvers can answer (default-deny), and stale prompts auto-expire to a deny. See Notification channels.

Fail-closed behavior

Whichever style you use, the engine is fail-closed: an evaluation error, a timeout, or an unanswered ask resolves to deny, and a deny can never be downgraded by the dev-toolchain allowlist.