agentjail sits at the boundary between your coding agent and the tools it can call. It intercepts every outgoing tool call, evaluates it against your policy, and returns a verdict before the call is handed off to the shell, filesystem, or network.
The tool-call boundary
Coding agents expose a lifecycle hook called PreToolUse. This hook fires
before a tool call executes, giving agentjail a chance to inspect it and either
pass it through, block it, or ask the user to confirm.
your agent (Claude Code)
│
│ tool call
▼
agentjail-hook (PreToolUse hook)
│
│ Unix socket
▼
agentjail-daemon (OPA / Rego, ~8 ms median)
│
├─ allow ─▶ shell / files / network
├─ ask ─▶ user prompted
└─ deny ─▶ blocked (e.g. rm -rf ~/.ssh)
The agentjail-hook binary (installed in ~/.agentjail/bin/) receives the
tool-call payload and forwards it over a Unix socket to the persistent
agentjail-daemon process. The daemon keeps OPA warm and evaluates your Rego
policy in ~8 ms median, then returns the verdict. The hook prints the decision to
the agent and exits.
Nothing runs until agentjail has evaluated the call. If the call is denied, the command never reaches the shell.
Integration status: Claude Code, Codex, and Cursor are all fully supported.
agentjail installauto-detects whichever agents are present and wires the hook for each.
What agentjail evaluates
Each tool call arrives as a structured object. The fields your policy can inspect include:
input.tool_name: the name of the tool being called (for example,"Bash").input.tool_input: the arguments for that tool (for example,{"command": "rm -rf ~/.ssh/"}).input.hook_event: the lifecycle hook name (always"PreToolUse").input.session_id: the agent session identifier.input.cwd: the agent’s current working directory.
For a Bash tool call the shell command is at input.tool_input.command. For
Write and Edit calls the target file is at input.tool_input.file_path.
MCP tools surface as input.tool_name values like mcp__server__tool.
Your policy rules inspect these fields and decide whether to allow, ask, or deny. See The policy model for how rules are written.
Config overlay
The daemon loads ~/.agentjail/policy.yaml at startup and re-injects it into
OPA as data.agentjail.config on every SIGHUP (no restart needed). The
config carries MCP allowlists, extra path deny patterns, disabled rule IDs, and
other tuning values that Rego rules read at evaluation time. The decision cache
is invalidated on every reload, so policy changes take effect immediately.
Request paths and cwd are canonicalized (symlinks and .. resolved) before
evaluation, so rules always operate on real absolute paths.
Offline evaluation
Evaluation runs entirely on the local machine. There is no network call at decision time, no external service, and no model in the decision loop. This keeps verdicts fast and deterministic: the same tool call produces the same verdict every time, regardless of network conditions or service availability.
Allow, ask, and deny
There are three possible verdicts:
- allow: the call proceeds normally.
- ask: the hook prompts the user to confirm before proceeding.
- deny: the call is blocked; the command never reaches the shell.
The resolver picks the most restrictive candidate: deny > ask > allow. If no
candidate fires at all, the default is ask (fail-safe: unknown calls
escalate to the user rather than silently proceeding).
See Verdicts for the full semantics and what the agent receives on each outcome.
What happens on a denial
When a call is denied, agentjail returns a structured block message to the agent and exits with status code 2 (the Claude fast-block convention). The agent receives the denial reason and stops rather than proceeding. The command is never handed to the shell.
You can test this manually while the daemon is running:
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"rm -rf ~/.ssh/"}}' \
| agentjail-hook
You can also unit-test Rego policies directly with opa test.
Why this approach
Enforcing policy at the tool boundary, offline, has a few practical consequences:
- No false sense of safety from latency. There is no window between “agent decides to act” and “policy is checked.” The check is synchronous and happens before execution.
- Auditable. Every rule is plain text you can read, diff, and version alongside your project.
- Works without network. Useful in air-gapped environments, strict CI, or anywhere an outbound call would be blocked.
Beyond hooks
The hook layer is cooperative: the agent must call the hook, and the hook must
pattern-match the command. Shell tricks like variable expansion, eval, or
non-shell interpreters can bypass hook-level protection. agentjail provides
stronger isolation tiers for these cases:
- OS-native sandbox: wraps the agent in the kernel sandbox so subprocess spawns and shell tricks are caught at the syscall level.
- Isolation tiers: the full spectrum from hooks to containers to kernel-level enforcement.
Next steps
- The policy model: how rules are written and evaluated.
- Verdicts: the exact semantics of allow, ask, and deny.
- OS-native sandbox: kernel-level enforcement for defense in depth.