Govern Claude Code with Agentic Control Plane
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
curl -sf https://agenticcontrolplane.com/install.sh | bash
The script detects Claude Code, writes a hook to ~/.acp/govern.mjs, registers it in ~/.claude/settings.json for both PreToolUse and PostToolUse events, and opens your browser to provision a workspace. Restart Claude Code (Ctrl+C then claude --continue) and every tool call is governed.
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. PreToolUse is fail-closed by default (an unreachable ACP blocks the tool call for safety) — a deliberate choice so a downed governance plane doesn’t silently leave tool calls ungoverned.
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. Slower governance backends will 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. Check ~/.acp/credentials exists and contains a valid token. PreToolUse is fail-closed — unreachable ACP or expired credentials block all calls. Run the installer again to re-authenticate.
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.
Related integrations
- 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