CLAIMS: receipts for what AgentFence promises
July 5, 2026 · View on GitHub
AgentFence makes a handful of trust claims. This page turns each one into a reproducer — a command you can run to see the claim hold — plus explicit non-claims so the boundary is clear. Where a claim is a property of the code rather than a single command, the receipt names the file or behaviour to check.
All commands below run against files bundled in this repo. None require network access — which is itself the first claim.
Claim 1 — Local-first, no telemetry
AgentFence evaluates policy entirely on your machine. It opens no network
connection on its own; the only outbound traffic is to endpoints you
configure (--audit-sink <url> for log shipping, --upstream <url> for the
HTTP proxy).
Receipt. Every evaluation command works with networking disabled — there is nothing to phone home to. And the core evaluation packages contain no outbound HTTP/dial calls outside the operator-configured sink and upstream paths:
# Evaluation runs fully offline:
agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl \
--no-interactive --output text
# No hidden network egress in the core paths (returns nothing):
grep -rn "net.Dial\|http.Get\|http.Post\|http.DefaultClient" internal/ \
--include=*.go | grep -v "_test.go" | grep -viE "sink|upstream|httpproxy|oplog"
Claim 2 — Deny by default
With defaults.decision: deny, any tool call that matches no rule is denied. A
misconfiguration fails closed, not open.
Receipt. A tool that appears nowhere in the policy is denied with a
default_decision reason:
$ echo '{"id":"x1","tool":"database.drop_table","arguments":{"table":"users"}}' \
| agentfence check --policy examples/policy.yaml --call /dev/stdin \
--output text --no-interactive
x1 database.drop_table -> deny (no rule for database.drop_table; using default decision)
1 call(s) processed, 0 parse error(s): allow=0 deny=1 ask=0
Claim 3 — ask fails closed when unattended
An ask decision needs a human. With no controlling terminal — CI, a service —
--no-interactive auto-denies rather than silently allowing.
Receipt. github.create_issue is an ask rule in examples/policy.yaml;
non-interactively it becomes a deny:
$ agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl \
--output text --no-interactive
…
call_003 github.create_issue -> deny (non-interactive: ask auto-denied)
Claim 4 — Secrets are redacted in the audit log
Redaction patterns are applied to arguments before they are written to the audit log, so a blocked secret-bearing call does not persist the secret.
Receipt. examples/tool-calls.jsonl includes a write of an
OPENAI_API_KEY=… value to .env. In the audit log the value is redacted:
$ agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl \
--no-interactive --audit-log audit.jsonl --output text >/dev/null
$ grep call_002 audit.jsonl
{…,"call_id":"call_002","tool":"filesystem.write","decision":"deny",…,"arguments":{"content":"OPENAI_[REDACTED:generic_secret_assignment]","path":".env"}}
The raw secret never reaches the log.
Claim 5 — Tamper-evident audit trail
With --tamper-evident, audit events are hash-chained. agentfence audit verify
confirms the chain, and any post-hoc edit is detected and located.
Receipt. Verify an intact log, then tamper with it and verify again:
$ agentfence check --policy examples/policy.yaml --call examples/tool-calls.jsonl \
--no-interactive --tamper-evident --audit-log te.jsonl --output text >/dev/null
$ agentfence audit verify --log te.jsonl
OK: 4 event(s) verified
# Flip a decision in the file, then re-verify (sed -i'' works on GNU and BSD/macOS):
$ sed -i'' 's/"decision":"deny","reason":"tool github.delete_repo/"decision":"allow","reason":"tool github.delete_repo/' te.jsonl
$ agentfence audit verify --log te.jsonl
FAILED: integrity check failed at event 4 (possible tampering)
error: audit verify: audit: event 4: hash mismatch: …
$ echo $?
1
For writer authentication (not just tamper-evidence), sign events with
--sign-key and verify with agentfence audit verify --pubkey — a stronger
claim covered in the audit event schema.
Claim 6 — Policy is validated before use
Invalid policy is rejected with an actionable error, not silently accepted.
Receipt.
$ agentfence validate --policy examples/policy.yaml
examples/policy.yaml: OK
A malformed policy exits non-zero and names the problem (try removing a required field and re-running).
Explicit non-claims
AgentFence is a policy decision point. It is not:
- A sandbox for a malicious MCP server. It gates the calls an agent makes; a compromised server can still misbehave within allowed calls. Combine it with OS/container/network isolation.
- A guarantee that your policy is safe. A wrong or permissive policy is enforced faithfully. Review policies like code (see CONTRIBUTING on security-sensitive paths).
- A cloud gateway or hosted monitoring service. It is a local tool with a local audit log; there is no AgentFence backend.
- A replacement for host, container, or network isolation. It is one layer of a defence-in-depth posture, not the whole of it.
See the threat model for the full scope, trust boundaries, and residual risks.