<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dinesh Jinjala</title>
    <description>The latest articles on DEV Community by Dinesh Jinjala (@dineshai).</description>
    <link>https://dev.to/dineshai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4101368%2F38c6371c-ec2c-4c21-88d9-04744780ceeb.png</url>
      <title>DEV Community: Dinesh Jinjala</title>
      <link>https://dev.to/dineshai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dineshai"/>
    <language>en</language>
    <item>
      <title>You can't leak what you can't call: an AI agent with no way to spill your data</title>
      <dc:creator>Dinesh Jinjala</dc:creator>
      <pubDate>Sun, 30 Aug 2026 12:39:34 +0000</pubDate>
      <link>https://dev.to/dineshai/you-cant-leak-what-you-cant-call-an-ai-agent-with-no-way-to-spill-your-data-3lnl</link>
      <guid>https://dev.to/dineshai/you-cant-leak-what-you-cant-call-an-ai-agent-with-no-way-to-spill-your-data-3lnl</guid>
      <description>&lt;p&gt;Your AI agent can read your database. That's what makes it useful — ask it how many support tickets came in last week, and it writes the query, runs it, and answers: 1,284.&lt;/p&gt;

&lt;p&gt;Now someone asks it to export the customer emails.&lt;/p&gt;

&lt;p&gt;Same access. Same obedience. Emails, phone numbers, private support notes — one query away.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faz8wfq44ukpivy5xmp26.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faz8wfq44ukpivy5xmp26.gif" alt="The same helpful agent, asked for something it should never give out" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the TrueForge Agent Harness Hackathon (one week, ending today), I built &lt;strong&gt;Need-to-Know&lt;/strong&gt;: an agent where that export doesn't get &lt;em&gt;refused&lt;/em&gt; — it's &lt;em&gt;impossible&lt;/em&gt;. The tool that would do it doesn't exist, so there is nothing to trick.&lt;/p&gt;

&lt;p&gt;This post is the honest build log: how it works, what TrueForge actually did for us, the three bugs a review bot caught, and the one run out of thirteen that failed.&lt;/p&gt;

&lt;p&gt;Repo (you can check everything below yourself): &lt;strong&gt;&lt;a href="https://github.com/MachineLearning-Nerd/need-to-know" rel="noopener noreferrer"&gt;https://github.com/MachineLearning-Nerd/need-to-know&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The sticky-note problem
&lt;/h2&gt;

&lt;p&gt;How do most systems protect data today? They ask the AI nicely, in a system prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Never export personal data. Don't reveal emails. Be very careful."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Three problems, and none of them is rare:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection.&lt;/strong&gt; Someone hides one line — &lt;em&gt;"ignore your rules"&lt;/em&gt; — in a message, a document, or a tool result, and the rules flip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long chats.&lt;/strong&gt; The longer the conversation, the less attention the AI pays to a rule it read an hour ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It only takes one slip.&lt;/strong&gt; The AI can behave 999 times. One bad answer and the data is out — and there is no undo for a leaked email list.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgdgbgr2v5ksky8tmrwk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgdgbgr2v5ksky8tmrwk.gif" alt="The same attack hits both designs — only one has something to grab" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A prompt is a &lt;em&gt;request&lt;/em&gt;. A real boundary has to hold even when the AI is confused, tricked, or just wrong. So instead of asking the AI to behave, we changed what it is physically able to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works: four locks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lock 1 — The vault: raw data never touches the AI
&lt;/h3&gt;

&lt;p&gt;All the raw rows live in a small database inside a &lt;strong&gt;vault&lt;/strong&gt; (an MCP server the agent talks to). The agent gets &lt;strong&gt;exactly five tools&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;What it can never do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;describe_dataset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;shows column names and safe info&lt;/td&gt;
&lt;td&gt;never shows actual rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prepare_analysis&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the vault does the math &lt;em&gt;inside its own walls&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;raw values never come out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validate_release&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;runs the checklist (below)&lt;/td&gt;
&lt;td&gt;no AI involved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;release_result&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the real action — waits for a human&lt;/td&gt;
&lt;td&gt;can't run without an Allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;render_safe_chart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;draws a chart from already-released numbers&lt;/td&gt;
&lt;td&gt;can't touch anything else&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice what's missing: there is no "export rows" tool. Not blocked — &lt;strong&gt;absent&lt;/strong&gt;. Trick the AI all you want; the dangerous request has no tool to land on.&lt;/p&gt;

