Your agent decides. Parry enforces.
Runtime security for AI agents and coding tools. One binary. Policy-driven enforcement.
Parry sits outside your AI agent. It reads tool-call payloads from stdin, classifies the action by blast radius, and returns allow/block as a small, deterministic decision. It’s designed to be hard to “talk out of” via prompt injection.
AI agents run shell commands, send emails, edit files, and hit APIs on your behalf. When they go wrong (prompt injection, context drift, misconfiguration), the consequences are real.
Many security tools today run inside the agent as natural-language instructions. A prompt injection can override those. Your security skill gets talked out of protecting you.
Parry runs out-of-process. Deterministic rules. The agent doesn't know it's there.
brew install kkd16/tap/parryOr with Go:
go install github.com/kkd16/parry@latestParry reads a tool call on stdin and returns allow/block (and an optional message) via stdout + exit code. If Parry crashes, a non-zero exit code blocks the action (fail-closed).
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' | parry check
# exit code 2 → blocked| Tier | Name | Examples | Default |
|---|---|---|---|
| T1 | Observe | file.list, git.status |
Allow silently |
| T2 | Local Write | file.write, git.branch |
Allow + log |
| T3 | Destructive | file.delete, rm -rf |
Confirm above threshold |
| T4 | External Comms | gmail.send, slack.post, git.push |
Confirm if external |
| T5 | Credential/System | sudo, oauth.token, ssh |
Block by default |
Unknown tools default to T3 (configurable via default_tier).
# ~/.parry/policy.yaml
version: 1
mode: observe # observe | enforce
# In check mode (synchronous), "confirm" rules fall back to this.
# block = fail-closed (default). allow = trust and permit.
check_mode_confirm: block
tiers:
T1_observe: allow
T2_local_write: allow
T3_destructive: confirm
T4_external: confirm
T5_credential: block
rules:
gmail.delete: { tier: T3 }
gmail.send: { tier: T4, block_when: { recipients_count: "> 10" } }
Bash: { tier: T3, allow_list: ["git *", "npm *", "ls *"],
block_list: ["rm -rf *", "sudo *", "curl * | sh"] }
Edit: { tier: T2, block_when: { path_matches: ["/etc/*", "~/.ssh/*"] } }
rate_limits:
- { scope: "gmail.*", max: 50, window: "5m", on_exceed: block_and_alert }
- { scope: "gmail.delete", max: 5, window: "1m", on_exceed: confirm }
- { scope: "Bash", max: 10, window: "1m", on_exceed: block_and_alert }Parry ships a setup command for supported agents.
parry init
parry setup cursorparry init
parry setup claude- Initialize.
parry initcreates~/.parry/policy.yaml(defaults to observe). - Install hooks.
parry setup cursororparry setup claude. - Review. Use
parry reportandparry dashboardto see what’s happening. - Enforce. Set
mode: enforcein the policy when you’re ready.
- Deterministic enforcement. The policy engine makes binary decisions. No LLM in the enforcement path.
- Observe before enforce. New installs start in observe mode. Build trust with data.
- Classify by consequence. The blast-radius tiers classify by what happens if it goes wrong, not what the action is called.
- External enforcement. Out-of-process. The agent can't override it.
- Local-first. All data, models, and enforcement stay on your machine. Nothing leaves.
- Fail closed. If Parry crashes, tool calls are blocked via a non-zero exit code.
| Component | Choice |
|---|---|
| Language | Go 1.26+, single binary, ~15MB core |
| Database | modernc.org/sqlite, pure Go, no CGO |
| Policy | YAML (go.yaml.in/yaml/v4) |
| CLI | alecthomas/kong |
| Dashboard | React (Vite) embedded in binary via embed |
make build # build the binary
make lint # run golangci-lint
make lint-fix # run golangci-lint with auto-fix
make test # run tests with race detector- Keep changes focused and easy to review.
- Vibe coded contributions welcome.