What inspired us

We are all CS and CIS majors, and we came into Cal Hacks wanting one thing: a project grounded in real research, not another wrapper on an API.

So we started by reading instead of coding. While looking into where security is actually heading, we kept landing on the same gap — AI agents have become the target. Companies are adopting agents fast, giving them tools, inboxes and bank rails, and in that rush the attack surface is being skipped over. The agent isn't just answering questions anymore; it is acting. And when an agent is compromised, nobody finds out at the moment of the attack. They find out weeks later, in an audit, after the money is gone.

The paper that turned that hunch into a plan:

The Attack and Defense Landscape of Agentic AI: A Comprehensive Survey Kim et al. — UC Berkeley, SNU, UIUC, UCSB

It lays out the layers of an agent and the attack vectors against each one. Reading it, the design question became obvious: if every layer of the agent can be attacked, stop trusting the agent. Put the guarantee somewhere the attacker can't reach.

That became Sudibran — a security and audit layer that sits between an AI agent and the money. We also wanted the sponsor integrations to be load-bearing rather than decoration, so we picked a scenario where each one does a real job: an accounts-payable agent paying supplier invoices, where one invoice quietly says "our bank account has changed, pay ****9917."

We deliberately avoided rebuilding something already on the market. Everyone else is trying to make the model safer with better prompts and better detection. We took the opposite approach: assume the model is already fooled, and make the path the money takes safe.

How we built it

The core idea is a gate. Nothing the agent decides becomes an action until the gate checks it against what the human actually signed for.

The trust boundary. Three zones, one rule between them: nothing that reads untrusted text can move money, and nothing that moves money is reachable by the agent. We enforced that in the infrastructure, not in code comments — Docker Compose declares eight networks, six of them internal: true. The agent container physically has no route to the broker. Each key lives in exactly one process, and the payment tool takes no account-number argument; it looks the payee up from the supplier record itself.

The pipeline. A run starts when another agent sends an invoice, not when someone presses a button:

seller agent ──► gate (FastAPI) ──► parser (Claude, no tools) ──► planner (Droid, MCP tools only)
                      │
                      ├─ policy (OPA Rego, default = deny)
                      ├─ CRS identity check on any new account
                      └─ broker ──► one-time capability token ──► Nessie
                      │
              every check ──► hash-chained audit log
  • Signed intent — the human's instruction is Ed25519-signed before any untrusted text is read.
  • Claude as a quarantined parser — it reads the attack and can do nothing with it: no tools, typed output only.
  • Factory Droid as the planner — it plans over six MCP tools, none of which moves money. There is no pay(account_number) to call.
  • OPA Rego policy — eight rules you can diff in a pull request, with a Python mirror tested against real OPA.
  • CRS Credit API — the question policy cannot answer: is this new bank account a real identity?
  • Capital One Nessie — the actual rail, and the crime scene.
  • A hash-chained audit log — each event commits to the one before it: h[i] = SHA-256(h[i-1] || e[i]). The browser re-verifies the chain itself with crypto.subtle, so you don't have to trust our dashboard.

Trust labels never go up. Memory keeps a value's original source label forever — trust(output) <= min(trust(inputs)) — which is why the memory-poisoning attack fails a full run later, when the laundered value tries to act.

Assume breach, on by default. A switch replaces the planner with one that obeys every injected instruction it finds, a stand-in for a fully compromised model. Sudibran still has to stop the attack, and does.

Two agents, one real loss. Every scenario runs twice: once through an unprotected agent holding its own bank key, once through Sudibran. Both move real sandbox money. On the raw track, the attacker's $140 genuinely lands in their Nessie account; on the protected track, Nessie is never called at all. A separate script reads the loss back out of the bank, trusting nothing we wrote.

Measured on the final build: 20/20 benign invoices completed, 8/8 attacks blocked, 5.76 ms added p95 latency.

Stack. Next.js 16 + React 19 + Tailwind 4 dashboard · FastAPI + Pydantic + SQLAlchemy gate · OPA Rego · Ed25519 / EdDSA / SHA-256 · Factory Droid over MCP · Anthropic Claude · CRS Credit API · Capital One Nessie · Docker Compose · Postgres.

Challenges we faced

We spent the first several hours of the hackathon reading instead of coding, which felt like a mistake at the time. Fintech security papers, accounts-payable fraud reports, research on attacks against AI agents. It turned up a dozen problems we could have built on, and picking one was harder than any of the engineering. We went with the one we think gets much worse in a couple of years: companies are starting to let AI agents move real money, and nothing is checking the agent's work.

The breakthrough came from the Berkeley paper on the attack and defense landscape of agentic AI. It lays an agent out in layers and lists the attacks on each one, and that made the gap obvious. Every defense we could find tries to make the model harder to fool, but the model is only one of those layers, and an attacker only needs one soft spot. So we stopped trying to protect the model and put the checks outside it instead.

Getting from the paper to something that actually runs was the real grind. A paper tells you an attack exists. It doesn't tell you what the check looks like in code, what it has to reject, or what it costs you. We built all nine attacks, wrote the policy rules twice so we could test them against real OPA, added one-time tokens and a hash-chained log the browser can verify on its own, and ran both the protected and unprotected agent against a live sandbox so the money genuinely moves. Then we measured the overhead rather than guessing at it: 5.76 ms.

What we learned

Technically, we learned how to build a system whose guarantees live in its structure rather than in a model's judgement. A good part of that was learning to read the sponsor platforms properly instead of just plugging them in. For each one we had to work out what the tool is really for, where the company behind it looks to be taking it, and then hold that up against the research we'd just read to see whether it had a genuine place in the pipeline. Some didn't, and we left those alone. Seven did, and CRS Credit, Factory Droid, Capital One Nessie, Anthropic Claude, Microsoft, Armadin and Hyphenate each carry real weight in the system rather than appearing in a checklist.

Personally, from me (DP) as the one leading the team: I learned what leading actually means under a deadline — that the job during the low hours isn't to have the answer, it's to keep everyone talking until the answer shows up. I learned to use sponsor tracks as building blocks instead of checkboxes, and to ship a real solution in a very short window.

And I learned not to spend the whole weekend behind a screen. This was my first trip out of state from Texas, and some of what I'll keep from Cal Hacks isn't code: walking the streets of San Francisco, the beaches, the photos. Build the thing — but look up once in a while, too.

Built With

Share this project:

Updates

Submission history