-
-
Checking the SSN against the W-2: stamped ALLOWED, ran on LOCAL and TOKEN FACTORY. The local step took 7871 ms, 179 tokens in, 26 out.
-
No consent names [email protected], so the W-2 email is DENIED, refused before any email was drafted. Nothing was sent.
-
Box 1 wages: ALLOWED on TOKEN FACTORY. The W-2's SSN span was removed before the model read it. Each call lists latency, tokens and cost.
-
The client file: two active consents, one saved note, and a ledger that records the SSN only as a keyed hash fingerprint.
-
One container, four processes. A Rust gateway checks every model and tool call. SSN-bearing work stays on the local model.
Inspiration
In June the IRS published introductory guidelines for using AI in federal tax practice, and one line stuck with me: "Practitioners must strictly handle all client data using only secure, enterprise-approved AI." The IRS also reported nearly 300 data breaches at tax professionals in the first half of 2025 (IR-2025-88). (Guidelines: https://www.irs.gov/pub/opr-taxpros/issue-number-2026-19-introductory-guidelines-for-responsible-ai-use-in-federal-tax-practice.pdf) A big firm can pay for an enterprise AI review. A solo preparer can't. So I built them an assistant whose data rules are written down, checked by a machine, and shown on every single request.
What it does
Pick a client and ask Pru in plain English, like "What's still missing from this file?" or "Draft the 1099-INT request."
- It routes before anything leaves. Text with a Social Security number goes only to a small NVIDIA model running inside the same container. SSN-free work can go to Nebius Token Factory, but only if that client has an active consent for that purpose.
- It asks for consent. A consent is a token signed with the client's PIN, naming the client, the purpose, an expiry and, for a disclosure, the exact recipient. It's revocable anytime. An email to anyone needs a consent naming that exact recipient, or the draft is refused.
- It remembers. Short notes per client, like "prefers callbacks on Tuesday mornings," read back only for that client.
- It drafts. Emails and calendar holds come back as complete drafts. They don't send yet, and the page says so.
- It shows its work. Every turn opens with ALLOWED or DENIED and where it ran, LOCAL or TOKEN FACTORY, then each call's model, reason, latency and cost. The ledger keeps a keyed hash of any SSN, never the number.
In 30 recorded agent turns on Token Factory, zero SSNs left the machine, and the agent picked the right tool first on 27 of 30.
Try it: one container, no account: docker build -t pru:test . then docker run --rm -p 3000:3000 -e NEBIUS_API_KEY pru:test and open http://127.0.0.1:3000. Without a key, consent, the local SSN check and the ledger still run, and anything that needs Token Factory is refused in plain words. A hosted copy runs on a Nebius endpoint during judging, December 1 to 15, at https://port3000-najy7whtsgwrd47.tunnel.applications.us-north1.nebius.cloud
How I built it
NVIDIA Nemotron 3.5 Lightning (nvidia/Nemotron-3_5-Lightning) is the agent's brain for SSN-free work. I picked it by measurement: the agent had to call the right tool first on at least 27 of 30 test turns, and Lightning passed at exactly 27, with zero SSN egress and all 3 security probes passed, for $0.011 computed at list rates.
NVIDIA Nemotron 3 Nano 4B (nvidia/NVIDIA-Nemotron-3-Nano-4B-GGUF) runs under llama.cpp inside the container and handles anything with an SSN in it. That text never reaches a model provider. A local turn that checks an SSN took 13 seconds on the hosted CPU in my Oct 21 endpoint test, and 15.5 seconds on my PC in the video.
Nebius Token Factory serves Lightning. A Token Factory turn took 1.4 seconds in my Oct 21 hosted test, and every step shows its Token Factory cost, computed at list rates.
Nebius AI Cloud hosts the judge copy as an endpoint (4 vCPU, no GPU) built from the same image you can run locally.
Hermes Agent from Nous Research is the agent loop. It runs as one long-lived process with exactly one toolset, Pru's own MCP server with seven tools, and no shell and no web.
The Rust gateway is the only server Hermes is allowed to reach: the jail limits it to the gateway's port. Every model call and tool call goes through it, and it decides where each one may go. It finds SSNs dashed, spaced, masked, split across lines and OCR-noisy: 360 of 360 test canaries caught, 0 of 120 look-alikes flagged.
Cedar makes the allow or deny decision, and CI uses Cedar's SymCC with cvc5 to prove the live policy is at least as strict as a reference policy. Biscuit tokens carry each signed consent.
NVIDIA OpenShell: Pru compiles each client's consent ledger into an OpenShell network policy whose only allowed host is the Pru gateway, and CI checks it with OpenShell's prover, which also catches a planted outside host. In the shipped container, Hermes is jailed directly with Landlock and seccomp; running it inside an OpenShell sandbox is the next step.
Challenges I ran into
- My first model test failed badly, 12 of 30, and most of the misses weren't the model. A tool rejected
W-2when it expectedw2, another returned the wrong JSON shape, and the repeated errors tripped Hermes's own circuit breaker. Fixing the wiring is what got Lightning to 27. - On the hosted test the agent answered an SSN question from the SSN-free summary instead of the local model, while the page claimed the local model was reading it. I changed the preset and the page now waits for the gateway's real route before saying anything.
Accomplishments that I'm proud of
- Zero SSNs left the machine across 30 recorded turns.
- A policy that's proved by a solver, not just tested.
What I learned
- The model is the easy part. The wiring between the agent and its tools decided most of my results.
- An assistant for a regulated job has to show its routing on every request, not promise it once in a README.
What's next
Real sending behind the same consent check, so a draft can go out once the client has signed for that recipient, and testing it with a real small practice.
Code: https://github.com/StephenSook/pru (Apache-2.0)
Built With
- axum
- biscuit
- cedar
- cvc5
- docker
- hermes-agent
- landlock
- llama.cpp
- mcp
- nebius-token-factory
- nemotron
- next.js
- nvidia-nemotron-3-nano-4b
- nvidia-nemotron-3.5-lightning
- nvidia-openshell
- playwright
- rust
- seccomp
- typescript