Policy Rules
Manage the ArmorIQ policy for your workspace from inside Gemini CLI
Policy Rules
Control what tools Gemini can use directly from the CLI. ArmorGemini ships four /armor:* slash commands as Gemini CLI native custom commands, copied by the installer into ~/.gemini/commands/armor/. The commands talk to the ArmorIQ backend, so every change is versioned, signed, and auditable from the ArmorIQ dashboard.
Commands
| Command | Purpose |
|---|---|
/armor:list | Show the current ArmorIQ policy for this workspace |
/armor:add <rule> | Stage a policy rule change (verb + target + optional note) |
/armor:template <name> | Stage a named ArmorIQ policy template (lockdown, strict-read-only, balanced, ...) |
/armor:help | Show the /armor command help |
Staged changes go through the ArmorIQ policy proposal flow. They are not applied instantly, the workspace owner confirms from the ArmorIQ dashboard at https://platform.armoriq.ai. This keeps a single ArmorIQ tenant policy authoritative even when multiple Gemini sessions are running in parallel.
How Rules are Evaluated
Enforcement runs inside the BeforeTool hook. For every tool call:
- ArmorGemini calls
POST /iap/verify-stepon the ArmorIQ backend. - The backend loads the workspace's active policy (via
GET /policies/current), evaluates the tool call against it, and returns a signed verdict. decision: "allow"lets the call proceed.decision: "deny"blocks the call and surfaces the reason to Gemini.
The full policy compilation and CSRG Merkle binding happen on the backend. The plugin does not evaluate rules locally, it forwards the request and honours the answer.
Writing effective policies: block outcomes, not just tools
This is the most important section in this guide. Denying a single tool is necessary but not sufficient because Gemini's planner can often route around a single block by using a different tool to achieve the same outcome.
Real example we saw
> /armor:add deny fetch
Staged. Confirm at https://platform.armoriq.ai (proposal #ap_42).
> Fetch https://example.com and dump it
● register_intent_plan({ steps: ["run_shell", "read_file"] })
● run_shell curl -sS https://example.com > /tmp/page.html
● read_file /tmp/page.html
● Retrieved via curl (fetch is denied by policy).ArmorGemini did exactly what you told it: blocked fetch. But Gemini annotated "fetch is denied by policy" and switched to the run_shell tool with curl. The outcome (fetching the URL) happened anyway.
This is not a bypass. It is a policy authoring problem. The fix is to write broader rules that cover the OUTCOME you actually want blocked.
Pattern 1: Block multiple tools
> /armor:add deny fetch
> /armor:add deny web_search
> /armor:add deny run_shell when args contain "curl"
> /armor:add deny run_shell when args contain "wget"
> /armor:add deny run_shell when args contain "nc "Now Gemini cannot fetch URLs via fetch, cannot search the web, and cannot shell out to curl / wget / nc.
Pattern 2: Match on argument patterns
Rules can match on tool arguments, not just tool names:
> /armor:add deny run_shell when args contain "http://"
> /armor:add deny run_shell when args contain "https://"This blocks any shell command that includes a URL. Covers not just curl and wget but git clone https://..., pip install https://..., anything that touches a URL.
Pattern 3: Block writes outside a sandbox
> /armor:add deny write_file when path not starts with "/tmp"
> /armor:add deny run_shell when args contain "rm -rf"Pattern 4: Sensitive-data classification
ArmorGemini forwards tool arguments to the ArmorIQ data classifier before the call runs:
| Class | What it detects |
|---|---|
| PCI | Credit card numbers (Luhn-validated), card-related keywords |
| PAYMENT | Payment tool names, banking keywords (IBAN, SWIFT, routing) |
| PHI | Health/medical data |
| PII | Personal data, SSN |
Use it to block by data class instead of tool name:
> /armor:add deny * for payment data
> /armor:add deny fetch for PIIIf Gemini tries to send a credit card number anywhere, ArmorGemini detects PCI, matches the rule, and blocks the tool call.
Templates
/armor:template <name> stages a curated policy for common workspace shapes. Each template is a set of rules maintained on the backend so you get updates without editing anything locally.
| Template | What it stages |
|---|---|
lockdown | Deny everything except read_file. Good for review-only sessions. |
strict-read-only | Allow read_file, list_files, grep, deny writes and network. |
balanced | Allow reads and controlled writes to the workspace, deny network egress and destructive shell. |
Templates behave like any staged change, they are not applied until confirmed from the dashboard.
Recommended starter policy
If you want a sensible default that catches the common LLM-routing-around-single-blocks cases, run this at the start of every workspace:
> /armor:add deny fetch
> /armor:add deny web_search
> /armor:add deny run_shell when args contain "curl|wget|nc "
> /armor:add deny run_shell when args contain "http://|https://"
> /armor:add deny run_shell when args contain "rm -rf /"
> /armor:add deny * for payment dataSix rules cover: network egress via dedicated tools, network egress via shell, destructive deletes, and PCI exfiltration. Add more as needed for your workflow.
Where policies live
The active policy lives on the ArmorIQ backend. The plugin fetches it on demand via GET /policies/current and does not persist a local copy. If the backend is unreachable, ArmorGemini fails closed in enforce mode (see Configuration).
Local per-session state (pending plan, session id, audit records queued for flush) lives under:
| Path | What |
|---|---|
~/.gemini/armorgemini/runtime.json | Session state, per-turn intent plans |
~/.gemini/armorgemini/pending-plan.<sessionId>.json | Pending intent plan waiting to be consumed by BeforeTool |
~/.armoriq/credentials.json | ArmorIQ API key (shared with the armoriq CLI) |
The /armor:* commands stage changes but do not confirm them. To see everything you have staged and finalize the change, open the ArmorIQ dashboard at https://platform.armoriq.ai and confirm the proposal there. The confirm step is JWT-authenticated, so it cannot be done from the CLI on purpose.