&lt;p&gt;Ask for "tickets by week and region" and the vault groups and counts &lt;em&gt;inside&lt;/em&gt;, then hands out only the totals:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckmpbcbinal40hjvx6gs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckmpbcbinal40hjvx6gs.gif" alt="The math happens inside the vault — only totals come out" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock 2 — The checklist: plain code decides, not the AI
&lt;/h3&gt;

&lt;p&gt;Before anything can be released, &lt;code&gt;validate_release&lt;/code&gt; runs a checklist. This is &lt;strong&gt;plain, boring code — no AI anywhere in it&lt;/strong&gt;. Same input, same answer, every single time. It checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why&lt;/strong&gt; is this being released? Only one approved reason is allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who&lt;/strong&gt; will see it? Only one approved audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which columns?&lt;/strong&gt; Every one must be on a pre-approved list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is every group big enough?&lt;/strong&gt; Every count must cover &lt;strong&gt;at least 3 people&lt;/strong&gt; — a "group" of 2 is small enough to guess who they are, so it's blocked.&lt;/li&gt;
&lt;li&gt;Finally it computes a &lt;strong&gt;fingerprint&lt;/strong&gt; (a sha256 hash) — a unique stamp of the exact numbers being released. Change one digit, the fingerprint changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8fzccabza8eg2xcmahh6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8fzccabza8eg2xcmahh6.gif" alt="The checklist ticks — and a group of 2 gets blocked" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The AI asks. Plain code decides.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock 3 — The pause: a human clicks Allow on the real action
&lt;/h3&gt;

&lt;p&gt;The release tool doesn't just run. TrueForge &lt;strong&gt;pauses the whole turn&lt;/strong&gt; and shows a human exactly what wants to go out: the reason, the audience, the columns, the smallest group size, and the fingerprint. The human clicks Allow or Deny.&lt;/p&gt;

&lt;p&gt;And here's the part I care about most: at the moment it actually runs, the vault &lt;strong&gt;checks everything again&lt;/strong&gt; — every rule, and the fingerprint, recomputed from scratch. If anything is even slightly different from what the human approved: nothing runs, and a note is written to the audit log.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftv1k4y59qclxppbqcpx5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftv1k4y59qclxppbqcpx5.gif" alt="Pause, Allow, then check everything again" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What you approve is exactly what runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock 4 — The receipt: don't trust us, check it
&lt;/h3&gt;

&lt;p&gt;Every release produces a &lt;strong&gt;receipt&lt;/strong&gt;, bundled with the stored server events that produced it. A small command-line tool re-checks the whole story: did prepare → validate → approval → release happen, in that exact order? Do the fingerprints still match when recomputed? It works &lt;strong&gt;offline — no server, no AI key&lt;/strong&gt;, just Node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/MachineLearning-Nerd/need-to-know &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;need-to-know
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;                                            &lt;span class="c"&gt;# 365 tests&lt;/span&gt;
npm run verify-receipt &lt;span class="nt"&gt;--&lt;/span&gt; evidence/attempt-9-bundle.json
&lt;span class="c"&gt;# verify-receipt: PASS receipt=r-4ed4eb7a-... query=q-7ca61fb7-...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk2edhyvrcmmg3nang3ci.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk2edhyvrcmmg3nang3ci.gif" alt="The verifier walks the whole story and says PASS" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What TrueForge actually gave us (the honest review)
&lt;/h2&gt;

