# Claude Code Audit Log, Hook & Governance — Install Guide

Install ACP's PreToolUse and PostToolUse hooks in Claude Code. Every Bash, Edit, and MCP tool call is logged, scoped, and auditable. One-line install.

# Claude Code Audit Log, Hook & Governance — Install Guide

<p style="font-size:17px;line-height:1.6;color:var(--acp-text);max-width:660px;margin:8px 0 6px;">See, control, and price <strong>every tool call Claude Code makes</strong> — Bash, Edit, Write, WebFetch, MCP, and the model calls behind them — from one dashboard. One command, no code changes.</p>

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







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](https://docs.claude.com/claude-code/hooks) 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](https://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 →](/blog/claude-code-dangerously-skip-permissions)
- **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](/cost-tracking).

## Frequently asked questions



## Related integrations

- [Which Claude Code tools to deny out of the box](/blog/which-claude-code-tools-to-deny-out-of-the-box) — the argued deny list, from a 76-tool live capture
- [Cursor](/integrations/cursor) — same hook pattern, different config file
- [OpenAI Codex CLI](/integrations/codex) — Claude-style hooks, currently Bash-only
- [Anthropic Agent SDK](/integrations/anthropic-agent-sdk) — for building your own agents on Claude with full ACP delegation chain support
- [Agent-to-Agent governance](/agent-to-agent) — how delegation chains carry identity and budget across hops

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "Install Agentic Control Plane in Claude Code",
  "description": "Install ACP's PreToolUse and PostToolUse hooks in Claude Code for governance and audit.",
  "totalTime": "PT1M",
  "step": [
    {"@type": "HowToStep", "name": "Run the installer", "text": "curl -sf https://agenticcontrolplane.com/install.sh | bash . On Windows PowerShell: irm https://agenticcontrolplane.com/install.ps1 | iex"},
    {"@type": "HowToStep", "name": "Authenticate", "text": "Your browser opens to provision a workspace. The API key is saved to ~/.acp/credentials. Free up to 5 agents, and you get team policy plus the cost X-ray."},
    {"@type": "HowToStep", "name": "Restart Claude Code", "text": "Ctrl+C then run `claude --continue`. The hook is now active for every tool call."},
    {"@type": "HowToStep", "name": "(Optional) Fully on-device instead", "text": "Add --local to the installer for no account at all: decisions run locally from ~/.acp/policy.json and are logged to ~/.acp/audit.jsonl. Nothing leaves your machine, so there is no shared activity log or cost X-ray. You can re-run without --local any time to upgrade."}
  ]
}
</script>

