armorgemini
ArmorIQ intent-based security enforcement for the Gemini CLI. Enforces that Gemini declares what it intends to do before doing it: declared
Open source Open in the app JSON README (API)
About
ArmorIQ intent-based security enforcement for the Gemini CLI. Enforces that Gemini declares what it intends to do before doing it: declared plans, intent-drift blocking, policy rules, and audit logging.
Details
- Kind
- Plugins
- Topic
- AI, RAG & memory
- Publisher
- armoriq
- Origin
- gemini
- Category
- ferramentas
- Version
- 0.3.3
- Stars
- 4
- Last push
- 2026-08-22T13:56:15Z
- Repository state
- ativo
- Language
- JavaScript
- License
- MIT
- Added
- 2026-08-30 14:13:39
- Updated
- 2026-08-30 14:13:39
- Origin id
armoriq/armorgemini
README
# ArmorGemini
ArmorIQ intent-based security enforcement plugin for the Gemini CLI. Enforces that Gemini declares what it intends to do before doing it, and every action is checked against that declared intent.
**Status:** v0.3.2. Intent-plan enforcement via a bundled MCP server, local-first policy activation (`/armor:yes` is the only confirmation), backend policy layer on top for org-wide rules. Requires an ArmorIQ API key. See [CHANGELOG](CHANGELOG.md) for what changed.
## Design
ArmorGemini is **backend-authoritative for policy** and **local-first for intent drift**. Every enforcement decision that catches drift fires client-side without waiting on the network; every policy decision flows through the ArmorIQ IAP backend. If the plugin is not configured with an API key, hooks fail closed and every tool call is denied with a clear "not configured" message.
```
User Prompt ──► SessionStart hook (banner: ENFORCING)
│
▼
BeforeAgent hook (inject "declare your plan first" directive)
│
▼
Model calls register_intent_plan (armorgemini-policy MCP tool)
│
▼
BeforeToolSelection hook (no-op today, see Hook lifecycle)
│
▼
Tool Call ──► BeforeTool hook ──► 1. is tool in plan? (drift check, local, no network)
2. is plan still fresh? (TTL)
3. local policy match? (${dataDir}/policy.json, no network)
4. POST /iap/enforce (backend policy check)
│
▼
allow | deny
│
▼
Tool Result ──► AfterTool hook ──► POST /iap/audit (best-effort)
│
▼
SessionEnd hook (clear the plan file)
```
## Install
One-command install (writes global Gemini CLI settings, installs the ArmorIQ SDK/CLI, and prompts you to sign in):
```bash
curl -fsSL https://armoriq.ai/install_armorgemini.sh | bash
```
The installer:
1. Installs `@armoriq/sdk` globally (adds the `armoriq` CLI to your PATH)
2. Downloads the plugin into `~/.armoriq/armorGemini`
3. Wires the six ArmorGemini hooks into `~/.gemini/settings.json`
4. Wires the `armorgemini-policy` MCP server via `gemini-extension.json`
5. Registers the `/armor:*` slash commands in `~/.gemini/commands/armor/`
6. Runs `armoriq login --product armorgemini` which opens your browser, mints an API key, and writes it to `~/.armoriq/credentials.json`
7. Verifies the hooks fire
After that first run there is nothing more to do. The plugin picks up the key from `~/.armoriq/credentials.json` on every subsequent Gemini CLI session.
### Manual credential controls (dev / advanced)
End users should not need these. For local dev or CI:
| Variable | Purpose |
|---|---|
| `ARMORIQ_API_KEY` | Override the credentials.json key. Precedence: env > credentials.json. |
| `ARMORIQ_BACKEND_ENDPOINT` | Override backend URL. Default `https://api.armoriq.ai`. |
| `ARMORIQ_ORG_ID` | Scope the plugin to a specific ArmorIQ org. |
| `ARMORGEMINI_TIMEOUT_MS` | Per-request timeout to the backend (default 8000). |
| `ARMORGEMINI_DATA_DIR` | Where per-session plan files live. Default `~/.gemini/armorgemini`. |
| `ARMORGEMINI_INTENT_REQUIRED` | Set to `false` to disable intent-plan enforcement and fall back to policy-only mode (v0.2 behavior). |
| `ARMORGEMINI_PLAN_TTL_SECONDS` | Age (in seconds) after which a stored plan is treated as stale. Default 600. |
### Reconnecting or switching accounts
```bash
armoriq login --product armorgemini # re-runs the browser auth, overwrites credentials.json
armoriq logout # clears credentials.json
```
## The `armorgemini-policy` MCP server
Bundled with the plugin, declared in `gemini-extension.json` under `mcpServers`. Gemini CLI launches it automatically on session start. Three tools:
| Tool | Purpose |
|---|---|
| `register_intent_plan` | Declare your plan for the current turn. Must be called before any other tool when `ARMORGEMINI_INTENT_REQUIRED=true` (the default). |
| `reset_intent_plan` | Clear the current plan explicitly. The next tool call will be denied until a fresh plan is registered. |
| `get_intent_plan` | Read the currently registered plan for a session. Informational. |
The plan shape:
```json
{
"goal": "One-line summary of the task",
"steps": [
{ "action": "read_file", "description": "Peek at the top of README" },
{ "action": "list_directory", "description": "See what else is in the dir" }
]
}
```
Tools listed in `steps[].action` are allowed for the rest of the turn. Anything else is denied at BeforeTool as intent drift.
## The `/armor` slash commands
Installed alongside the hooks. `/armor:add` and `/armor:template` stage the policy locally with a YAML preview; `/armor:yes` activates it and enforcement kicks in immediately on this session. No dashboard round-trip.
| Command | Purpose |
|---|---|
| `/armor:list` | Show the current active local policy. |
| `/armor:add <verb> <target> [note]` | Stage a rule change (verb: `allow`, `deny`, or `hold`). Shows a YAML preview. |
| `/armor:template <name>` | Stage a named policy template (`lockdown`, `strict-read-only`, `balanced`). Shows a YAML preview. |
| `/armor:yes` | Confirm the currently staged proposal. Writes it to `${dataDir}/policy.json` (`BeforeTool` picks it up immediately) and fire-and-forgets the same policy to the ArmorIQ backend for audit. |
| `/armor:no` | Discard the currently staged proposal. |
| `/armor:help` | Show help. |
Example flow:
```
/armor:add deny web_fetch external network not allowed here
(YAML preview appears, nothing sent anywhere)
/armor:yes
(local policy.json written, enforcement live, backend audit push best-effort)
/armor:list
(shows the new rule)
```
## Hook lifecycle
| Hook | What ArmorGemini does |
|---|---|
| `SessionStart` | Logs session_id, cwd, and configured state. Prints the ENFORCING banner. |
| `BeforeAgent` | Injects a directive telling the model to call `register_intent_plan` (armorgemini-policy MCP) before any other tool. |
| `BeforeToolSelection` | No-op today. Gemini API rejects `allowedFunctionNames` with `mode: "AUTO"`, and `mode: "ANY"` forces tool calls on every turn. Enforcement stays in `BeforeTool`. |
| `BeforeTool` | Layered enforcement: (1) intent-drift check against the registered plan, (2) local policy check against `${dataDir}/policy.json` (source of truth for enforcement, written by `/armor:yes`), (3) backend `POST /iap/enforce` for org-wide policy. Any layer denying → deny. |
| `AfterTool` | Sanitizes input (redacts obvious secret-shaped keys, truncates long strings), then best-effort `POST /iap/audit`. Never blocks. |
| `SessionEnd` | Clears the session's plan file. |
## Tests
```bash
node --test tests/*.test.mjs
```
Tests stub `globalThis.fetch` per case to simulate backend responses (allow, deny, 401, network error), and use a scratch data dir to exercise the intent-plan path without touching real state. No real network is hit.
## Provenance
Ports the ArmorClaude enforcement model to Gemini CLI. Gemini CLI's hook set is a superset of what Claude Code exposes: `BeforeAgent` is the per-turn hook (equivalent of Claude's `UserPromptSubmit`), `BeforeToolSelection` is a bonus tightening layer that Claude Code doesn't have (structurally hides off-plan tools from the model). The plugin bundles a stdio MCP server declared via `mcpServers` in the `gemini-extension.json` manifest, so intent-plan capture works natively without shell-side hacks.
## License
MIT