A community-driven registry for the Claude Code ecosystem. Not affiliated with Anthropic.
Are you the author? Sign in to claim
Security hooks with SSRF protection, MCP compression, and OpenTelemetry tracing
|
|
claude-warden is a hook system for Claude Code that intercepts every tool call before and after execution. It silences verbose commands, compresses large outputs, blocks unsafe network calls, enforces subagent budgets, and surfaces a live statusline — saving tens of thousands of tokens per session with negligible added latency.
jq (required). Optional: rg, fd.~/.claude/ (symlink mode):
./install.sh
--profile standard).[!TIP] Run
./install.sh --dry-runfirst to preview every change before anything touches~/.claude/.
./install.sh --dry-run
claude-warden installs shell hooks that intercept every Claude Code tool call. Each hook enforces token-efficient patterns and blocks unsafe operations.
|
|
|
|
stat() checks.
| Dependency | Purpose | |
|---|---|---|
| Required | jq | JSON processing |
Go 1.23+ | Builds the warden-collector binary | |
| Recommended | rg, fd | Faster search/find in hooks |
| Optional | python3 | Warden-viewer web UI |
mitmdump | API capture tool only |
curl -fsSL https://raw.githubusercontent.com/johnzfitch/claude-warden/master/install-remote.sh | bash
Downloads a release tarball, verifies its SHA-256 checksum, validates contents, then runs install.sh --copy.
To pin a version:
curl -fsSL https://raw.githubusercontent.com/johnzfitch/claude-warden/master/install-remote.sh | bash -s -- v0.6.1
git clone https://github.com/johnzfitch/claude-warden.git ~/dev/claude-warden
cd ~/dev/claude-warden
./install.sh
The installer applies a configuration profile that sets token limits, tool permissions, and internal thresholds. Choose one during install or pass --profile:
| Profile | What it sets |
|---|---|
minimal | Hooks only. No env or permission changes. For users who manage settings.json themselves. |
standard | Token/output limits, OTEL monitoring, 40 safe tool permissions. Recommended. |
strict | ~40% tighter limits. Fewer pre-approved tools (19). Lower subagent budgets. |
./install.sh --profile standard
Profiles live in config/profiles/. Create config/user.json (gitignored) for personal overrides:
cp config/user.json.template config/user.json
# Edit config/user.json, then re-install:
./install.sh --profile standard
Merge order: defaults.json ← profile ← user.json ← existing settings.json (non-warden keys preserved).
| Mode | Command | Behavior |
|---|---|---|
| Symlink (default) | ./install.sh | Edits to repo take effect immediately |
| Copy | ./install.sh --copy | Files independent of repo |
| Dry run | ./install.sh --dry-run | Preview changes, write nothing |
What install.sh does:
jq required, warns if rg/fd missing). Detects platform. Backs up existing hooks and settings.json.--profile). Deep-merges defaults.json + profile + user.json.lib/ + statusline.sh into ~/.claude/.~/.local/bin/.warden.env (thresholds). Merges env vars and permissions into settings.json../uninstall.sh
Restores your most recent settings.json backup. Removes ~/.claude/.warden/ config. Hook backups remain in ~/.claude/hooks.bak.*/.
All thresholds are configurable via config/defaults.json, profiles, or config/user.json. After editing, re-run ./install.sh to regenerate ~/.claude/.warden/warden.env.
| Threshold | Config key | Default | Strict |
|---|---|---|---|
| Output truncation | warden.truncate_bytes | 20KB | 10KB |
| Subagent read cap | warden.subagent_read_bytes | 10KB | 6KB |
| Output suppression | warden.suppress_bytes | 512KB | 256KB |
| Read file size limit | warden.read_guard_max_mb | 2MB | 1MB |
| Write max size | warden.write_max_bytes | 100KB | 50KB |
| Edit max size | warden.edit_max_bytes | 50KB | 25KB |
| Subagent call limits | warden.subagent_call_limits.* | 15–40 | 10–25 |
| Subagent byte limits | warden.subagent_byte_limits.* | 80–150KB | 50–100KB |
Token limits and tool permissions are set in the env and permissions sections of the config files and merged into settings.json during install.
read-compress — subagent threshold at 300 lines, main agent at 500 linespost-tool-use — POSIX od + grep for NUL bytes (full-stream scan)Shell environment
Add to ~/.zshrc or ~/.bashrc:
# claude-warden env
source "$HOME/.claude/.warden/warden.env.sh"
Exports OTEL, token limit, timeout, and sandbox vars from your profile into every new shell.
Token savings accounting
All hooks report savings to ~/.claude/.statusline/events.jsonl at ~3.5 bytes/token (estimated). For exact counts:
export WARDEN_TOKEN_COUNT=api
Each truncation event spawns a background call to the Anthropic token counting API (free, separate rate limits) and appends a correction event. Zero added latency.
ANTHROPIC_API_KEY in environment; python3 with anthropic installedWARDEN_PYTHON=/path/to/venv/bin/python3 if neededDisabling specific guards
Remove the corresponding matcher from settings.hooks.json and re-run ./install.sh. Example — disable read compression:
{
"matcher": "Read",
"hooks": [{"type": "command", "command": "$HOME/.claude/hooks/read-compress", "timeout": 7}]
}
Adding your own permission allow-list
cp config/user.json.template config/user.json
{
"permissions": {
"allow": ["Bash(gh api:*)", "Bash(pacman -Q:*)", "mcp__filesystem__list_directory"]
}
}
Re-run ./install.sh to merge. User permissions are unioned with profile permissions — nothing is removed.
| Platform | Status | Notes |
|---|---|---|
| Linux | Full support | Primary development platform |
| macOS | Full support | gtimeout fallback, osascript notifications, macOS stat flags |
| WSL | Full support | Detected via /proc/version |
timeout: Falls back to gtimeout (coreutils), then no-timeoutstat: Uses -c%s (Linux) with -f%z (macOS) fallbackflock: Replaced with mkdir-based locking (atomic on all POSIX)notify-send: Falls back to osascript (macOS), silently skips if neither availablerg: Falls back to grep where usedod -An -tx1 | grep ' 00' (POSIX, works on macOS/Linux/BSD)The warden-collector is a Go service that provides session tracking, subagent budget enforcement, and OTLP span ingestion. It starts automatically on session-start and stops when idle.
:4319 for traces from Claude Code, extracts llm_request spanspre-tool-use via stat()POST /v1/ingest/hook over Unix domain socketGET /v1/sessions, GET /v1/sessions/{id}/context (used by statusline)Data storage — ${XDG_STATE_HOME:-~/.local/state}/claude-warden/:
| File | Purpose |
|---|---|
collector.db | SQLite database (sessions, events, budgets, OTLP spans) |
collector.sock | Unix domain socket for hook → collector communication |
collector.pid | PID file for lifecycle management |
budget-deny-* | Deny files written when subagent budgets are exceeded |
Viewer — optional htmx web UI on port 8477:
python3 viewer/warden-viewer.py
Optional Docker stack in monitoring/ for log aggregation, metrics, and tracing. Supplements the Go collector with Grafana dashboards and long-term storage.
| Service | Image | Port | Purpose |
|---|---|---|---|
| Loki | grafana/loki:3.4.2 | 3100 | Log aggregation (30-day retention) |
| OTEL Collector | otel/opentelemetry-collector-contrib | 4317/4318 | OTLP logs + traces, tails events.jsonl |
| Prometheus | prom/prometheus | 9090 | Metrics |
| Node Exporter | prom/node-exporter | 9101 | Textfile collector for budget metrics |
| Tempo | grafana/tempo:2.7.2 | 3200/3205 | Trace storage |
| Grafana | grafana/grafana | 3000 | Dashboards (admin/admin) |
Start (Linux):
cd monitoring && docker compose up -d
macOS / Docker Desktop:
cd monitoring && docker compose -f docker-compose.yml -f docker-compose.macos.yml up -d
Dashboards — four provisioned in monitoring/grafana/dashboards/: cost/tokens/budget (claude-code-otel), tool latency/traces (warden-tool-latency), output size/tokens (warden-output-size), subagent/session lifecycle (warden-subagent-lifecycle).
Verification:
curl -s http://localhost:3100/ready # Loki
curl -s http://localhost:3200/ready # Tempo
grep tool_latency ~/.claude/.statusline/events.jsonl | tail -5
MITM proxy wrapper in capture/ for recording Claude Code API traffic.
capture/claude # interactive session
capture/claude -p "prompt" # non-interactive
Logs land in ~/claude-captures/YYYY-MM-DD/capture-HHMMSS.jsonl.
[!IMPORTANT] Bodies are truncated to 200 chars by default. Set
WARDEN_CAPTURE_BODIES=1for full capture. Sensitive keys (system,messages) are always redacted.
mitmdump + trusted CA cert at ~/.mitmproxy/mitmproxy-ca-cert.pemx-api-key, authorization, proxy-authorization headers redacted| Path | Purpose |
|---|---|
hooks/ | Hook scripts (bash) |
hooks/lib/common.sh | Shared library: parsing, events, latency, cross-platform shims |
hooks/lib/otel-trace.sh | OTLP/HTTP trace span emitter |
config/ | Defaults, profiles (minimal/standard/strict), user overrides |
collector/ | Go collector: SQLite, OTLP receiver, budget enforcement |
viewer/ | htmx web UI (sessions, tokens, events) |
capture/ | MITM proxy for API traffic capture |
statusline.sh | Claude Code statusline script |
settings.hooks.json | Hook config merged into ~/.claude/settings.json |
install.sh | Install hooks, merge config, generate warden.env |
uninstall.sh | Remove hooks, restore settings backup |
monitoring/ | Optional Docker stack (Loki, OTEL Collector, Prometheus, Tempo, Grafana) |
tests/ | Fixture-driven test harness |
Claude Code supports hooks — shell commands that run at specific points in the tool-use lifecycle. Hooks receive JSON on stdin describing the tool call and can:
{"suppressOutput":true}){"modifyOutput":"..."}) or suppress itHooks are pure bash with a single dependency (jq). They run in milliseconds. All paths use $HOME for portability. Every decision (block, truncate, compress, strip) is logged to events.jsonl with token savings estimates.
Run the test harness:
bash tests/run.sh
It runs shell syntax checks, validates JSON fixtures, and executes fixture-driven behavioral assertions covering pre-tool-use blocking/allow, post-tool-use output tracking/truncation, read-compress, permission-request, and statusline rendering.
# Shell syntax
find hooks -maxdepth 1 -type f -print0 | xargs -0 bash -n
bash -n install.sh uninstall.sh statusline.sh
# JSON validity
jq . settings.hooks.json config/defaults.json config/profiles/*.json >/dev/null
# Exercise post-tool-use fixture (system reminder stripping)
cat demo/mock-inputs/post-tool-use-reminder-bash.json | hooks/post-tool-use | jq -r '.modifyOutput'
~/.claude/settings.json contains the hooks key: jq '.hooks | keys' ~/.claude/settings.jsonpermissions.allow bypass the permission-request hook entirely.read-guard blocks bundled/generated patterns (node_modules/, dist/, minified JS) and files larger than 2MB (configurable via warden.read_guard_max_mb). Use bounded reads or find the source file.
The base docker-compose.yml uses network_mode: host, which Docker Desktop does not support. Use the macOS override:
docker compose -f docker-compose.yml -f docker-compose.macos.yml up -d
This switches to bridge networking and replaces localhost with Docker service DNS names.
Claude Code gates hooks on workspace trust. Until the project's CLAUDE.md is accepted at launch, all hooks are skipped. Symptom:
Skipping PreToolUse:Bash hook execution - workspace trust not accepted
Fix: create a CLAUDE.md in the project root. Press Enter to accept it on next session start.
See CONTRIBUTING.md.
See SECURITY.md for security assumptions, data handling notes, and reporting guidance.
MIT
Give Claude Code memory that evolves with your codebase via hooks and LLM-compiled knowledge
Context management with hooks for state via ledgers, MCP without context pollution
An LLM council that reviews your coding agent's every move for quality assurance
Protective hooks preventing code loss via branch protection, checkpointing, and safe squashing
Community Package
@johnzfitch on GitHub