source
Ardur Claude Code Plugin
This plugin protects Claude Code at the local tool boundary. `PreToolUse` runs
What it does
This plugin protects Claude Code at the local tool boundary. PreToolUse runs
before a Claude Code tool executes: the adapter loads the active Ardur profile,
maps the tool input to declared telemetry, evaluates the Mission Passport, and
emits a signed receipt. If Ardur denies the call, the hook returns Claude Code’s
current hookSpecificOutput.permissionDecision = "deny" response.
For the one-screen source-checkout walkthrough, including the no-key evidence
harness and live-Claude demo path, start with
docs/guides/claude-code-mvp-quickstart.md
.
On permit, Ardur emits evidence only. It does not return
permissionDecision=allow, so Claude Code’s normal permission prompts and user
approval flow remain in charge.
Easiest Setup
Install Ardur with its Python dependencies, then create a plain Markdown guardrail file:
cd <ardur-repo>
pip install -e python/
ardur profile init --template read-only --path ARDUR.md
Open ARDUR.md in any text editor:
# Ardur Guardrails
Mode: read only
Mission: Review this project without changing files or running commands.
Protect folder: .
Max tool calls: 100
Duration: 1d
## Allow
- Read files
- Search files
## Block
- Run shell commands
- Edit files
- Write files
Turn protection on:
ardur protect claude-code --profile ARDUR.md
Start Claude Code with the exact command printed by Ardur. It includes
VIBAP_HOME=... so the hook can find the active passport and the Python
environment where Ardur is installed. It will look like:
VIBAP_HOME=/path/to/.vibap claude --plugin-dir /path/to/plugins/claude-code
Low-latency PreToolUse path
ardur protect claude-code also tries to build a native PreToolUse daemon
client at $VIBAP_HOME/claude-code-pre_tool_use when a local C compiler is
available. The hook wrapper attempts the fast path first, then falls back to
Python handling when no daemon is listening, the native client is missing, or
the daemon response is invalid.
To use the daemon path, start the daemon from the same Python environment where Ardur is installed before launching Claude Code:
VIBAP_HOME=/path/to/.vibap python -m vibap.claude_code_daemon
By default, the Unix socket lives at:
$VIBAP_HOME/daemon/claude-code-hook-daemon.sock
The daemon creates the daemon/ directory as 0700 and the socket as 0600.
If the socket path already contains a non-socket file, Ardur fails closed rather
than deleting it.
Operational toggles:
ARDUR_CC_HOOK_DAEMON=0disables daemon-first dispatch.ARDUR_CC_HOOK_DAEMON_SOCKET=/path/to/socketuses a custom socket path.ARDUR_CC_HOOK_DAEMON_TIMEOUT_MS=5changes the daemon client timeout.ARDUR_CC_HOOK_STRICT_NATIVE=1forces the wrapper to use only the native client when benchmarking or diagnosing the fast path; do not use it if you want Python fallback behavior.
Claim boundary: the gated release test targets the native daemon-client path.
Shell wrapper latency is recorded as telemetry because /bin/bash startup and
workstation scheduler tails can dominate p95 even when the native hot path is
fast.
Built-In Options
ardur profile init --template read-only: safest first run. Allows reading and searching only.ardur profile init --template safe-coding: allows local file edits inside the selected folder, but blocks shell commands.ardur protect claude-code --scope . --mode read-only: same behavior without a Markdown profile.ardur protect claude-code --scope . --mode safe-coding: flag-based setup for technical users.
Advanced users can still use ardur issue, ARDUR_MISSION_PASSPORT,
ARDUR_CC_HOOK_DIR, and custom Mission Passport fields directly. The Markdown
profile is a friendly layer over the same capabilities, not a replacement.
What happens when a tool is called
PreToolUsefires.- Ardur maps the Claude Code tool input into declared telemetry.
- Ardur checks the active Mission Passport: allowed tools, forbidden tools, resource scope, cwd, and relevant policy backends.
- If permitted, Ardur appends a compliant receipt and lets Claude Code continue its normal permission flow.
- If denied, Ardur appends a violation receipt and returns
hookSpecificOutput.permissionDecision = "deny". PostToolUserecords the SHA-256 digest of permitted tool responses as a chained evidence receipt.SubagentStart/SubagentStoprecord signed lifecycle receipts and append per-trace subagent registry events. Tool receipts are attributed only when Claude Code exposes an exact agent id or when transcript evidence binds one child; otherwise they remain trace-level and are reported as unattributed.
Where receipts live
Receipts are written to:
$ARDUR_CC_HOOK_DIR/<trace_id>/receipts.jsonl
Default when ARDUR_CC_HOOK_DIR is not set:
~/.vibap/claude-code-hook/<trace_id>/receipts.jsonl
Subagent lifecycle observations for the same trace are also written to:
$ARDUR_CC_HOOK_DIR/<trace_id>/subagents.jsonl
Verify a receipt chain
PYTHONPATH=python python3 -c "
from pathlib import Path
from vibap.receipt import verify_chain
from vibap.passport import load_public_key
jwts = Path('~/.vibap/claude-code-hook/<trace>/receipts.jsonl').expanduser().read_text().splitlines()
jwts = [j.strip() for j in jwts if j.strip()]
pk = load_public_key()
verify_chain(jwts, pk)
print('chain ok:', len(jwts), 'receipts')
"
Boundaries
This plugin captures at the tool-call boundary — every Claude Code tool
invocation (Read, Edit, Write, Bash, WebFetch, WebSearch,
Task, MCP tools) is signed and chained.
What is not captured today:
- Side effects of shell commands. A
Bash("rm foo")is recorded as the command string; the actualunlinksyscall is invisible. - Subprocess trees spawned by a tool call (e.g. by
Bash("./run.sh")). - Network connections initiated by tool-spawned processes.
- Filesystem changes outside the typed file tools.
- Provider-side reasoning, hidden state, server-side tool calls — out of scope by definition for any local tool.
The roadmap closes these gaps in phases: v0.2 adds filesystem snapshots,
v0.5 adds Linux eBPF kernel-level capture, v1.0 adds macOS Endpoint
Security Framework. See docs/coverage-map.md
for the full per-tool audit.
Other notes:
- Ardur is not a sandbox. Use it with Claude Code’s normal permissions and OS filesystem controls.
- Codex and Claude Desktop are not first-class in this RC. They remain separate next-cycle integrations.
Troubleshooting
“ardur: no active mission passport found”
Run:
ardur protect claude-code --profile ARDUR.md
Plugin validation fails
Run:
claude plugin validate plugins/claude-code
The plugin should contain .claude-plugin/plugin.json and hooks/hooks.json.
Receipts are not appearing
Confirm python3 is on PATH for Claude Code and that
~/.vibap/claude-code-hook/ is writable.