ArmorClaude LogoArmorClaude
Concepts

How ArmorClaude Works

Architecture, intent enforcement, drift detection, and the hook system

View Source

How ArmorClaude Works

ArmorClaude is a Claude Code plugin that intercepts every tool call via lifecycle hooks. It enforces a simple rule: Claude must declare what it intends to do before doing it.

Architecture

User Prompt
     |
     v
UserPromptSubmit Hook
  Injects directive: "register your plan first"
     |
     v
Claude calls register_intent_plan MCP tool
  Plan validated + stored (local or signed JWT)
     |
     v
Claude calls a tool (Read, Write, Bash, etc.)
     |
     v
PreToolUse Hook (ENFORCEMENT)
  1. Is this tool in the registered plan?
  2. Do the parameters match?
  3. Do policy rules allow it?
  4. Is the intent token still valid?
     |
  ALLOW or DENY
     |
     v
PostToolUse Hook
  Audit log sent to ArmorIQ (if API key set)

Hook Events

ArmorClaude registers 7 lifecycle hooks with Claude Code:

HookWhenWhat ArmorClaude Does
SessionStartSession opensInitialize, show enforcement status
UserPromptSubmitUser sends a promptInject plan directive, handle policy commands
PreToolUseBefore any tool runsEnforce: check plan, policy, token
PostToolUseAfter tool succeedsSend audit log to backend
PostToolUseFailureAfter tool failsSend failure audit log
StopEnd of a turnCheck token expiry
SessionEndSession closesClean up state

Intent Drift Detection

If Claude tries a tool that was not in its declared plan, ArmorClaude blocks it:

ArmorClaude intent drift: tool not in plan (Bash)

This prevents prompt injection from silently steering Claude into unauthorized tool use. Claude sees the denial and either re-registers a new plan that includes the tool, or tells the user it cannot perform that action.

Parameter Enforcement

Plan steps can constrain expected parameters. If Claude's actual tool call parameters do not match the plan's declared inputs, the call is blocked:

ArmorClaude intent mismatch: parameters not allowed for Write

Token Lifecycle

With an API key, ArmorClaude gets a signed JWT from the ArmorIQ backend:

  1. Claude calls register_intent_plan with its tool list
  2. ArmorClaude sends the plan to the backend's token endpoint
  3. Backend returns a signed JWT with a 5-minute TTL
  4. Each tool call is validated against the plan embedded in the token
  5. After 5 minutes, the token expires and Claude must register a new plan

Without an API key, the plan is stored locally with no expiry.

Fail-Closed

In enforce mode (the default), any of these causes a tool call to be blocked:

  • No intent plan registered (and intent is required)
  • Tool not in plan (intent drift)
  • Parameters do not match plan constraints
  • Policy rule denies the tool
  • Intent token expired
  • Internal hook error

In monitor mode, these events are logged but tool calls proceed.

Whitelisted Tools

These tools are never blocked (they are needed for ArmorClaude to function):

  • register_intent_plan Claude needs this to declare plans
  • policy_read / policy_update for policy management
  • ToolSearch / TodoWrite / ListMcpResourcesTool Claude Code internals with no side effects
  • ExitPlanMode plan mode approval

State Management

Hooks are stateless (new Node process per event). All state persists to files:

FileWhatLifecycle
runtime.jsonSessions, tokens, plans, discovered toolsPer-session, pruned after 24h
policy.jsonPolicy rules + version historyPersistent
pending-plan.jsonPlan awaiting consumption by PreToolUseConsumed + deleted on next tool call

Files live in ~/.claude/plugins/data/armorclaude-armoriq/.

No Separate LLM Call

ArmorClaude does not call a separate LLM to generate plans. Claude itself generates the plan as part of its normal reasoning turn, using the session's own credentials. Zero extra cost, zero extra latency.

On this page