Two engines, kept separate
Prompts guide an agent; they do not hold when the agent is being manipulated. So the repo splits the job.
The Brain: in-context skills
Three optional SKILL.md packages: safe-agent-coder (blast-radius tiers, tests before claims), untrusted-data-guard (treat fetched text as data, keep the Rule of Two) and governance-auditor (self-audit against the matrix). Guidance only. Nothing here is enforced.
The Brakes: runtime hooks
A PreToolUse hook inspects each call and exits 2 to block, 0 to allow. Any crash, bad config, missing pack or changed policy also exits 2. A PostToolUse hook keeps a hash-chained audit log.
What the hook checks
35 rules in five swappable policy packs, across four tiers. Rules read the decoded tool input, not just the raw JSON, and normalise quotes, zero-width characters, SQL comments and whitespace first.
| Tier | Examples | Why it can be decided per call |
|---|---|---|
| Global (10) | API keys, private-key headers, SSNs, Luhn-valid card numbers and SINs | A secret in an argument is visible before it is written or sent. |
| Path | Your denylisted folders, the approval secret and receipt, and writes to guardrails config, packs, source and harness settings | The target path is in the call. |
| Command (20) | terraform destroy, DROP TABLE, force-push, rm -rf of / or ~, curl | sh, reverse shells, upload commands | The command text is the call. |
| Outbound (5) | Personal finance, health or housing terms in Gmail or Buffer-style publishing tools only | Applies only to tools that leave your machine. |
Every rule is tested in pairs: what must be blocked and what must still be allowed.
| Blocked | Still allowed |
|---|---|
block git -C . push origin main (needs approval) | allow git commit -m "fix push notification bug" |
block rm -r -f ~ | allow rm -rf ./node_modules |
block kubectl --context prod delete ns payments | allow kubectl delete pod web-1 --namespace dev |
block python3 <<PY import urllib.request … | allow node build.js --env=prod --target internet |
The approval gate
git push and git remote add are held until a human mints a receipt from an interactive terminal. Receipts are HMAC-signed, single-use, scoped to one action and capped at 60 minutes. A receipt clears only the rule it was minted for, so git push && rm -rf ~/ is still blocked. Force-push, terraform destroy and the other destructive commands have no approval path at all.
python3 guardrails.py approve --scope git_remote_push --ttl 15 # you, in your own terminal
Scope: what a tool-call hook can decide
The MIT AI Risk Repository has 24 risk subdomains. It does not say which a runtime hook can decide, so the repo publishes its own row-by-row classification and labels it as such.
Matrix rows without enforcing code are marked "not enforced". See the classification and framework mapping.
A tripwire, not a sandbox
Read this before relying on it. The full list is in limitations.md.
- An agent running as your own OS user can still approve its own actions (by faking a terminal or writing its own code). The gate catches accidents; a real boundary needs the secret kept away from that user.
- Pattern matching misses shell indirection: variables,
cdthen a relative path, globs. - An agent able to rewrite the engine's source from a shell can remove the checks. Install the clone where the agent's user cannot write.
- The audit log's hash chain detects naive edits only. Ship records somewhere the agent cannot write.
- For real isolation use a rootless container, a VM or a separate user.
How it was checked
The first draft went through two adversarial review rounds (AI-assisted red-teaming with working payloads against the real code). Each finding became a permanent test.
- Round one found that the agent could approve itself, that a valid receipt skipped every later check, and that sixteen of eighteen framework references pointed at unrelated entries.
- Round two found two back doors added by the first fix, config that an agent could rewrite from its working directory, and a risk-taxonomy table with subdomains that do not exist.
- Fixes include a policy digest pinned at install time that fails closed on any edit, and every framework ID re-checked against the source register.
Quick start
Run these in your own project. Re-run install-hooks after any edit to guardrails.json or a pack, or every tool call fails closed.
git clone https://github.com/Charitarth1/agent-guardrails.git
cd your-project
python3 ../agent-guardrails/guardrails.py init # creates ./guardrails.json
python3 ../agent-guardrails/guardrails.py install-hooks # wires .claude/settings.json + AGENTS.md
python3 ../agent-guardrails/guardrails.py vendor-skills --target claude-code # optional skills
# restart Claude Code, then:
python3 ../agent-guardrails/guardrails.py check --tool Bash --payload "terraform destroy"