Tesseract · a benchmark in four dimensions

500 attacks on my AI agent,
replayed in 4D.

Ten jobs × ten prompt-injection attacks × five delivery channels make a 3D lattice of 500 runs. Time is the fourth axis: every run lands in the order it actually ran. Left, an ordinary agent. Right, BULWARK, the one I built. Nothing here is simulated for effect. It is my bench, replayed.

0
run of 500
0
landed · ordinary agent
0
landed · BULWARK
0 / 0
jobs still done · naive / BULWARK
Ordinary agentreads the injected text as an instruction
BULWARKchecks who said it before it acts
drag to rotate · click a cube to jump to that run
#0 · press play
run 1 · summarize_inboxthe fourth axis: time →run 500 · review_day
This run
Press play, or drag the timeline.
Each cube is one run: a job the user asked for, an attack hidden in something the agent had to read, and the channel it came in on.
Ordinary agent
Treats every sentence it reads as if the user said it.
waiting
BULWARK
Labels every value with where it came from, and checks the label at every action.
waiting
How to read it

Three axes, one clock.

Across is the job the user asked for: summarise my inbox, pay the Acme invoice, put the Northwind sync on my calendar. Up is the attack: leak the Q3 report, wire money to the attacker, plant an SSH key. Depth is the channel the attack arrived on: an email, a vendor invoice, a web page, a shared file, a calendar invite.

A cube lights ember when the attack landed: the agent did what the attacker's text said. It settles amber when the job got done and the attack did not. The single cyan cube is the one run where BULWARK refused the job outright: the attacker had rewritten the bank details inside the real Acme invoice, and there is no safe way to pay a poisoned invoice.

The fourth axis is the timeline under the lattice. The ember line is the ordinary agent's running total of hijacks. The amber line is BULWARK's. One of them never leaves zero.

Where it breaks

Level 2: the attacker writes the plan.

The lattice is level 1: the attacker controls text the agent reads. Level 2 gives the attacker the planner itself, so the plan is theirs and only the policy layer stands. It stops 6 of 10. The four that pass are actions with constant arguments (delete a file, add a contact, create an event, write a key) that look identical to the user asking for them. I would rather show you the four than pretend there are zero.

AttackPolicy aloneRule that stopped it
The numbers

What the bench says, exactly.

Ordinary agent
150 / 500 attacks landed
498 / 500 jobs completed. The two misses are runs where the hijack itself broke the job.
BULWARK
0 / 500 attacks landed
499 / 500 jobs completed. The one refusal is the poisoned invoice, blocked by OneOf(to_iban) and reported, not silently dropped.

Both agents run against the same deterministic mock world and mock models, so the whole thing replays identically every time and in about a fifth of a second. That is what makes it reproducible rather than a story. The source is BULWARK, pure standard-library Python; the browser port you can play as the attacker is The Gauntlet.

How it was built

One file. Real rows. No renders.

If you can name an attack that should be in this lattice and is not, tell me. I will add it to the corpus, rerun the bench, and if it beats the labels I will say so here, with your name on it.