Hook Architecture & Security
Hooks run in the critical path of every tool call — a bug in a hook silently breaks the agent loop. Understand what hooks can do, what they can't, and how to guard the blast radius of each one you deploy.
Introduction
Hooks are powerful because they run on every matching tool call. That's also what makes them dangerous if written carelessly: a buggy hook that exits 2 incorrectly will block legitimate operations, and a hook that hangs will stall the agent loop indefinitely.
The discipline of hook architecture is proportionality: guard the blast radius of each tool correctly, keep hooks fast and side-effect free, and always fail open (exit 0) on unexpected hook errors.
Blast Radius by Tool Type
The blast radius analysis is simple: if the hook exits 2 incorrectly, what legitimate operation does it prevent? For a Bash hook, that could be any shell command — so the pattern match must be as specific as possible. For a Write hook, an incorrect block prevents any file write — potentially stopping the entire coding workflow.
This is why Knox uses narrow pattern matching in his hooks:
grep -q "git push --force"rather thangrep -q "git push"— otherwise all git pushes get blockedgrep -E "^(AKIA|AKID|AKEY)"for AWS keys rather than scanning for any uppercase string — too broad blocks legitimate content
The narrower the match, the smaller the blast radius, the safer the hook.
What Hooks Can and Cannot Do
Hooks operate on the request before execution (PreToolUse) or after execution (PostToolUse). This is a critical constraint:
Hooks CAN:
- Read the tool input from stdin (JSON blob)
- Inspect file paths, command strings, content before write
- Exit with a code that blocks or allows
- Write the block reason to stderr (fed to Claude on exit 2) or informational output to stdout
- Log to local files for audit trails
- Count events and compare to thresholds
Hooks CANNOT:
- Undo a tool call that has already run (PostToolUse only sees aftermath)
- Modify the tool's input before execution (read-only interception)
- Access Claude's full context or conversation history
- Make changes that persist beyond the hook's execution
- Make network calls reliably (latency unpredictability)
- Import external Python/Node modules that may not be installed
The read-only interception model means PreToolUse is where prevention happens. PostToolUse is where observation happens. Trying to "fix" a problematic tool call in PostToolUse by modifying its output is not supported.
Security Checklist
The most important security rule: never let hook internal errors block operations. A hook that exits 2 on JSON parse errors will block all tool calls when Claude Code changes its input format, the JSON is malformed for any reason, or a tool is called in an unexpected way. Always catch errors and exit 0 when the hook itself fails — only exit 2 when you've confirmed the threat.
The Hook Audit Pattern
Before deploying any hook, Knox runs through three questions:
- What does this hook block when it works correctly? (The intended blast radius)
- What does it block when it malfunctions? (The unintended blast radius)
- What happens if this hook hangs for 30 seconds? (The latency blast radius)
If the answer to question 2 is "everything" or question 3 is "the agent loop stalls," the hook needs a timeout guard and a fail-open error handler.
The test suite for hooks is manual but systematic: invoke the hook directly on sample inputs before wiring it to settings.json. Check that it exits 0 on safe inputs, 2 on blocked inputs, and 0 on malformed inputs (fail open, not fail closed).
Practical Hook Deployment Workflow
- Write the hook as a bash script
- Test it in isolation:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /tmp"}}' | bash hook.sh - Verify exit codes: dangerous input → exit 2, safe input → 0, malformed input → 0
- Add a
timeout 5wrapper to prevent hangs - Wire to settings.json under
hooks.PreToolUsewith a matcher - Test in a live session with a non-dangerous command that matches the pattern
- Monitor the first week for unexpected blocks
This workflow catches the common failure modes before they reach production: false positives from too-broad pattern matching, hangs from unexpected input, and crashes from missing error handling.
What's Next
The next lesson covers permission modes and the allowlist — how to choose the right mode for different contexts, design an allowlist that eliminates prompt fatigue without sacrificing safety, and understand when the bypass flag is appropriate.