The policy decision point for AI agents. Apache-2.0.
Tool Guard Core is the decision engine for AI agent actions. Send it a
tool call — POST /evaluate with a JSON ActionEnvelope, or call the
Go library in-process — and it returns allow / deny / escalate, the
exact rule that fired, and the reason, in microseconds. Every decision
is appended to a SHA-256 hash-chained audit log you can verify offline
with tg verify and stream into standard log pipelines with tg export.
Core decides and records; your tool-execution layer enforces. When the
answer is deny, you don't run the call — one if statement at the
point where your agent executes tools, with working patterns for MCP,
LangChain, AutoGen, and native Anthropic/OpenAI tool-use loops in
docs/integration.md. This repo is the complete
engine: policy evaluation, the SQL / path / shell / write / HTTP / LLM
classifiers,
the /evaluate service, the Go library, and the audit primitive.
Nothing in it is gated.
The native protector currently supports Claude Code 2.1.139 or newer. It previews the exact
settings change, preserves unrelated hooks, installs a secure starter policy,
and does nothing until -apply is supplied:
git clone https://github.com/dimaggi-ai/tool-guard-core
cd tool-guard-core
make build
./bin/tg protect claude # dry-run: inspect the proposed settings.json
./bin/tg protect claude -apply # back up, install, and enable enforcement
./bin/tg status claudetg unprotect claude previews rollback; add -apply to remove the managed
hook. The first pristine backup is never overwritten, and post-install user
changes are preserved by targeted removal. See the protection guide.
The starter policy matches the shape of a destructive command (an rm plus
recursive/force flags and a root/home target), rather than one brittle string.
The lower-level hook and reference adapters for Codex and Antigravity remain in
examples/coding-agent-guard/. Want a
different policy? Pass -policy /absolute/path/policy.yaml, or browse the
example bundles for ready-made guards (money, database,
customer-data export, content safety).
Model-layer guardrails govern what a model says. Tool Guard governs what an agent does. Refund over $500, export PII, transfer funds, drop a table — these are policy decisions, not language decisions, and they need a dedicated decision point: evaluated before the API executes, with a tamper-evident, offline-verifiable record of who decided what and why. Core is that decision point.
Every envelope is evaluated against operator-authored YAML policies: deterministic rules (thresholds, regex, scope, condition trees) plus six semantic classifiers — SQL (postgres / mysql / sqlite / mssql), path (traversal, symlink, shell-meta), shell (env-rewrap, argv resolution), write (file-write path/size/content governance), HTTP (egress host/scheme/method/port governance), and a local-LLM content classifier (Gemma 4 via Ollama, fail-closed). The strongest effect wins; the result is the decision, the rule, the reason, and a citation back to the source document the rule came from. Stage policies in shadow mode against live traffic, then promote to enforcement without redeploying agents.
Not every action carries the same risk when an agent gets it wrong. Reading
a record is free to retry; wiring money, dropping a table, deploying to
production, or deleting an account cannot be taken back. Core classifies
every tool call by whether its visible structure appears undoable and provides
an irreversibility floor: calls classified irreversible or unknown are
not silently allowed by the reference policy.
engine.ClassifyReversibility is deterministic (no network, no LLM). It
derives a class from the tool name/group and the shape of the parameters,
reusing the existing destructive-operation detectors (the four-dialect SQL
classifier, the quote-aware shell tokenizer, HTTP method):
| Class | Meaning | Examples |
|---|---|---|
reversible |
no lasting effect, or a one-step undo at no cost | read / list / get, add-label, create-draft, HTTP GET, SELECT |
recoverable |
undoable only with effort / a restore window | file overwrite, UPDATE, DELETE ... WHERE, rm file |
irreversible |
no ordinary undo path | payments / wire / transfer / refund, deploy / publish, account or data destruction, physical actuation, DROP/TRUNCATE/unscoped DELETE, rm -rf |
unknown |
not recognized or not provably undoable — fail-safe, treated as caution-worthy | an unrecognized tool; generic HTTP POST/PUT/PATCH/DELETE |
The class is exposed as the ordinary condition field reversibility, so any
rule can gate on it:
conditions:
field: reversibility
operator: eq
value: irreversible
effect: escalate # human-in-the-loop; an irreversible action is never auto-allowedThe shipped policies/irreversibility_floor.yaml
is an approved, enforcement-mode reference policy that escalates every
irreversible and every unknown action to a human reviewer (mapped to
EU AI Act Article 14 — human oversight), and allows reversible and
recoverable ones. Escalating unknown is the fail-safe that makes this a
floor: an action the classifier cannot positively recognize as safe is never
waved through to the engine's default-allow, so novelty or obfuscation
escalates rather than slipping past a gap in name/parameter coverage.
The parameter-shape detectors are adversarially hardened: destructive commands
hidden behind wrappers (sudo/env/timeout … rm -rf), command substitution
(echo $(rm -rf /data)), and output redirection to a device (… > /dev/sda)
are recognized; SQL is analysed per-statement after comment and string-literal
content is neutralized, so a data-modifying CTE, an unscoped UPDATE, a
tautological WHERE 1=1, a WHERE (or a destructive keyword) hidden inside a
quoted value, or a sibling statement's WHERE cannot disguise a whole-relation
write.
What it does not do (deliberate, structural limits — the classifier reads
call structure, not world state): it does not inspect an HTTP URL's path, so
generic mutating HTTP methods are unknown and escalate; even a nominally safe
GET can have a badly designed irreversible endpoint; it cannot see downstream
side effects (a scoped DELETE that fires a settled-payment trigger looks
recoverable); and parameter-less calls still depend on the declared tool
name/group. Explicit command/cmd, SQL, argv, and HTTP surfaces are parsed
regardless of a misleading tool name. These remain false-negative surfaces of
pre-execution classification. The class is a necessary structural gate, not a
proof of safety.
The SQL and shell parameter analysis is deliberately conservative and has been
hardened against a broad set of obfuscations (comment and literal injection in
four quoting styles, nested comments, no-semicolon T-SQL batches, tautological
WHERE predicates, command wrappers, substitutions, device-target writes), but
it is a heuristic, not a full multi-dialect parser: a sufficiently exotic
always-true predicate or an unlisted write-capable program can still be
mis-graded. The design leans on two backstops rather than on exhaustive
recognition: an unrecognized program or statement is unknown (escalated), and
the recommended deployment keeps a raw shell out of the write-capable policy
scope entirely. Treat the classifier as defense-in-depth, not the sole control.
Core is the decision point. Tool Guard Enterprise
(dimaggi.ai/products) is the enforcement
point: an MCP gateway your agents connect to as their single access
point. It fronts your MCP servers and tools, calls this engine on every
tool call, forwards allowed calls upstream and blocks denied ones —
enforcement the agent cannot route around. Enterprise also adds the
management plane: audit dashboard and signed Evidence Packs
(Postgres-backed), per-agent identity + RBAC, schema-driven PII
redaction, and policy lifecycle approvals. The decision engine, the
classifiers, and the audit primitive (hash-chained JSONL + offline
tg verify) live in this repo under Apache-2.0 with no usage limits;
Enterprise consumes them — it does not replace them, and this repo does
not depend on it.
Ships in this repo:
- Deterministic policy evaluation (thresholds, regex, scope, condition trees)
- Shadow (observe-only) and enforcement modes — stage a policy against live traffic, then promote it to returning real deny/escalate decisions for your tool layer to act on, without redeploying agents
- SHA-256 hash-chained audit log — a portable JSONL file you download, verify offline with
tg verify, and use as source evidence in an audit workflow; current records identify their engine version, canonical schema, and normalized policy set by hash - Policy lint warnings (
tg lint) tg-proxyHTTP service with policy reload, rate limit, escalation- Four-dialect SQL classifier (postgres / mysql / sqlite / mssql)
- Path classifier (traversal, symlink, shell-meta defence)
- Shell classifier (env-rewrap detection, argv path resolution)
- Write classifier (file-write path allow/deny-lists, byte ceiling, denied-content regex)
- HTTP classifier (egress host/scheme/method/port allow/deny-lists)
- LLM content classifier for image, audio and text generation prompts, using Gemma 4 via Ollama or a TypeSafe System One model such as Jev (text only)
- Reversibility classifier + irreversibility floor — deterministically class every call reversible / recoverable / irreversible / unknown, and gate irreversible actions to human oversight (
policies/irreversibility_floor.yaml) tg coverage— measures what fraction of an agent's tool calls have any governing policy- Battle-test harness (
cmd/battle-test)
Not in this repo — ships in Tool Guard Enterprise (the commercial, self-hosted enforcement gateway + management product; see dimaggi.ai/products):
- MCP gateway / single point of access — agents connect to one endpoint; Enterprise calls Core on every tool call and forwards allowed calls upstream, blocking denied ones before they reach the tool (turnkey, unavoidable enforcement — the agent has no other path)
- Management dashboard with policy lifecycle, RBAC + per-agent API keys, HMAC-signed decisions, schema-driven PII redaction, signed Evidence Pack export (HIPAA §164.312 control mapping — not a certification), webhooks, stateful per-agent budgets, multi-tenancy, AI counter-agent review.
Not present in either edition:
- Multi-model ensemble voting, voice-print matching, managed hosting. See Known limitations for the full list and what each would take to build.
The included cmd/battle-test drives a local LLM (Gemma 4 e4b via
Ollama) against the engine; the resulting block-rate table shows which
adversarial classes the deterministic engine catches and which it does
not. See docs/battle-test-results.md.
git clone https://github.com/dimaggi-ai/tool-guard-core
cd tool-guard-core
make build # → bin/{tg, tg-proxy, battle-test, example-chain}
make test # unit + golden tests with -race
make integration # CLI + HTTP-proxy contract tests
make sample # run the end-to-end sample app
make cover # statement coverage per package
make help # see all targetsmake sample # hand-coded Go agent (no LLM required)
make sample-ollama # real LLM (Gemma 4 via Ollama) drives the agent
make sample-postgres # dockerized: real LLM attacks a real PostgresThe first two spin up a fake refund tool on :18080, tg-proxy on
:19090, load the strict refund policy, run an agent against the
proxy, and print the live allow/deny flow. Then tg verify re-walks
the audit chain offline, demonstrating it is tamper-evident.
examples/sample-app/— 4 hardcoded scenarios. Fastest path to seeing the integration shape end-to-end. No model required.examples/ollama-agent/— real Gemma 4 reasoning. The model emits its own tool calls, gets blocked by policy, adapts, and gets through. Requires Ollama withgemma4:e4bpulled.examples/postgres-attack/— five-container compose: a Postgres instance, a db-tool, an os-tool,tg-proxy, and a Gemma 4 agent with an aggressive prompt that tries to drop production tables. tg-proxy returns a deny decision, the db-tool wrapper refuses to run the call, and the database is untouched. SQL / shell / path classifiers in action.examples/finance-cfo/— CFO control plane: purchase-order, wire-transfer, and expense-velocity policies covering per-action caps, cumulative spend ceilings, approved-vendor allowlists, OFAC-jurisdiction deny, and free-text consistency tripwires. 18 deterministic assertions; no LLM, no Docker.examples/business-ops/— customer-data-export (GDPR/CCPA shape), mass-communication (blast radius + compliance content gates), and support-credit policies. 26 deterministic assertions; no LLM, no Docker.examples/content-gen/— multimodal content-safety bundle: image, audio, and text generators gated by regex prefilter + a Gemma 4 (via local Ollama) semantic classifier. Catches CSAM, deepfakes of named public figures, copyrighted style, weapons / self-harm / extremist prompts. 16 deterministic assertions against real Gemma 4 e4b (~600 ms per call). Requires Ollama withgemma4:e4bpulled.examples/coding-agent-guard/— one policy gating Claude Code, OpenAI Codex, and Google Antigravity at the hook layer. No LLM, no Docker — see Guard a coding agent in 60 seconds above for the one-liner.
Requires Go 1.25+. The default binary is pure Go — no cgo, no
database, no network services — and links one runtime library,
gopkg.in/yaml.v3, for policy parsing. The module also requires
pg_query_go and protobuf for the strict-postgres SQL parser, which
is build-tagged and excluded from the default binary.
1. Define a policy (policies/refund_cap.yaml):
schema_version: 1
policy_id: pol-example-refund
name: refund-cap-example
version: 1
status: approved
mode: enforcement
scope:
tool_names: [issue_refund]
rules:
- rule_id: rule-amount-cap
name: Single refund amount limit
rule_type: threshold
conditions:
field: amount
operator: gt
value: 500
effect: deny
effect_config:
severity: high
suggested_response: "Refunds over $500 require supervisor approval."
citation:
document_id: doc-refund-sop
section: "2.3"
excerpt: "Individual refund transactions exceeding $500 require supervisor approval prior to processing."2. Evaluate a tool call (examples/call_over_cap.json):
{
"agent_id": "support-agent-v2",
"session_id": "sess-001",
"org_id": "acme",
"tool_name": "issue_refund",
"tool_group": "monetary_outflow",
"parameters": {"amount": 1000, "reason": "Goodwill credit"}
}./bin/tg evaluate -policy policies/refund_cap.yaml -call examples/call_over_cap.json
# {"decision":"denied", "decision_reason":"Denied by: [rule-amount-cap] Single refund amount limit ...", ...}
echo $?
# 3Exit codes: 0 allow/pass, 1 internal/load/parse error, 2 usage error, 3 deny, 4 escalate, 5 chain-verification-failed, 6 lint-error.
3. Lint the policy for known footguns:
./bin/tg lint -policy policies/refund_cap.yaml
# [
# {"rule":"scope-no-tool-group", "severity":"warn", ...},
# {"rule":"amount-without-semantic-check", "severity":"warn", ...}
# ]Both warnings correspond to real bypass classes that Gemma 4 found in
the battle test — see docs/battle-test-results.md.
4. Verify an audit log offline:
./bin/tg verify -file decisions.jsonl
# {"intact": true, "records": 4711, "tail_hash": "sha256:..."}
echo $?
# 05. Export verified audit records as JSONL:
./bin/tg export -file decisions.jsonl --format jsonl \
--since 2026-08-25T00:00:00Z --action denied \
| jq -c '{timestamp, trace_id, action_taken, policy_set_hash}'The source chain is verified before anything is written to stdout. See Exporting audit records for filter semantics, version stamps, rotation behavior, and a generic log-forwarding example.
6. Benchmark the engine on this host:
./bin/tg benchmark -trials 10000
# {"p50_us": 3, "p95_us": 8, "p99_us": 14, "max_us": 387, "trials": 10000}p99 ~14µs on an AMD Ryzen 7 7700 — about three orders of magnitude below a 25 ms budget. This is a point measurement, not a promise; the nightly same-runner proxy regression gate and its methodology are in docs/performance.md.
7. See the audit chain do its job:
# Verify the sample chain at examples/decisions_chain.jsonl
./bin/tg verify -file examples/decisions_chain.jsonl
# {"intact": true, "records": 3, ...}
# Tamper with one decision and watch verification fail
sed '1s/"decision":"allowed"/"decision":"denied"/' examples/decisions_chain.jsonl > /tmp/tampered.jsonl
./bin/tg verify -file /tmp/tampered.jsonl
# {"intact": false, "first_failure_at": 1, "failure_reason": "trace_hash \"sha256:6c69...\" does not match canonical recomputation \"sha256:177f...\""}
echo $?
# 5Regenerate the sample chain after a schema bump:
go run ./cmd/example-chain > examples/decisions_chain.jsonlThe three starter policies live in policies/ (indexed
there). The sample tool calls and audit chain to exercise them:
| File | Use |
|---|---|
examples/call_under_cap.json |
Allowed $85 refund. |
examples/call_over_cap.json |
Denied $1000 refund. |
examples/decisions_chain.jsonl |
Real 3-record SHA-256 hash-chained audit log for tg verify and tg export. |
| Bundle | Scope | Asserts |
|---|---|---|
examples/postgres-attack/ |
SQL + shell + path for engineering agents | 28 deterministic + 45 + 56 bruteforce |
examples/finance-cfo/ |
Procurement / wires / expenses for finance agents | 18 deterministic |
examples/business-ops/ |
Customer data export / mass communication / support comp | 26 deterministic |
examples/content-gen/ |
Image / audio / text generation with Gemma 4 content classifier | 16 deterministic (real LLM) |
Every bundle ships a policies/ directory, a test-policies.sh curl
suite, and a README that maps each rule to the threat it closes. Each
suite is one-line runnable:
make build # binaries
./bin/tg-proxy -listen :19090 -policy-dir <bundle>/policies & # load
bash <bundle>/test-policies.sh # assertThe
workflow runs the no-LLM bundle suites (finance, business-ops, the
sample app, and postgres-attack) against a live
tg-proxy on each
push; a green check means each suite's attack envelopes were denied.
The same suites run locally with one target each:
make test-finance # CFO spend: over-cap POs/wires, velocity, sanctioned-jurisdiction — 18 assertions
make test-business # bulk PII export, mass-comms compliance, support-credit abuse — 26 assertions
make test-postgres-full # SQL injection + shell escape + path traversal — 28 + 45 + 56 = 129 checks, all blocked in this suite (Docker)
make test-policies # finance + business-ops + the sample app (use make test-postgres-full for SQL/shell/path)make test-postgres-full brings up a real Postgres plus the db-tool /
os-tool services and throws 129 SQL-keyword-evasion, dollar-quoted
EXECUTE, tool-name-spoofing, path-traversal, shell-metacharacter, and
interpreter--c attacks at the proxy; the suite fails the build if any
one gets through. The multimodal content-gen bundle isn't in CI
because it needs a local Ollama + Gemma model (GPU) — run it yourself
per its README.
./bin/battle-test -model gemma4:e4b -trials 5Drives a local Ollama model as an adversarial agent that tries to bypass
the policy. Reports per-scenario block rate and prints every successful
bypass in full so the operator can read what the model actually did. See
docs/battle-test-results.md for the
canonical run + analysis.
The defaults (-temperature 0.6, -seed 0) keep variety so the harness
surfaces a broader set of bypass shapes. For exact reproducibility,
combine -temperature 0.0 with a fixed -seed:
./bin/battle-test -model gemma4:e4b -trials 5 -temperature 0.0 -seed 42Use deterministic mode for regression and CI runs — the canonical
"33.3% block rate" in docs/battle-test-results.md comes from a
deterministic run (-temperature 0.0 -seed 42). The default mode varies
generation to surface new bypass shapes, so its block rate moves from run
to run.
pkg/domain/ Tool-call envelopes, policy / rule / condition types,
decision traces. JSON-tagged; no loading or I/O.
pkg/policyload/ Strict YAML loading and deterministic policy-set hashing.
Shared by every policy-loading binary.
pkg/engine/ Pure policy evaluation. No I/O. Given an envelope and a
set of policies, returns a decision in microseconds.
Hosts path, shell, write, HTTP, and reversibility
classification plus the regex compile cache.
pkg/sqlguard/ Four-dialect SQL classifier. The tokenizer-based
lite implementation (postgres / mysql / sqlite) and the
mssql package are pure-Go and registered by default.
Strict variants (pg_query_go / tidb/parser / rqlite/sql)
are opt-in via build tags pg_strict / mysql_strict /
sqlite_strict and override the same dialect through the
sqlguard registry. Loads a function-class registry from
tools.yaml so policies can match by group instead of
enumerating every dangerous function.
pkg/llmguard/ Local-LLM content classifier. Pure HTTP client against
Ollama; multimodal (text + image) via Gemma 4 vision
variants. Used by the llm_classify condition for image,
audio, and text generation tool surfaces. Fail-closed
semantics: timeout / network / model-refusal → deny.
pkg/audit/ SHA-256 hash-chained traces, offline replay verifier,
versioned canonical JSON, and per-record engine/policy-set
provenance for stable hashing.
cmd/tg/ The one-shot CLI. Policy evaluation, audit verification/export,
linting, simulation, coverage, hooks, protection, and benchmarks.
cmd/tg-proxy/ HTTP service. POST /evaluate, hash-chained JSONL audit
with rotation, SIGHUP policy reload, /metrics,
/healthz, /readyz, per-agent rate limiting, escalation
store, unknown-tools-deny gate. See
docs/integration.md for the operator path.
cmd/battle-test/ Adversarial harness. Drives a local LLM (Gemma 4 today,
Qwen 3.x once integrated) against the engine.
cmd/example-chain/ Tiny generator for examples/decisions_chain.jsonl.
Re-run after a CanonicalTraceVersion bump.
examples/sample-app/ Hand-coded end-to-end demo. `make sample`.
examples/ollama-agent/ Real-LLM end-to-end demo: Gemma 4 (via Ollama)
emits tool calls, tg-proxy returns the decision,
the demo wrapper blocks denied calls and the
model adapts. `make sample-ollama`.
examples/postgres-attack/ Dockerized demo: real Postgres, real db-tool,
tg-proxy with sql_classify / path_classify /
shell_classify policies, Gemma 4 agent on the
attack side. `make sample-postgres` to run the
whole compose; `make test-postgres` to run the
28 deterministic assertions.
examples/content-gen/ Three policies for image / audio / text
generators using a local Gemma 4 classifier.
Standalone (proxy + Ollama on host) or full
docker-compose stack (proxy + Ollama + mock
generator tools). 16 deterministic assertions.
examples/mcp-server/ MCP (Model Context Protocol) bridge — a
stdio JSON-RPC 2.0 server that exposes
gated tools to any MCP client (Claude
Desktop, Continue.dev, MCP-rs). Every
tools/call goes through tg-proxy /evaluate.
examples/coding-agent-guard/ PreToolUse hooks that gate shell commands
for Claude Code, OpenAI Codex, and Google
Antigravity from one policy.yaml. `./run.sh`
shows all three offline; `./install.sh` wires
the hooks into the agents you have installed.
The engine package has zero I/O and zero external dependencies beyond
pkg/sqlguard. The verify primitive reads JSONL from any io.Reader.
The default-build CLI's only external dep is gopkg.in/yaml.v3 for
policy loading.
- Engine + audit chain: table-driven tests pass with
-race, ~µs p99 on commodity hardware. CI enforces statement-coverage floors of 85% onpkg/auditandpkg/engineand 90% onpkg/sqlguard/mssql. - CLI: policy evaluation/simulation/coverage, native hooks/protection, audit verification/export, linting, and benchmarks, with a documented exit-code contract; integration tests shell-out the binary and assert behaviour end-to-end.
tg-proxyHTTP service:POST /evaluatewith hash-chained JSONL audit, SIGHUP policy reload, fail-closed by default, per-agent rate limiting, audit log rotation, integration tests assert the HTTP contract over a real socket. Successfully appended decisions carry an optional, hash-keyeddecision_receipt; escalation approvals/denials carry a separateresolution_receipt(contract and limits).- SQL / path / shell / write / HTTP semantic classifiers: four-dialect
SQL classifier (
postgres,mysql,sqlite,mssql) with pure-Go default tokenizer + opt-in strict variants via build tags. Closes CTE, dollar-quote, multi-statement, COPY-PROGRAM, and dynamic-SQL bypass classes. Path classifier defends against traversal / symlink escape / shell-meta injection. Shell classifier resolves argv path arguments and detects env-rewrap escapes (env/sudo/nice/ionice/chroot/doas). Write classifier governs file-write path/size/content, with unconditional symlink resolution so a write through a symlinked directory can't escape an allow-list. HTTP classifier governs egress host/scheme/method/port. - Sample app:
make sampleruns a refund-tool + tg-proxy + agent end-to-end — tg-proxy returns deny and the sample wrapper does not execute the call. - End-to-end policy correctness:
make test-postgres-fullbrings up the full docker stack and runs 28 deterministic assertions plus 45-case and 56-case adversarial bruteforce suites — all pass with zero bypasses when-unknown-tools-denyis set. - Battle-test: a local Gemma 4 model attacks the engine over
Ollama; the measured results are in
docs/battle-test-results.md. - Possible future work (not committed): Qwen 3.x battle scenarios,
JSON-Schema for the policy DSL, a gRPC variant of
tg-proxy. (examples/mcp-server/already ships a runnable MCP bridge — see the Examples section above.)
Comprehensive docs live in docs/:
- Getting Started — install, build, first policy in 5 commands
- CLI Reference — every
tgcommand, checked against the binary in CI - Architecture — engine, audit chain, classifiers
- Creating Policies — full YAML schema, every operator and classifier
- Operating in production — systemd, k8s, metrics, backup, recovery
- Exporting audit records — verified JSONL, filters, version stamps, log pipelines
- Content-gen bundle — the multimodal Gemma 4 classifier
- Integration — MCP, LangChain, AutoGen, native tool-use loops
- Escalation flow — human-in-the-loop approval
- Battle-test results — real Gemma 4 vs the engine
- Core vs Enterprise — the precise boundary: what ships here, what the Enterprise platform adds, what exists in neither
New in 0.8.1: no change to Tool Guard's behavior. Binaries and the container image are built with Go 1.26.9, which fixes nine reachable standard-library vulnerabilities. Go users should pin
v0.8.1: the module proxy recordedv0.8.0from an earlier commit than thev0.8.0git tag (Python SDK packaging, release scripts and docs differ; Go source is identical). See Release-Notes.md. New in 0.8.0 (breaking): policy YAMLmodeis authoritative; duplicate policy/rule identities are rejected; new audit writes use canonical v2 and must not be resumed by a 0.7 writer; and direct access toengine.LLMClassifyHookis replaced by its synchronized getter/setter.llm_classifygains a System One backend (backend: systemone). It is text-only, makes a network call for each classified prompt, and its fixed 0.6 floor is not calibrated for your traffic. Upgrade every writer and verifier that shares a chain together, runtg lintandtg simulatebefore rollout, and follow the exact migration checklist in Release-Notes.md. New in 0.7.0 (breaking): policy loading is strict everywhere —schema_version: 1declared (omitted still loads as v1), unknown or misspelled fields are load errors, the no-opdeep_evaluationfield is removed, and a policy file must be exactly one YAML document. One bad file fails its whole policy set:tg-proxyrefuses to start, whiletg hookwithout-fail-closed/-fail-closed-toolsenforces nothing on a failed load (the error is reported on stderr). Runtg linton every policy file before upgrading; migration notes in docs/operating.md. New in 0.6.0: reversibility-aware gating — a deterministic, offline classifier exposingreversibility(reversible/recoverable/irreversible/unknown) as an ordinary policy condition field, plus a shipped irreversibility floor that a more permissive policy cannot override (max-severity resolution) and that escalatesunknownas well asirreversible;tg protect claude/tg status/tg unprotectfor one-command, preview-first, exactly-reversible protection of a coding agent; an OpenAPI spec fortg-proxywith conformance tests; and platform-native config roots for the managed policy/audit location. New in 0.5.0: a per-request evaluator panic ontg-proxynow always denies and audits (previously it dropped the connection with no record);tg-proxyverifies the full audit chain on startup, not just the tail;tg hook -unknown-tools-deny; a real, quote-aware shell tokenizer behind-protect-paths(the old scanner's documented quoting/ substitution/variable-expansion evasions are now caught or fail closed); and a new Python SDK with LangChain/AutoGen/ native/MCP adapters, plus partial progress on five public 1.0-roadmap items (conformance corpus, throughput floor, provenance attestation, policy-compatibility net, documented review process) — see CHANGELOG.md for the full list. New in 0.4.0: correct path-prefix matching on Windows (deny-lists inpath_classify/write_classify/-protect-pathswere previously silently inert there). New in 0.3.0:write_classifyandhttp_classify— file-write and egress governance — plustg hook -audit-logandtg coverage. New in 0.2.0: velocity tracking that closes the amount-fragmentation bypass, thetg hookcoding-agent guard,-protect-pathsself-protection, five new operators, andtg simulate. See Release-Notes.md.
Features absent from this repo today:
- Single-model classifier. The semantic classifier is one local
Gemma. A single model can be bypassed by adversarial paraphrases —
the measured per-class numbers from our own harness are in
docs/battle-test-results.md; run
cmd/battle-testagainst your own policies to measure it for your taxonomy. Voting across several off-the-shelf Ollama models would reduce this, but is not built. - No voice-print matching. The audio classifier reads the PROMPT ("clone the voice of X"). It does not analyse a reference voice sample. Pretrained speaker-embedding models (e.g. ECAPA-TDNN via SpeechBrain) exist and could be integrated, but no such integration exists today.
- No PII redaction in this repo. Free-text fields are hashed into the audit log as-is. Put a redaction layer in front of the proxy — for example, Microsoft Presidio or your existing DLP pipeline — or use Tool Guard Enterprise, which applies schema-driven redaction before the trace is written.
- No compliance evidence-pack exporter in this repo. Every decision is in the audit log; turning that into an evidence bundle is a manual step. Tool Guard Enterprise ships a signed Evidence Pack export with a HIPAA §164.312 control mapping and an offline verifier. The mapping is operator-maintained; it is not a certification or compliance guarantee.
- No managed hosting. You run the proxy. You run Ollama. You own the operations. There is no hosted version.
- No receipt resolver. Decision receipt URNs are deliberately opaque and non-resolvable. Building a lookup endpoint requires authentication, field-level disclosure policy, retention/rotation search, and rate limiting.
- No gRPC variant. REST
POST /evaluateonly. - No Node client SDK. A Python SDK ships at
sdk/python/(PyPI distributiontool-guard-corebeginning with v0.8.0; import nametoolguard) with adapters for LangChain, AutoGen, native OpenAI/Anthropic tool use, and MCP. For other languages, thetg-proxyHTTP surface is specified inapi/openapi.yaml(with conformance tests) and documented indocs/integration.md; the request/response shapes are small enough to hand-write a client. - Battle-test catalogue is Gemma-4-only. Today's bypass numbers reflect Gemma's failure modes specifically; other models will fail differently.
For the precise Core / Enterprise boundary — and the list of things that exist in neither edition — see docs/oss-vs-enterprise.md.
Tool Guard governs what an agent does. Many AI-safety tools govern what a model says. They are complementary layers.
| Category | Primary layer | What it primarily checks |
|---|---|---|
| Prompt and response filters | Model input/output | Whether text should be accepted, refused, rewritten, or classified |
| Content classifiers | Model input/output | Whether generated or requested content matches safety categories |
| Conversation-flow guardrails | Dialogue flow + content | Whether a conversation follows an allowed path or response flow |
| Structured-output validators | Model output | Whether model output matches a schema, regex, topic, or format requirement |
| Pre-deployment eval suites | Test/eval time | Whether prompts and responses pass benchmark or regression checks before release |
| Tool Guard Core | Tool execution | What action is about to run — SQL/path/shell/write/HTTP/LLM classifiers plus policy evaluation on the structured tool call, before the API executes; tamper-evident audit trail |
Use prompt and response guardrails when you need to control model text. Use Tool Guard when agents call tools that touch money, customer data, infrastructure, databases, filesystems, or generative APIs. Most production agent stacks need both: model-layer controls for language, and action-layer controls for execution.
See CONTRIBUTING.md. Bug reports and PRs welcome;
security issues go through SECURITY.md, not GitHub
issues. Community standards in CODE_OF_CONDUCT.md.
Notable changes per release are recorded in
CHANGELOG.md.