Claude Code Audit Log, Hook & Governance — Install Guide
See, control, and price every tool call Claude Code makes — Bash, Edit, Write, WebFetch, MCP, and the model calls behind them — from one dashboard. One command, no code changes.
Every tool call Claude Code makes — Bash, Edit, Write, WebFetch, MCP — flows through ACP’s PreToolUse and PostToolUse hooks. Logged with identity. Denied when policy says deny. Audited end-to-end through delegation chains when subagents spawn other subagents.
TL;DR
# Free, on-device, no account — decisions run locally from ~/.acp/policy.json:
curl -sf https://agenticcontrolplane.com/install.sh | bash -s -- --local
# Or connect a workspace for team policy, cost X-ray, and a shared console:
curl -sf https://agenticcontrolplane.com/install.sh | bash
bash deserves that. The run ends on the audit log showing two allows and one deny — governance is mostly saying yes, in writing.Three things you get the moment it's running — click any to see it full-size:
rm -rf, force-push, and the rest. Enforced at the call, outside the model.See your first governed call →Free for individuals · full install below
The script detects Claude Code, writes the hook ~/.acp/govern.mjs, and registers it in ~/.claude/settings.json for both PreToolUse and PostToolUse events. Restart Claude Code (Ctrl+C then claude --continue) and every tool call is governed.
With --local, decisions are made on your machine by ~/.acp/decide.mjs against ~/.acp/policy.json (allow / ask / deny per tool, plus an always-on safety floor), and every call is logged to ~/.acp/audit.jsonl. No browser, no account, nothing leaves your machine. Without the flag, the installer opens your browser to provision a workspace and routes decisions through the ACP API instead. You can start local and re-run without --local any time to upgrade.
How it works
Claude Code’s hook system fires before and after every tool call. ACP installs a single govern.mjs Node script and registers it for both events:
- PreToolUse — fires before the tool runs. ACP’s hook POSTs the tool name, input, session ID, and
agent_tiertohttps://api.agenticcontrolplane.com/govern/tool-use. Server evaluates the seven-layer governance pipeline (immutable rules, delegation chain, scopes, ABAC, rate limits, plan limits, content scanning). If the response saysdecision: deny, the hook printspermissionDecision: denyand Claude Code blocks the call. - PostToolUse — fires after the tool returns. ACP’s hook POSTs the tool output (truncated at 200KB) to
/govern/tool-output. Server scans for PII, prompt injection, and secrets. The response can flag findings into the audit log; output mutation is currently observe-only.
Both hooks have a 4-second timeout. If ACP is unreachable, the hook fails open by default — the call is allowed but every one carries a loud “this ran UNGOVERNED” message. We never brick your agent because our gateway had a bad moment. Compliance-sensitive? Opt into fail-closed (unreachable ACP blocks the call) with echo closed > ~/.acp/failmode or ACP_FAIL_MODE=closed. A policy deny always blocks either way — fail mode only governs infrastructure failures.
What gets installed and where
| Path | Contents |
|---|---|
~/.acp/govern.mjs |
The hook script — Node, ~200 lines, no dependencies |
~/.acp/credentials |
Your bearer token (set after browser OAuth) |
~/.claude/settings.json |
Adds hooks.PreToolUse and hooks.PostToolUse entries pointing at govern.mjs |
The installer is idempotent — re-running it adds the hook entry to settings.json only if missing, and preserves any other hooks you’ve configured. Running on a machine without Claude Code installed is a no-op.
What you’ll see in the dashboard
Open cloud.agenticcontrolplane.com/agents — your Claude Code installation appears in the Detected agents table within seconds of the first hook fire. Each row shows:
- Client (Claude Code) and the API key label
- Activity breakdown by tier (
interactive,subagent,background) and any named subagents (Explore,Plan,general-purpose) - 30-day call count and last-seen timestamp
For deeper investigation, the Activity log shows every individual tool call with its full context: identity, scopes, decision, latency, PII findings, and — for delegated runs — the full chain provenance.
Setting up policy
Three policy axes apply to Claude Code traffic:
- Tool policies (Policies → Tool Policies) — workspace-wide tool allowlist and required scopes. Blocks
github.repos.deletefor everyone? Set it here. - Agent policies (Policies → Agent Policies) — per-tier rules for Claude Code interactive vs subagent vs background. Lock down what background agents can do without restricting your interactive sessions.
- User policies (Policies → User Policies) — overrides per identity. Grant Alice access to Stripe-touching agents, restrict Bob.
Most-restrictive wins on conflict. Start in audit mode for a few days to learn what your team actually does, then switch to enforce mode when patterns are clear.
Limitations
Honest list of what ACP’s Claude Code integration cannot do today:
--dangerously-skip-permissionsbypasses all hooks. Claude Code’s own escape hatch removes hook execution entirely. ACP can’t intercept what Claude Code doesn’t tell it about. This is a Claude Code design choice — not an ACP bug. Mitigation: detect the flag’s use server-side via the absence of expected hook calls, alert when an agent goes silent.- Tool input mutation is not yet supported. PreToolUse can deny but cannot rewrite the tool input. PostToolUse can flag findings but cannot redact the output. Both are observe-or-block today. SDK adapter (path 3) will add mutation when shipped.
- Subagent attribution is partial. When Claude Code spawns a subagent via the Agent tool with
subagent_type: "Explore", ACP captures the spawn event with the named type. The Explore subagent’s downstream Bash and Read calls show as genericsubagenttier — Claude Code’s hook payload doesn’t propagate parent agent context. We’ve requested this upstream. - Hook timeout is fixed at 4 seconds in govern.mjs. A governance response slower than that is treated as unreachable — allowed-but-ungoverned by default, blocked if you’ve opted into fail-closed.
- No support for offline / air-gapped Claude Code. Hook requires reachable HTTPS to
api.agenticcontrolplane.com. Self-hosted GatewayStack supports air-gapped — setACP_API_BASEenv var to point at your instance.
Troubleshooting
Hook isn’t firing. Restart Claude Code completely (Ctrl+C, then claude --continue). Hook registration is read on session start.
Every tool call is being blocked. You’ve opted into fail-closed (~/.acp/failmode says closed) and ACP is unreachable, or a policy is denying the calls — the block message says which. For credential problems, check ~/.acp/credentials exists and run the installer again to re-authenticate. To restore the fail-open default: echo open > ~/.acp/failmode.
govern.mjs: command not found. The hook calls node $HOME/.acp/govern.mjs. Ensure Node 18+ is on PATH for non-interactive shells (zshrc / bashrc, not just zlogin).
Hook fires but nothing appears in the dashboard. Confirm the workspace tied to your token. The Activity page is workspace-scoped — if you have multiple workspaces, switch in the top-right dropdown.
--dangerously-skip-permissions was used and there’s a gap in the audit log. Expected — see Limitations. A future ACP feature will alert on hook silence relative to expected baseline.
Add the cost X-ray
The hook above governs and audits tool calls — it never sees the model’s token usage, so it can’t show cost. To meter spend, prompt-cache hit rate, and the loop-vs-leaf cost X-ray, route Claude Code’s model calls through the ACP proxy: set ANTHROPIC_BASE_URL=https://api.agenticcontrolplane.com/anthropic/v1 and use your ACP key (gsk_...) as ANTHROPIC_AUTH_TOKEN. Same responses, now metered — and the proxy governs the tool-use blocks the model emits as a second layer.
Full walkthrough: Turn on Cost X-Ray.
Frequently asked questions
How do I get an audit log of everything Claude Code did?
Install the hook (curl -sf https://agenticcontrolplane.com/install.sh | bash) and restart Claude Code. Every tool call — Bash, Edit, Write, WebFetch, MCP — then lands in the Activity log with identity, decision, latency, and, for delegated runs, the full chain provenance.
Can I block specific Claude Code tools or commands?
Yes. Tool policies set a workspace-wide allowlist and required scopes; agent policies restrict by tier (interactive vs subagent vs background); user policies override per identity. A policy deny is returned to the PreToolUse hook and Claude Code blocks the call before it runs. Most-restrictive wins on conflict.
Does --dangerously-skip-permissions bypass the hooks?
Yes — Claude Code’s own escape hatch removes hook execution entirely, so ACP can’t intercept those calls. That’s a Claude Code design choice. The mitigation is server-side: detect the silence of expected hook traffic and alert when an agent goes dark.
What happens if the governance service is unreachable?
The hook fails open by default: the call is allowed and loudly flagged as having run ungoverned, so an outage never bricks your agent. Compliance-sensitive teams can opt into fail-closed (echo closed > ~/.acp/failmode), which blocks calls when the check can’t run. A policy deny always blocks either way.
Can ACP show what my Claude Code sessions cost?
The hook alone can’t — it sees tool calls, not token usage. Route Claude Code’s model calls through the ACP proxy (ANTHROPIC_BASE_URL=https://api.agenticcontrolplane.com/anthropic/v1 with your ACP key as the auth token) and every session is metered, split loop-vs-leaf, with prompt-cache hit rates.
Related integrations
- Which Claude Code tools to deny out of the box — the argued deny list, from a 76-tool live capture
- Cursor — same hook pattern, different config file
- OpenAI Codex CLI — Claude-style hooks, currently Bash-only
- Anthropic Agent SDK — for building your own agents on Claude with full ACP delegation chain support
- Agent-to-Agent governance — how delegation chains carry identity and budget across hops

