Policy Rules
Control what tools Claude can use with allow/deny/hold rules managed entirely from the chat prompt
Policy Rules
Policy rules control what tools Claude can use. Rules are evaluated before intent plans - a denied tool stays denied even if it appears in the plan. All policy changes are human-only: Claude can read policy through MCP but cannot update it.
How Rules Work
- Rules evaluate top-to-bottom in statement order
- First matching rule wins (
allow,deny, orhold) - If no rule matches, the default decision applies (configurable per policy)
- New rules are added at the top, giving them highest priority
Rule Effects
| Effect | Keyword | What happens |
|---|---|---|
| Allow | allow | Tool call proceeds |
| Deny | deny, block | Tool call is blocked before execution |
| Hold | hold, require_approval, ask before | Tool call pauses for user approval via Claude Code's native UI |
Adding Rules
Use /armor policy add to stage a rule change. All changes go through a stage → confirm workflow:
/armor policy add deny WebFetchArmorClaude responds with a diff showing what will change. Then confirm:
/armor yesMultiple Rules at Once
Comma-separate different effects:
/armor policy add allow Read and Grep, deny Write, hold BashThis stages all three rules in one proposal. Confirm with /armor yes.
Tool Name Resolution
You can use exact Claude Code tool names (Read, WebFetch, Bash) or natural language:
| You type | Resolves to |
|---|---|
read, read files | Read |
shell, terminal | Bash |
fetch web, web fetch | WebFetch |
search web, web search | WebSearch |
sub agent, subagent | Agent |
file search | Glob |
code search | Grep |
multi edit, multi-edit | MultiEdit |
* | All tools (wildcard) |
Setting the Default Decision
The default decision applies when no rule matches a tool call:
/armor policy default denyOptions: allow, deny, hold.
Removing Rules
Remove by rule ID (shown in /armor policy list):
/armor policy remove stmt_1Clear all rules:
/armor policy resetBoth go through the stage → confirm workflow.
Policy Templates
Apply a pre-built policy in one command:
/armor policy template balanced| Template | Description |
|---|---|
all-allow | Everything permitted - intent planning still enforced |
strict-read-only | Only Read/Grep/Glob allowed. Everything else denied |
balanced | Read allowed. Bash/Write/Edit require approval |
lockdown | All tools require approval. Nothing auto-allowed |
Data Classification
ArmorClaude auto-detects sensitive data in tool arguments:
| Class | What it detects |
|---|---|
| PCI | Credit card numbers (Luhn validation), card-related keywords |
| PAYMENT | Payment tool names, banking keywords (IBAN, SWIFT, routing numbers) |
| PHI | Health/medical data identifiers |
| PII | Personal data, SSN patterns |
When sensitive data is detected, rules with data class conditions are evaluated. For example:
/armor policy add deny * for payment dataThe Stage → Confirm Workflow
Every policy mutation goes through staging. This prevents accidental changes:
- You type a command (e.g.,
/armor policy add deny Bash) - ArmorClaude stages the proposal and shows a diff
- You review and either confirm or cancel:
| Action | Command |
|---|---|
| Apply staged change | /armor yes or /armor policy confirm |
| Apply by proposal ID | /armor policy confirm pol_abc12345 |
| Discard staged change | /armor no or /armor policy cancel |
Staged proposals expire after 30 minutes if not confirmed.
Proposals include a base version check. If someone else changes the policy between staging and confirming, the confirm is rejected to prevent conflicts.
Draft Workflow (Advanced)
For complex policy edits, use the draft workflow:
Validate Pasted JSON
/armor policy draft validate {"schemaVersion":"armor.policy.v1",...}Validates the JSON against the policy schema and creates a draft with a draft_XXXXXXXX ID.
Edit an Existing Draft
/armor policy draft edit draft_abc12345 {"schemaVersion":"armor.policy.v1",...}Replaces the draft's JSON with new validated content.
Revise a Draft
/armor policy revise draft_abc12345 "remove stmt_2"Applies a deterministic edit to a draft (e.g., removing a statement).
Stage a Draft
/armor policy stage draft_abc12345Moves a validated draft into the staging area for confirmation.
Policy IR Format
Policies use the armor.policy.v1 schema internally:
{
"schemaVersion": "armor.policy.v1",
"kind": "PolicyProfile",
"metadata": { "name": "my-policy", "description": "..." },
"defaults": {
"decision": "deny",
"conflictResolution": "deny_overrides"
},
"statements": [
{
"id": "stmt_1",
"effect": "permit",
"principal": { "type": "agent", "id": "claude-code" },
"action": { "type": "tool", "eq": "Read" },
"resource": { "type": "workspace", "scope": "current" },
"conditions": []
}
]
}Statement Fields
| Field | Description |
|---|---|
id | Unique identifier (e.g., stmt_1) |
effect | permit, forbid, or require_approval |
principal | Who the rule applies to (agent, user, org, role) |
action | Tool matcher - eq for exact, in for list |
resource | Scope (workspace, file, directory, network) |
conditions | Additional constraints (see below) |
Condition Operators
| Operator | Example | Description |
|---|---|---|
eq | bash.program eq "curl" | Exact match |
in | tool.name in ["Read", "Grep"] | Value in list |
not_in | bash.program not_in ["rm", "curl"] | Value not in list |
matches | file.path matches "*.secret" | Pattern match |
not_matches | file.path not_matches "*.log" | Negative pattern |
starts_with | file.path starts_with "/etc" | Prefix match |
within_workspace | file.path within_workspace true | Within project dir |
Condition Fields
| Field | Description |
|---|---|
tool.name | Claude Code tool name |
bash.program | Program being run in Bash (e.g., curl, rm) |
bash.raw | Raw bash command string |
bash.hasWriteRedirection | Whether command writes to files |
file.path | File path being accessed |
network.host | Network host being contacted |
mcp.server | MCP server name |
Viewing and Exporting Policy
| Command | What it shows |
|---|---|
/armor policy list | Human-readable summary with rule IDs |
/armor policy view | Raw policy JSON |
/armor policy export | Full policy state JSON (including version history) |
Crypto Policy Binding
When connected to the ArmorIQ backend, policies are cryptographically bound using Merkle trees. After changing policy, ArmorClaude automatically reissues the crypto binding. To manually rebind:
/armor policy rebindThis ensures the policy hash in the signed intent token matches the active policy.
Where Policies are Stored
Policies persist across sessions in the plugin data directory:
cat ~/.claude/plugins/data/armorclaude-armoriq/policy.jsonThey survive restarts, plugin updates, and re-installs.