AI coding agents are getting access to your filesystem, GitHub, browser, databases, and internal APIs. AgentFence gives you a local policy layer before tool calls happen.
AgentFence is local-first, vendor-neutral, MCP-friendly, and designed for safe defaults. It evaluates tool calls, supports allow/deny/ask decisions, redacts sensitive-looking values in logs, and writes auditable decision records.
AgentFence is in active development. The table below distinguishes what works today from what is planned. Do not assume planned features are usable yet.
| Capability | Status | Where to read more |
|---|---|---|
JSONL batch policy evaluation (check) |
Implemented | Quickstart |
| Allow / deny / ask decisions | Implemented | docs/policy-language.md |
| Path, argument, URL, and shell-command constraints | Implemented | docs/policy-language.md |
| Tool groups and wildcard matching | Implemented | docs/policy-language.md |
Strict policy validation (validate) |
Implemented | Quickstart |
Single-call trace (explain) |
Implemented | agentfence explain --help |
Policy fixture tests (policy test) |
Implemented | examples/policy-tests.yaml |
| Regex-based redaction of audit values | Implemented | docs/policy-language.md |
| Structured output modes (text / json / jsonl) | Implemented | agentfence check --help |
CI gating via --fail-on |
Implemented | agentfence check --help |
| Pre-built release binaries | Implemented | Pre-built binaries |
Interactive TTY approval for ask decisions |
Implemented | Approval and dry-run |
| Approval timeout with default-deny | Implemented | Approval and dry-run |
| Dry-run evaluation mode | Implemented | Approval and dry-run |
MCP stdio proxy (agentfence proxy) |
Implemented | docs/integration-guide.md, docs/architecture.md |
Policy enforcement on intercepted tools/call |
Implemented | docs/integration-guide.md |
| Tamper-evident hash-chained audit logs | Implemented | docs/threat-model.md |
| MCP streamable-HTTP proxy | Planned | docs/architecture.md |
Tool-capable agents are useful, but they can also be risky:
- Prompt injection can trigger unsafe calls.
- Agents may take destructive actions too quickly.
- Sensitive values can leak into logs.
- Teams need an audit trail of what was allowed, denied, or sent for approval.
AgentFence is a practical local control point before execution.
Pre-built binaries for Linux, macOS, and Windows (amd64 and arm64) are
published on the GitHub Releases
page for each tagged release. Each release includes a checksums.txt for
verification. Download the archive matching your platform, extract it, and put
the agentfence binary on your PATH.
go build -o agentfence ./cmd/agentfenceTo embed a release version at build time (compatible with goreleaser):
go build -ldflags "-X main.Version=0.1.0" -o agentfence ./cmd/agentfenceOr via the project Makefile:
make build VERSION=0.1.0Run the built-in demo:
./agentfence demoRun policy checks against example tool calls:
./agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonlGet machine-readable output for CI pipelines:
./agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl --output json
./agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl --output jsonl | jq '.decision'Validate a policy file before use (catches typos and unknown fields):
./agentfence validate --policy examples/policy.yamlcheck exposes three operator controls for the ask decision and for
"evaluate without enforcing" workflows:
--no-interactive— never prompt; auto-deny anyaskdecision. The audit reason isnon-interactive: ask auto-denied. Use this in CI.--approval-timeout <duration>— bound the wait for a y/N response (e.g.30s,2m). On expiry the call is denied with reasonapproval timeout.0(the default) waits forever.--dry-run— evaluate policy and write audit records but never invoke the approver and never propagate a non-zero exit from--fail-on. Each audit record carries"mode": "dry_run"so downstream readers can distinguish simulated decisions from enforced ones. Text output is suffixed with[dry-run].
Typical CI invocation:
./agentfence check \
--policy policy.yaml --call calls.jsonl \
--no-interactive --approval-timeout 30s --fail-on deny,askRun the same input through --dry-run first to see what would happen without
failing the pipeline.
Initialize a starter policy in your current directory:
./agentfence initCheck the installed version:
./agentfence versionRun AgentFence as an MCP stdio proxy in front of any MCP server:
./agentfence proxy \
--policy examples/policy.yaml \
--audit-log audit.jsonl \
-- \
npx -y @modelcontextprotocol/server-filesystem /path/to/workspaceSee docs/integration-guide.md for Claude Code
and VS Code configuration, audit-log inspection, and troubleshooting.
Running the built-in demo against the bundled example tool calls produces the output below. The first line of each pair is the human-readable decision; the second line is the JSONL audit event with secret-looking values redacted.
$ ./agentfence demo
AgentFence demo:
call_001 filesystem.read -> allow (tool filesystem.read matched explicit policy rule)
{"timestamp":"<rfc3339>","call_id":"call_001","tool":"filesystem.read","decision":"allow","reason":"tool filesystem.read matched explicit policy rule","arguments":{"path":"README.md"}}
call_002 filesystem.write -> deny (path ".env" denied by pattern ".env")
{"timestamp":"<rfc3339>","call_id":"call_002","tool":"filesystem.write","decision":"deny","reason":"path \".env\" denied by pattern \".env\"","arguments":{"content":"OPENAI_[REDACTED:generic_secret_assignment]","path":".env"}}
call_003 github.create_issue -> ask (tool github.create_issue matched explicit policy rule)
{"timestamp":"<rfc3339>","call_id":"call_003","tool":"github.create_issue","decision":"ask","reason":"tool github.create_issue matched explicit policy rule","arguments":{"body":"Created by an agent","repo":"dgenio/agentfence","title":"Demo issue"}}
call_004 github.delete_repo -> deny (tool github.delete_repo matched explicit policy rule)
{"timestamp":"<rfc3339>","call_id":"call_004","tool":"github.delete_repo","decision":"deny","reason":"tool github.delete_repo matched explicit policy rule","arguments":{"repo":"dgenio/agentfence"}}
./agentfence check against the example policy and tool calls produces the
same decisions plus a one-line summary:
$ ./agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl
call_001 filesystem.read -> allow (tool filesystem.read matched explicit policy rule)
{"timestamp":"<rfc3339>","call_id":"call_001","tool":"filesystem.read","decision":"allow","reason":"tool filesystem.read matched explicit policy rule","arguments":{"path":"README.md"}}
call_002 filesystem.write -> deny (path ".env" denied by pattern ".env")
{"timestamp":"<rfc3339>","call_id":"call_002","tool":"filesystem.write","decision":"deny","reason":"path \".env\" denied by pattern \".env\"","arguments":{"content":"OPENAI_[REDACTED:generic_secret_assignment]","path":".env"}}
call_003 github.create_issue -> ask (tool github.create_issue matched explicit policy rule)
{"timestamp":"<rfc3339>","call_id":"call_003","tool":"github.create_issue","decision":"ask","reason":"tool github.create_issue matched explicit policy rule","arguments":{"body":"Created by an agent","repo":"dgenio/agentfence","title":"Demo issue"}}
call_004 github.delete_repo -> deny (tool github.delete_repo matched explicit policy rule)
{"timestamp":"<rfc3339>","call_id":"call_004","tool":"github.delete_repo","decision":"deny","reason":"tool github.delete_repo matched explicit policy rule","arguments":{"repo":"dgenio/agentfence"}}
4 call(s) processed, 0 parse error(s): allow=1 deny=2 ask=1
Timestamps are real time.RFC3339Nano values at runtime; they are shown as
<rfc3339> here so this section does not need to be updated on every build.
version: "0.1"
defaults:
decision: deny
tools:
filesystem.read:
decision: allow
filesystem.write:
decision: ask
github.create_issue:
decision: ask
github.delete_repo:
decision: denySee examples/policy.yaml for the full policy including constraints and redaction patterns.
AgentFence is built to reduce practical risks from agent tool calls:
- prompt injection
- confused deputy behavior
- accidental destructive actions
- secret leakage through logs
- excessive default permissions
See docs/threat-model.md for details and
docs/architecture.md for the evaluation flow.
AgentFence is not a sandbox. It enforces policy before a tool call executes; it does not contain a tool call that has already been forwarded. Pair it with OS-level isolation for defense in depth.
AgentFence is the external gate in a layered approach to agent safety:
- AgentFence (this project): a standalone CLI and (planned) MCP proxy that sits outside the agent process. It is configured by an operator and decides allow / deny / ask for each tool call before it reaches the tool server. Use AgentFence when you want a policy layer that does not require modifying the agent or the application embedding it.
- agent-kernel: an embeddable runtime/library layer for applications that build their own agent on top. It runs inside the agent process and is configured by the application author.
The two are complementary. An application can embed agent-kernel for
in-process safety and also run behind agentfence for an operator-controlled
policy boundary. If you only need one, pick AgentFence when the policy author
is not the application author, and pick agent-kernel when you are building
an agent application and want safety guarantees compiled in.
- MCP stdio proxy mode
- MCP streamable HTTP proxy mode
- signed audit logs
- GitHub Action mode for CI policy checks
- reusable policy packs for filesystem, GitHub, browser, database, and shell tools
- VS Code / Claude Code / Copilot CLI integration examples
- Executing real tool calls
- Claiming full MCP proxy compatibility before it ships
- Replacing runtime sandboxing or OS-level isolation
See CONTRIBUTING.md for local-development setup, test
conventions, and PR guidelines. The short version:
make cibefore opening a pull request. CI runs the same command.
Apache-2.0. See LICENSE.