How ArmorClaude Works
Architecture, intent enforcement, drift detection, and the hook system
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:
| Hook | When | What ArmorClaude Does |
|---|---|---|
| SessionStart | Session opens | Initialize, show enforcement status |
| UserPromptSubmit | User sends a prompt | Inject plan directive, handle policy commands |
| PreToolUse | Before any tool runs | Enforce: check plan, policy, token |
| PostToolUse | After tool succeeds | Send audit log to backend |
| PostToolUseFailure | After tool fails | Send failure audit log |
| Stop | End of a turn | Check token expiry |
| SessionEnd | Session closes | Clean 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 WriteToken Lifecycle
With an API key, ArmorClaude gets a signed JWT from the ArmorIQ backend:
- Claude calls
register_intent_planwith its tool list - ArmorClaude sends the plan to the backend's token endpoint
- Backend returns a signed JWT with a 5-minute TTL
- Each tool call is validated against the plan embedded in the token
- 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_planClaude needs this to declare planspolicy_read/policy_updatefor policy managementToolSearch/TodoWrite/ListMcpResourcesToolClaude Code internals with no side effectsExitPlanModeplan mode approval
State Management
Hooks are stateless (new Node process per event). All state persists to files:
| File | What | Lifecycle |
|---|---|---|
| runtime.json | Sessions, tokens, plans, discovered tools | Per-session, pruned after 24h |
| policy.json | Policy rules + version history | Persistent |
| pending-plan.json | Plan awaiting consumption by PreToolUse | Consumed + 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.