Observed since 20 Oct, 21:55 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
A runtime security and governance layer that evaluates AI-agent tool requests before they execute.
A runtime security and governance layer that evaluates AI-agent tool requests
before they execute and returns one of three decisions: allow, ask
(require human approval), or block.
The engine is vendor-neutral and dependency-light (just pydantic + PyYAML)
so it can run inside a Claude Code PreToolUse hook with no network and no
database. The same engine can back an MCP gateway or an HTTP service.
It is a policy gate, not a sandbox. It never executes the tool it is judging, and it only reasons about what the integration hands it. Read
docs/limitations.mdfor the honest threat model before you rely on it.
Every request flows through a fixed, security-critical pipeline
(src/agent_firewall/engine.py):
authorization.py)detection/)risk/)allow / approval
/ deny, with the risk score available as a threshold. (policies/)deny > approval > allow. A high score can
only raise scrutiny; it can never rescue a denied request, and a low score
can never override a deny.Each decision is explainable (explanation, matched_policies,
matched_rules, risk_factors) and recorded to a tamper-evident audit log.
Requires Python ≥ 3.11.
pip install -e . # core engine + CLI
pip install -e ".[dev]" # + pytest, ruff, mypy, hypothesis
pip install -e ".[api]" # + optional FastAPI/SQLAlchemy service deps
pip install -e ".[mcp]" # + optional MCP gateway deps
The repo also works without installing: hooks/pre_tool_use.py inserts
src/ onto sys.path itself.
.claude/settings.json already wires the firewall as a PreToolUse hook over
the file, shell, and network tools:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash|Read|Write|Edit|MultiEdit|NotebookEdit|WebFetch|Glob|Grep",
"hooks": [{
"type": "command",
"command": "python",
"args": ["${CLAUDE_PROJECT_DIR}/hooks/pre_tool_use.py"],
"timeout": 10
}]
}]
}
}
Decision mapping to the hook protocol:
| Engine decision | permissionDecision |
|---|---|
BLOCK | deny |
ASK | ask |
ALLOW | deferred to Claude Code's native flow (nothing emitted) unless AGENT_FIREWALL_EMIT_ALLOW=1 |
Deferring on allow means the firewall never disables native permission
controls — it only ever tightens them. Remove the hooks block (or the file) to
disable it entirely.
# Evaluate a synthetic request and print the full decision as JSON
agent-firewall evaluate --tool Bash --input '{"command": "rm -rf /"}'
# Validate a policy file against the schema
agent-firewall policy validate policies/default.yaml
# Inspect / verify the audit log
agent-firewall audit show --limit 20
agent-firewall audit verify
(agent-firewall hook runs the same stdin-driven hook adapter directly.)
The same engine can be exposed as a Model Context Protocol server so any MCP client can consult the firewall before running a tool:
pip install -e ".[mcp]"
agent-firewall mcp # serve over stdio
It registers one tool, evaluate_tool_request, that returns an
allow / ask / block decision for a described request. It is a consultative
gate — the calling agent must honor the decision. See
docs/mcp-gateway.md.
Policies are declarative YAML (policies/default.yaml). Selectors within a
policy are ANDed; lists within a selector are ORed. Effects are allow,
approval, or deny, with deny winning. The bundled set:
block-protected-credentials — never read/write .env, keys, .ssh, .aws, …block-critical-risk — deny anything scoring ≥ 85 (how rm -rf /, mkfs,
fork bombs, secret egress get hard-blocked — the score can't be argued away)require-approval-elevated-risk — score ≥ 50 (< 85) needs human approvalrequire-approval-network-egress — outbound tools (WebFetch) need approvalallow-project-reads — reading non-sensitive files is allowedRead from the environment (src/agent_firewall/config.py):
| Variable | Default | Meaning |
|---|---|---|
AGENT_FIREWALL_POLICY_FILE | bundled policies/default.yaml | policy document |
AGENT_FIREWALL_AUDIT_LOG | .agent_firewall/audit.jsonl | audit log path |
AGENT_FIREWALL_ON_ERROR | ask | fail mode on error: ask / deny / allow |
AGENT_FIREWALL_PROJECT_ROOT | cwd | project boundary for path checks |
AGENT_FIREWALL_ENV | development | environment label |
AGENT_FIREWALL_EMIT_ALLOW | unset | set 1 to emit explicit allow |
Append-only JSONL with a SHA-256 hash chain; audit verify detects truncation
or edits, and concurrent processes serialize appends via a lock file. Request
summaries are secret-scrubbed and raw tool input is never written verbatim. It
is tamper-evident, not tamper-proof — see
docs/limitations.md.
python -m pytest # test suite (unit tests in tests/)
python scripts/smoke_test.py # end-to-end sanity check on synthetic inputs
ruff check . # lint
mypy # strict type-check
All test fixtures and smoke-test inputs use synthetic credentials and safe, non-executed command strings.
src/agent_firewall/
engine.py core evaluation pipeline
authorization.py fail-closed identity/tool gate
normalize.py vendor payload -> NormalizedRequest
models.py typed domain models (pydantic)
config.py env-driven CoreConfig
detection/ command / filesystem / secrets / dlp / prompt-injection
risk/ deterministic additive scorer + weights
policies/ YAML schema, loader, evaluator
audit/ hash-chained JSONL writer + verifier
integrations/claude_code/ PreToolUse hook adapter
integrations/mcp/ MCP gateway server (optional [mcp] extra)
hooks/pre_tool_use.py thin stdin entry point for Claude Code
policies/default.yaml bundled policy set
docs/limitations.md threat model & known gaps
Contributions are welcome — see CONTRIBUTING.md for the dev
setup and the quality gate to run before a PR. To report a vulnerability, use
GitHub private vulnerability reporting as described in
SECURITY.md (please don't file public issues for security bugs).
MIT.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.0 | 10 Oct 2026 | 15 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 21:55 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:6855ee2335534f589646e3b09c4ef0225774a86ace00ed3ddffe44c55943518f