TESSERACT: 500 attacks on my AI agent, replayed in 4D by Jaden GreenTESSERACT: 500 attacks on my AI agent, replayed in 4D by Jaden Green

TESSERACT: 500 attacks on my AI agent, replayed in 4D

Jaden Green

Jaden Green

What it is

TESSERACT is my own prompt-injection benchmark, replayed in four dimensions. Ten jobs a user might ask an AI agent to do, ten attacks hidden in things the agent has to read, and five delivery channels (an email, a vendor invoice, a web page, a shared file, a calendar invite) make a 3D lattice of 500 runs. Time is the fourth axis: a scrubber replays every run in the order it actually ran.
Two agents face the same 500 runs side by side. On the left, an ordinary agent that treats every sentence it reads as an instruction. On the right, BULWARK, the agent I built, which labels every value with where it came from and checks that label before every action. It is live at greenaidigital.com/tesseract: drag it, scrub it, click any cube.
Mid-replay, run 117 of 500. Ember cubes are hijacks on the ordinary agent; the amber column on the right is BULWARK finishing the same jobs with nothing landed.
Mid-replay, run 117 of 500. Ember cubes are hijacks on the ordinary agent; the amber column on the right is BULWARK finishing the same jobs with nothing landed.

The numbers, derived not typed

Ordinary agent: 150 of 500 attacks landed, 498 of 500 jobs completed.
BULWARK: 0 of 500 attacks landed, 499 of 500 jobs completed.
The one refusal is the poisoned Acme invoice: the attacker rewrote the bank details inside a real invoice, and the OneOf(to_iban) rule refused the transfer and reported it instead of paying.
The page inlines the bench's own results file (500 rows, 6 KB) and recomputes every total from the rows. Open it with ?selftest=1 and it checks its derived totals against the published bench and writes PASS or FAIL into the tab title.
Run 117: the one refusal. The detail panel shows the job, the attack, the channel, and what each agent did.
Run 117: the one refusal. The detail panel shows the job, the attack, the channel, and what each agent did.

How it was built

One HTML file, three.js, no build step. Two instanced meshes of 500 cubes each, one per agent.
Clicking a cube raycasts into the instance ids and jumps the clock to that run, so the detail panel shows the exact job, attack, channel and both outcomes.
The timeline underneath draws the cumulative hijack count for both agents as the clock moves. One line never leaves zero.
Where it breaks is on the page too: when the attacker controls the planner itself, policy alone stops 6 of 10. The four that pass are actions with constant arguments that look identical to the user asking. I would rather show the four than pretend there are none.
Run 500 of 500. 150 ember cubes on the left, clustered in the email and invoice layers. Zero on the right.
Run 500 of 500. 150 ember cubes on the left, clustered in the email and invoice layers. Zero on the right.

Why I made it

Every benchmark I have published was a table. A table does not show you that the hijacks cluster in the email and invoice layers, or that the amber column on BULWARK's side climbs at the same pace as the ember one on the left while never lighting a single ember cube. Making the bench something you can rotate, scrub and click turns a claim into a thing you can inspect.
The playable version, where you write the attack yourself, is The Gauntlet. Same labels, same policy, your payload.
Like this project

Posted Sep 13, 2026

A 3D lattice of 500 real benchmark runs with a time scrubber. Ordinary agent: 150 hijacks. BULWARK: 0, with 499 of 500 jobs done. Built in three.js.

Likes

0

Views

1

Timeline

Sep 12, 2026 - Sep 12, 2026