Local guardrails for AI coding agents, in 60 seconds.
A 1.2 MB binary that sits between Cursor / Claude Code and the real MCP servers your agent talks to (Postgres, GitHub, shell, filesystem...). Inspects every tools/call against 45 adaptive rules, blocks the destructive ones, prompts you for the high-risk ones, and lets the other 98% through. No cloud, no telemetry, Apache 2.0.
Apache 2.0 45 rules · 8 surfaces macOS · Linux · Windows 95 tests · 0 dependencies on the call path
Same shape works for Claude Code (~/.claude/config.json). On the first run from a clean shell, drop --shadow in args to observe-only for a week, then remove it to enforce.
SQL (9):DROP DATABASE, DROP TABLE, TRUNCATE, ALTER … DROP COLUMN, unscoped UPDATE/DELETE, GRANT ALL, REVOKE FROM PUBLIC, Postgres COPY FROM PROGRAM, MySQL LOAD DATA INFILE. Git (5): force-push to protected branches, history rewrites, push --mirror --force, branch -D, checkout .. Filesystem (6):rm -rf /, writes/deletes inside sensitive paths (/etc, ~/.ssh, ~/.aws, /usr/local/bin, …), dd to a block device, find … -delete sweeps, world-writable chmod, recursive chown root. Secrets exfil (3):read-secret + post-to-network in one chain, raw SSH/AWS key reads, kubectl get secret … | base64 -d dumps. Supply chain (2):curl | sh and family, untrusted npm/pip/cargo registries. Shells / Privilege (3): reverse-shell incantations across bash/nc/python/perl/ruby/openssl/socat/PowerShell, sudo-prefixed destruction, setuid grants. Cloud / k8s / Docker (11):aws s3 rm --recursive, RDS deletes without snapshot, terraform destroy -auto-approve, kubectl delete namespace, helm uninstall, docker system prune --volumes. Anomaly (1): destructive-burst detector across the sliding window.
03
LLM-response surface
assistant plan
A second pair of eyes on the assistant's plan text before any tool is invoked, catching cases where the agent describes a destructive step in prose:
llm.suggests_drop_database — assistant proposes a DROP DATABASE.
llm.suggests_force_push — assistant proposes a force-push to a protected branch.
llm.suggests_rm_rf — assistant proposes a recursive delete at root scope.
llm.suggests_curl_pipe_sh — assistant proposes a fetch-and-execute one-liner.
llm.suggests_secret_exfil — assistant describes reading a secret file and piping it to a network endpoint.
Why both surfaces? Because the assistant often writes the plan in plain text before the destructive tool call lands, and a clean catch there is cheaper than catching it at tools/call time.
Adaptive layer (the v0.2 / v0.3 difference)
04
Composite scoring
Every matching rule contributes points. The sum decides the final tier — so three medium signals can stack into a critical, and a single weak match never blocks on its own.
05
Workspace probe
Shield checks for 12 prod-shaped signals (.env.production, kubeconfig, terraform.tfstate, …) at startup. If matched, every decision gets a +1 severity bump for the session.
06
Decision memory
Per-fingerprint append-only log under .aperion-shield/decisions.jsonl. After 3 approvals of the same (rule, argv) shape, demote silently. After a recent deny, escalate.
07
Burst detector
Sliding window across the current process: 5 destructive matches inside 300 s ⇒ workspace marked "burst-in-progress" and every subsequent match gets bumped one tier until the window clears.
CLI cheat-sheet
Flag
What it does
--rules PATH
Custom shieldset YAML. Bundled defaults if omitted.
Auto-deny High-severity instead of prompting. CI / unattended scripts.
--no-workspace-probe
Disable the prod-signal bump.
--no-memory · --no-burst
Disable decision-memory or burst-detector adjustments.
--check
One-shot evaluation mode. Reads JSON-Lines from stdin, prints decisions to stdout. No MCP / IDE needed.
--workspace PATH
Override the probe root (check-mode only). Useful for batch testing.
Test against your own command history
08
Mine your Cursor transcripts, run them through Shield
wide-scale test
Cursor stores every conversation as JSON-Lines under ~/.cursor/projects/**/*.jsonl. The bundled scripts/extract-cursor-corpus.py walks them, pulls every shell command and assistant-text turn, redacts AKIA / sk- / ghp_ / JWT-shaped secrets, dedupes, and emits the --check schema directly:
# Mine your history, evaluate every command, surface non-allow decisions.
python3 scripts/extract-cursor-corpus.py --shell-only \
| aperion-shield --check --no-memory --no-burst \
| jq -c 'select(.decision != "allow")'# Save the corpus for re-use; run again later after editing rules.
python3 scripts/extract-cursor-corpus.py --shell-only --out my-history.jsonl
aperion-shield --check --rules my-shieldset.yaml < my-history.jsonl > decisions.jsonl
The 13k-command corpus that drove the v0.3 rule-quality pass was harvested with this exact tool. No data leaves the machine.