[ Trojan Prompt Defense ]
Stop trojan
prompts at the gate.
Instructions your agent reads, hiding in content it trusts: a file, a page, a tool result. It can't tell an order it was given from one it found. trust.band records where every argument came from, so a trojan prompt gets read, and doesn't get to act.
Included free — the ones you open every day
- traceevery argument, with the band it arrived at
- shadow-reportwhat it would have refused, on your own traffic
- infera policy that permits exactly what you already do
- replaya policy change against recorded traffic, before it ships
- reporttokens by class, read from the transcript, never estimated
- exportthe whole record to any OTLP collector, no SDK
- packsseven starters, each with the number it was measured at
- testunit-test a policy in milliseconds, in CI
- revoke-agentrefuse every later call by a named agent, from any process
- lockpin tool descriptions, servers and skills; refuse the rug pull
- alertsa webhook per event class to Slack, Discord, ntfy — yours, free
Sixty seconds,
start to first refusal
An injected instruction doesn't break in. It persuades an agent that already holds permission to use it differently.
How the bands workInjection is a privilege problem, not a content problem
01 / THE PROBLEMEvery argument still looks legitimate, because every argument is legitimate. It just entered from the wrong place.
Filtering dangerous instructions out of content is a losing game: the space of phrasings is unbounded and one miss is enough. Inspecting a call's parameters is a better game but a partial one, because an attacker's account number and a real one are both just account numbers.
The question left is where a value came from. trust.band answers it at the boundary, before the model has a chance to be persuasive about it.
The stamp is applied on arrival and the value's own claim about its origin is never read — so nothing inside the conversation can raise its own trust level.
Where provenance matters, and where it doesn't
02 / THE RESULTEvery figure sits beside its undefended baseline, because a defended number on its own describes the model, not the defence. Predictions were registered and committed before each run.
| Suite | What an argument is | Undefended | Params-only | trust.band |
|---|---|---|---|---|
| AgentDojo banking | Enumerable — payees, amounts | 36 / 144 | 0 / 144 | 0 / 144 |
| AgentDojo Slack | Free text | 46 / 105 | 46 / 105 | 22 / 105 |
| AgentDojo Slack, second attack | Free text | 40 / 105 | 37 / 105 | 9 / 105 |
Four bands, one lattice
03 / THE MODELThe policy and the keys behind it. Nothing an agent can reach or influence.
What the operator typed or approved in this session.
Anything a tool returned — file contents, search results, API responses, another agent's output.
Untrusted third-party content reaching the agent by any other route.
A policy reads like a rule
Constrain an argument by band, by value, or by both. Mark a rule confirmable and a refusal becomes a question rather than a wall.
grant: banking.transfer
recipient:
min_band: session
in: known_payees
amount:
max: 5000
confirmable: true
A log you can hand over
Allowed or refused, each entry names the rule that decided, the band of each argument, and the outcome, including when the tool itself raised an exception. Hash-chained, so a missing or altered entry is detectable.
A refusal tells the operator which rule refused and why, rendered from the recorded provenance rather than from anything the model wrote. That message is the part of this product worth having.
In a swarm the record names the agent, not a session: researcher · send_message. The name is bound under the same key that mints capabilities, so a forged or borrowed one is refused before any rule is read, and revoking an agent kills every capability it held without touching another. Local identity, shared across processes by a key file; it attributes, it does not isolate code running in the same interpreter.
Fix the plan before the agent reads anything
State what a task may do and where it may send things, before its first read. An action outside that plan is refused, whatever the page said. Plan-then-execute is a known pattern; anyone can assert one.
Almost nobody can check it. A plan written after the agent has read a poisoned page may be the injection — and a plan the attacker wrote is worse than none, because it looks like a control. Checking that requires knowing whether tool output had already entered the session, which is what provenance records and a parameter rule cannot see. A plan fixed too late is marked untrusted here, and every check against it refuses.
Measured in a live agent loop. An injected page instructing the agent to copy its output to an unplanned address:
reached the attacker 10 / 10
# with a plan — same page, same model
reached the attacker 0 / 10
benign task still completed 10 / 10
It starts by refusing nothing
04 / ADOPTIONInstall it and it watches. Every decision it would have made is recorded and surfaced; nothing is blocked. Run your normal work for a week, read the report, then enable the rules you actually want.
A tool that enforces from the first call has to be right immediately or it breaks your agent — the objection that gets security tools uninstalled. Shadow mode is the default here, not a flag.
Adapters today: Claude Code, MCP, LangChain, LangGraph and CrewAI for tool calls; LangChain and LiteLLM for model constraints, where the gate's hint drops the deployments a step may not reach and the router chooses among the rest. GitHub Actions next. Every adapter passes one conformance suite, so they cannot drift into separate products. Each of the three framework adapters was written against the installed library rather than its documentation, which is how two silent bypasses turned up — in one, the framework called a different method than the one being guarded, so the tool ran while the checks looked green.
Which leaves a problem worth naming: a week of zero refusals shows you nothing. So the record is readable on its own. trace replays a session showing the band each argument arrived at — on 600 real events, 0 refusals and 71 arguments that came from tool outputrather than from the operator. That is the number that justifies keeping it installed on a day nobody is attacking.
replay tests a candidate policy against recorded traffic, and refuses to answer when the record cannot support the replay rather than reporting fewer refusals than the truth. report accounts for tokens by class, never estimated. export ships the whole record to any OTLP collector with no SDK and no dependencies.
pip install trustband
# starter policy + hook config, shadow by default
trustband init
# ...a week of normal work...
trustband shadow-report
400 calls observed, 0 would have been refused (0%)
SHADOW — nothing was blocked. This is measurement, not protection.
# a policy permitting exactly what you did
trustband infer
400 permitted call(s) -> 5 grant(s)
written to inferred.policy.json
Zero is the expected result on legitimate work, and it is the point: across 1,119 real tool calls from an actual session the gate refused 0.7%, all of them tools simply missing from the starter policy, at 0.090 ms per decision.
How we compare
05 / THE LANEAPort is the closest product to this one and worth your time — more integrations, a shipped identity spec, further along. The row that separates us is the third.
| Capability | trust.band | APort | Framework guardrails | Classifiers |
|---|---|---|---|---|
| Pre-action authorization | Yes | Yes | Partial | No |
| Value and parameter policies | Yes | Yes | Partial | No |
| Argument provenance in the decision | Yes | No | No | No |
| Shadow mode before enforcing | Default | No | No | Varies |
| Decision runs in your process | Yes | Hosted | Yes | Varies |
| Machine-checked enforcement model | Verus, deposited | No | No | No |
| Tamper-evident audit | Hash-chained | Ed25519 signed | No | No |
| Instant revocation | Key rotation | Status lookup | No | No |
| Agent identity spec | Local, MAC-bound | Open Agent Passport | No | No |
| Tool-definition pinning | Lockfile, MAC-bound | No | No | No |
| Framework integrations | 5 | 9+ | Native | Varies |
| Deliverable / quality gates | Yes | Yes | No | No |
| Open source | Apache 2.0 | Yes | Varies | Varies |
Free to protect your own agents
06 / TERMSThe whole enforcement layer is free forever, self-hosted, no account and no card. Provenance is deliberately in the free tier: if you cannot experience the thing that makes this different, the free tier is only a worse version of someone else's.
Free
agents you run for yourself
- Gate, provenance, plan-then-execute, contracts
- Shadow mode, inference, trace, replay, export
- Claude Code, MCP, LangChain, LangGraph, CrewAI
- Named agents, the lockfile, free alerts
- Model constraints: which models a step may reach, by band; cost ceilings refused at the gate
- Unlimited agents, calls, people
- Self-hosted, your own key
Pro
agents you run for paying customers
- Everything in Free
- Retained audit, verified on ingest by a party who cannot edit it; search across devices
- One signed policy everywhere, verified on each device
- Regression: what a policy change would have refused last week
- Per-client caps and an invoiceable report
- Approvals routed to a colleague or client
Enterprise
separation your auditor has to see
- Everything in Pro
- Bring your own key, your own KMS
- Per-tenant cryptographic separation
- SSO and compliance mapping
- Proof artifacts as procurement evidence
- Support with response terms
Where the line is. Everything that runs on your machine is free: the gate, the ledger, replay, contracts, the lockfile, agent identity, local alerts. Nothing of ours runs, nothing leaves your box, and it stays free.
You pay when someone else is involved. Running agents for a client or a team means the record has to live somewhere the laptop can't lose it, the policy has to reach every machine, and someone else has to be able to sign in. That takes a service of ours running, and external ones with it: hosting, email, payment. Pro is that service. Free protects you; Pro proves it to someone else.
Work alone, but need something of ours running while you are away? That is an add-on, not a tier: Unattended, £14.99 a month, sends a refusal a person may answer to your phone and waits for the answer.
After you pay: your key appears on the next page, once. trustband login with it on any machine, then trustband sync. Sign in any time at trust.band/account to rotate the key, add colleagues, or manage billing. If a subscription lapses, enforcement and the local log carry on unchanged and only the hosted parts stop.
Stated here rather than discovered later
07 / WHAT THIS DOES NOT DOWhere a policy can list every allowed value, provenance adds nothing
On the banking suite an allowlist of payees reaches zero attacks without us. Provenance earns its place when values are free text and there is no list to write.
Paraphrasing escapes
A payload surviving into an argument verbatim is caught. One the model restated in its own words is not, and that is the residue in the Slack figures. Token matching closes part of the gap and is off by default, because it flagged 4.9% of ordinary commands on real traffic.
Frontier models refuse most of these attacks unaided
A current frontier model contained 72 of 72 AgentDojo attacks with nothing installed. On such models the value here is the audit trail and the absence of any benign-task cost, not interception. Every attack figure above comes from a susceptible model.
A pin does not say the first version was clean
Tool descriptions, server entries and skill files are pinned when you accept them and refused when they change, and the pin is bound into every capability's MAC. That catches clean-then-dirty, which is the attack. It says nothing about whether version one was clean; scanning does that. Run mcp-scan in CI and trust.band at the gate.
A model pin sees a version only when the response names one
Model constraints decide, per step, which models the bands in context may reach and how much a session may spend, and refuse at the gate rather than on the invoice. A provider's silent swap behind an unchanged name is invisible by construction; drift is seen when a response names a version, and refuses the next call, not the one that revealed it. Where the framework lets a callback refuse, LangChain and LiteLLM today, the gate stands in front of the call; Claude Code and MCP see no model call. Any saving is arithmetic on your own price table until your traffic says otherwise.
The model is proved; the implementation follows it
31 obligations discharged in Verus against the enforcement model, and 17 counterexamples correctly rejected. There is no extraction and no refinement relation — the Python implementation is held to the model by a conformance suite, not by a proof.
Five design partners
Chosen for running agents against things that matter. Direct access to the person who built it, policy work done with you, not handed over, pricing fixed at what you sign, and real influence over what gets built next. In return we want your shadow reports and your objections.
Apply