Skip to content
Agentic Control Plane

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

macOS · Linux · WSL
curl -sf https://agenticcontrolplane.com/install.sh | bash
Windows · PowerShell
irm https://agenticcontrolplane.com/install.ps1 | iex
A real recording, nothing mocked. Note the middle: every file the one command wrote, on camera. Piping a script to 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:

ACP tool-surface control table: every tool the agent can call, Allow/Deny per tool
ControlEvery tool Claude Code can call, one row each — Allow / Deny per tool. Deny-by-default on rm -rf, force-push, and the rest. Enforced at the call, outside the model.
ACP cost-per-run distribution for one agent across many runs, log scale
PriceRoute model calls through the proxy and every one is metered — each run split loop-vs-leaf and plotted across runs, so you see which step is the bill and which runs blow the budget.
ACP session trace: per-call timeline of one agent run with cost and decision
AuditEvery tool + model call in a run, in order — what ran, what it returned, the policy decision, and what it cost. Exportable.

See your first governed call →Free up to 5 agents · 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.

By default the installer opens your browser to provision a workspace and routes decisions through the ACP API, so every call lands in one activity log with the cost X-ray alongside it — free up to 5 agents. That is the mode to want: policy you can change in one place, and a record you can actually read.

Prefer fully on-device? Add --local — no browser, no account, nothing leaves your machine. Decisions are made 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. The trade is that the log stays on that one machine: no shared activity view, no cost X-ray, and nothing to compare across agents. 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_tier to https://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 says decision: deny, the hook prints permissionDecision: deny and 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:

  1. Tool policies (Policies → Tool Policies) — workspace-wide tool allowlist and required scopes. Blocks github.repos.delete for everyone? Set it here.
  2. 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.
  3. 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-permissions removes the prompts, not the hooks. The flag puts the session in bypassPermissions, which auto-allows everything Claude Code’s own permission system would have asked about. PreToolUse hooks still fire (the hook even receives permission_mode: "bypassPermissions"), and an ACP deny still blocks the call. What you lose is the human in the loop, so the policy you wrote beforehand is the only thing standing. What the flag actually turns off →
  • 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 generic subagent tier — 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. For an offline machine, install with --local: rules are evaluated on the machine and nothing is sent.

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. Not expected: hooks fire in bypass mode too. A gap means the hook itself wasn’t invoked. Check that the PreToolUse entry is still in ~/.claude/settings.json and that ~/.acp/govern.mjs exists.

Add the cost X-ray

The hook sees tool calls, not tokens — but Claude Code hands it the session transcript on every PostToolUse, and the transcript records each model turn’s input, cache-read, cache-write and output tokens with the model. The installed hook reads the turns it hasn’t reported yet and sends them along with the tool-output report it already makes; ACP prices each turn at list rates, cache economics included, and writes it beside the tool calls. So a plain claude session gets the cost X-ray with no launcher and no env var, and it works on a Max subscription (the figures are what the same tokens cost through the API, not a charge).

Two things still want the proxy path: policy on the model call itself (denied tool_use blocks stripped before the harness sees them) and the declared tool surface, which is captured from request bodies. For those, set ANTHROPIC_BASE_URL=https://api.agenticcontrolplane.com/anthropic with your ACP key (gsk_...) as ANTHROPIC_AUTH_TOKEN, or launch with claude-acp, which the installer writes.

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?

No. The flag suppresses the interactive permission prompts, not the hook subsystem: PreToolUse hooks fire in every permission mode, and an ACP deny still blocks the call. What the flag removes is the human confirmation step, so the deny rules you wrote beforehand are the whole boundary.

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?

Yes, from the hook alone. Claude Code hands the PostToolUse hook the session transcript, which records every model turn’s tokens and model; the hook reports the turns it hasn’t seen yet and ACP prices them at list rates, cache reads and writes included. No launcher, no env var, works on a Max subscription (figures are API-rate equivalents, not charges). The proxy path (ANTHROPIC_BASE_URL or the claude-acp launcher) is still there for model-call policy and the declared tool surface.