&lt;p&gt;You can read a hundred "built on X" posts and never learn what X really did. Here's the real split.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The killer feature: built-in tool approval.&lt;/strong&gt; One line in the agent's config — &lt;code&gt;require_approval_for_tools: ["release_result"]&lt;/code&gt; — and the harness does everything else: the turn pauses on the real action, the pending call and its full arguments show up in the API and the UI, and the human's decision is saved as an event. We didn't build a pause system, an approval queue, or a resume path. The whole project leans on this, and it's one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saved events are what make receipts possible.&lt;/strong&gt; TrueForge stores every event of every turn, and lets you fetch them back. Our verifier never trusts what we &lt;em&gt;saw&lt;/em&gt; live — it always re-fetches what the server &lt;em&gt;stored&lt;/em&gt;. Without that, "here's a receipt" would just mean "trust me."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frozen agent config.&lt;/strong&gt; When a session starts, TrueForge snapshots the agent's setup. Our verifier checks that snapshot against the version we published — so a session can't quietly run with a different tool list than the one we claim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP support.&lt;/strong&gt; The vault plugs in as the agent's &lt;em&gt;only&lt;/em&gt; tool server, registered by one setup script. Those five tools are the entire attack surface, by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask User Questions.&lt;/strong&gt; If the request is missing something (say, who the numbers are for), the agent pauses with a fixed set of choices &lt;em&gt;before touching the vault&lt;/em&gt; — instead of guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generative UI.&lt;/strong&gt; The vault writes its own result cards and the agent must pass them along &lt;em&gt;unchanged&lt;/em&gt; — we check this byte-for-byte, so the AI can't re-word the evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reconnect that doesn't drop events.&lt;/strong&gt; Kill the connection mid-turn, reconnect, and you get every missed event in order. We wrote a proof script for this, because an approval screen that misses events is worse than none.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandbox.&lt;/strong&gt; After a release, the agent re-adds up the released numbers in TrueForge's sandbox and recomputes the fingerprint — and our own check recomputes it again from the stored bytes. Nobody's word is taken for it, not even the sandbox's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we turned OFF on purpose — and one gotcha.&lt;/strong&gt; Sub-agents are disabled: in trueforge 0.1.4, child agents inherit the parent's tools, which would open a side door around our approval trail — so the verifier rejects any run that used them. And a tip that will save you an hour: 0.1.4 only listens on IPv6 — use &lt;code&gt;http://localhost:8891&lt;/code&gt;, never &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three bugs the review bot caught
&lt;/h2&gt;

&lt;p&gt;Every pull request went through Qodo review. Three findings were real, and they're my favorite part of the story:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The checker that was quietly offline&lt;/strong&gt; (PR #8). Our test harness forgot to pass the server address to the verifier — so it silently checked runs in &lt;em&gt;offline&lt;/em&gt; mode while we recorded them as "verified against the live server." The runs were fine; our claim was stronger than our check. Fixed, re-verified properly, and written down in the run log.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The receipt that wouldn't die&lt;/strong&gt; (PR #9). Starting a new analysis didn't clear the old receipt from the console screen — so an old receipt could show up next to a new question. Fixed by wiping the evidence panel on every new analysis and tying every receipt to its own query ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "not" that passed the test&lt;/strong&gt; (PR #11). One of our checks just looked for the fingerprint &lt;em&gt;appearing&lt;/em&gt; in the AI's message. Which means the sentence &lt;em&gt;"this does NOT equal &amp;lt;fingerprint&amp;gt;"&lt;/em&gt; would have &lt;strong&gt;passed&lt;/strong&gt;. A sentence denying the proof would have passed the proof. Fixed by requiring the confirmation to start the message and rejecting words like "not" and "unless."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last one is the whole project in one lesson: don't check that the right words show up — check the thing itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest numbers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;13 recorded demo runs. 12 clean. 1 failure — published, not hidden.&lt;/strong&gt; Run 3 failed because the AI re-worded a card it was supposed to copy exactly — which is precisely the mistake that check exists to catch. The last five runs were clean in a row, and their full evidence bundles are in the repo. Every one of them passes the offline check from a fresh clone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we do NOT claim
&lt;/h2&gt;

&lt;p&gt;Being honest about limits is part of the design, so: we can prove &lt;em&gt;an&lt;/em&gt; approval happened in the right order — not cryptographically prove &lt;em&gt;who&lt;/em&gt; clicked it. The offline check is weaker than the live one (the bundle carries its own events). And none of this defends against the server's own administrator rewriting stored history. The full details are in the repo: &lt;code&gt;docs/THREAT_MODEL.md&lt;/code&gt; and &lt;code&gt;docs/LIMITATIONS.md&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/YxvzagWAh5c" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/MachineLearning-Nerd/need-to-know" rel="noopener noreferrer"&gt;https://github.com/MachineLearning-Nerd/need-to-know&lt;/a&gt; — the 60-second offline check needs only Node ≥ 24.&lt;/p&gt;

&lt;p&gt;The takeaway, in one line each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A prompt is a request. A boundary is what still holds when the AI is wrong.&lt;/li&gt;
&lt;li&gt;Give the agent only the tools it needs — and make the dangerous one not exist.&lt;/li&gt;
&lt;li&gt;Let plain code, not the AI, decide what may go out.&lt;/li&gt;
&lt;li&gt;Pause the real action for a human — then check everything again when it runs.&lt;/li&gt;
&lt;li&gt;Hand out receipts anyone can verify.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can't leak what you can't call.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built in one week for the TrueForge Agent Harness Hackathon by WeMakeDevs × TrueFoundry, with Qodo reviewing every PR. Built with AI coding assistants as pair programmers; every change human-reviewed. All data is synthetic. The animations are Manim.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>llm</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
