An agent that does useful work holds a credential. The API key, the database role, the cloud permission, the payment scope. That is what makes it useful. It is also the whole of the problem.
An agent reads text and acts on it. That is the design, not a flaw in one vendor's model. So anyone who can put text in front of your agent acts with your agent's credential. A support ticket. An invoice PDF. A page it browses. A README in a dependency it summarizes. The attacker never needs your key. They need your agent to read a sentence.
Speed removes the second line of defence. A person who is tricked makes one bad decision and usually notices. An agent that is tricked makes the same decision several hundred times before anyone opens a dashboard.
Filters are partial by construction
The first instinct is to filter the input: scan the ticket, score the prompt, classify the document, refuse the suspicious ones.
Filters help. They do not close the hole, and the reason is structural rather than a matter of tuning. A filter is a classifier over open-ended text, and the attacker writes the text. Every classifier has a false-negative rate. The attacker is free to keep searching until they find one, and you find out afterwards, if at all.
That trade is fine where being wrong is cheap. It is not a basis for letting software move money, delete backups, or change a production rule.
So assume the injection works. Assume the model is manipulated on the day it matters. Then the useful question is a different one: what is the agent actually able to do once it has been?
Authorization, not persuasion
Stop giving the agent authority in the first place.
Take the credential out of the agent. The agent holds one key that can ask, not act. Every action leaves as a signed proposal: this action, these arguments, these resources. A proposal is a request. Holding one grants nothing.
The proposal meets a policy you signed. The policy grades it on what it touches, what it costs, and whether it can be undone. Routine actions are granted in milliseconds, so the agent never waits on something that was always going to be fine. Dangerous actions are held. An action with no rule is refused, never guessed at. Unknown is never low risk.
A held action needs people, and needs them in a particular way. Approvers sign the exact bytes that will run, not a summary of them. The rule is a quorum: held, quorum 2-of-2 required. One approver signs and the other refuses, so quorum is not met and nothing ran. Silence is not consent. Whoever proposed the action cannot approve it.
Then execution. Only granted proposals run, and they run under ZIFFER's identity rather than the agent's. Your systems accept the action from that identity and from no other. An agent that has been fully hijacked and tries to go direct hits a wall, not an alert.
What a decision has to leave behind
Every outcome writes a receipt: what was proposed, how it was graded, who signed, what ran. Receipts are signed, appended to a ledger, and anchored outside the system that produced them before an irreversible action releases. Nobody rewrites that history afterwards, including us.
That is the part that turns a policy into evidence. A log says what a service claims happened. A receipt lets somebody else check it, without production access and without taking your word for it.
The shape of the fix
Prompt injection is not waiting on a better prompt, and it is not a model quality problem that gets solved on someone else's roadmap. It is what happens when credentials are handed to something that takes instructions from strangers.
So do not hand it credentials. Let it propose. Let your policy grant. Keep your people on the actions that deserve them, and keep a receipt for everything that ran.
The agent stays exactly as capable as you built it. It stops being the thing that decides.
ZIFFER is built on an open specification, and every claim in it is meant to replay on your own machine. The documentation is where that starts.