<?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: Tejaswi Suresh</title>
    <description>The latest articles on DEV Community by Tejaswi Suresh (@axiorank).</description>
    <link>https://dev.to/axiorank</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%2F4025955%2F516ca6ee-48b9-49f0-9446-dea1d6d0c9e2.png</url>
      <title>DEV Community: Tejaswi Suresh</title>
      <link>https://dev.to/axiorank</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/axiorank"/>
    <language>en</language>
    <item>
      <title>Logs are claims. Receipts are proofs.</title>
      <dc:creator>Tejaswi Suresh</dc:creator>
      <pubDate>Sun, 12 Jul 2026 11:03:45 +0000</pubDate>
      <link>https://dev.to/axiorank/logs-are-claims-receipts-are-proofs-1j23</link>
      <guid>https://dev.to/axiorank/logs-are-claims-receipts-are-proofs-1j23</guid>
      <description>&lt;p&gt;Your AI agent just refunded a customer, deleted a database table, or emailed a spreadsheet to the wrong person. Three weeks later, someone asks what happened.&lt;/p&gt;

&lt;p&gt;What do you have?&lt;/p&gt;

&lt;p&gt;A log line.&lt;/p&gt;

&lt;p&gt;Who wrote it?&lt;/p&gt;

&lt;p&gt;Your own code.&lt;/p&gt;

&lt;p&gt;Could it have been edited?&lt;/p&gt;

&lt;p&gt;Absolutely.&lt;/p&gt;

&lt;p&gt;That is the state of most AI audit trails today, and they're only as trustworthy as whoever controls the logs.&lt;/p&gt;

&lt;p&gt;Payment systems solved this decades ago. You don't get an assertion that money moved—you get a receipt that anyone can verify.&lt;/p&gt;

&lt;p&gt;This post shows how to give every LLM response a cryptographically verifiable receipt, what that receipt actually proves, and just as importantly, what it doesn't.&lt;/p&gt;

&lt;p&gt;Everything below ships in an MIT-licensed gateway you can run with a single command. You don't even need an API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @axiorank/gateway demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What an AI response receipt needs to contain
&lt;/h2&gt;

&lt;p&gt;A useful receipt should commit to at least four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The exact request bytes received by the gateway (stored as a SHA-256 hash)&lt;/li&gt;
&lt;li&gt;The exact response bytes returned (also stored as a SHA-256 hash)&lt;/li&gt;
&lt;li&gt;Everything the gateway did in between:

&lt;ul&gt;
&lt;li&gt;routing decisions&lt;/li&gt;
&lt;li&gt;selected provider&lt;/li&gt;
&lt;li&gt;guardrail verdicts&lt;/li&gt;
&lt;li&gt;redactions applied&lt;/li&gt;
&lt;li&gt;token usage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Its position in history so previous records cannot be silently modified.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A real receipt (trimmed) looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"axiorank-gateway-receipt-v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"modelRequested"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpt-4o-mini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bodySha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"axio/auto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"served"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"upstream"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"groq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llama-3.3-70b-versatile"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"guardrails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"redactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"completion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"redactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bodySha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inputTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outputTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;118&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"seq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prevReceiptHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keyId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a1b2c3..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"algorithm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EdDSA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why canonicalization matters
&lt;/h2&gt;

&lt;p&gt;Signing JSON is harder than it sounds.&lt;/p&gt;

&lt;p&gt;JSON doesn't have a single canonical byte representation. Different serializers can reorder keys, change whitespace, or format numbers differently. Even though the data is identical, the bytes change, which invalidates signatures.&lt;/p&gt;

&lt;p&gt;The solution is RFC 8785 (JSON Canonicalization Scheme), which defines one deterministic serialization that every verifier can reproduce.&lt;/p&gt;

&lt;p&gt;The receipt payload is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Canonicalized using RFC 8785&lt;/li&gt;
&lt;li&gt;Signed using Ed25519&lt;/li&gt;
&lt;li&gt;Stored with a detached signature&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verification only requires standard cryptography libraries—you never have to trust the gateway's own implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning receipts into an audit trail
&lt;/h2&gt;

&lt;p&gt;A signed receipt proves one response.&lt;/p&gt;

&lt;p&gt;An audit trail has to prove something more important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nothing was deleted&lt;/li&gt;
&lt;li&gt;nothing was reordered&lt;/li&gt;
&lt;li&gt;nothing was modified&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every receipt includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a sequence number&lt;/li&gt;
&lt;li&gt;the hash of the previous receipt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together they form a hash chain stored as JSONL.&lt;/p&gt;

&lt;p&gt;If any historical byte changes, every receipt after it fails verification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @axiorank/gateway verify ~/.axiorank/gateway/receipts.jsonl

&lt;span class="c"&gt;# receipt chain valid (128 receipts, key a1b2c3d4)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signing key is generated locally on first run and published through a local JWKS endpoint.&lt;/p&gt;

&lt;p&gt;No account.&lt;/p&gt;

&lt;p&gt;No cloud service.&lt;/p&gt;

&lt;p&gt;No central authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  What receipts do &lt;em&gt;not&lt;/em&gt; prove
&lt;/h2&gt;

&lt;p&gt;It's important to be precise.&lt;/p&gt;

&lt;p&gt;A receipt &lt;strong&gt;does not&lt;/strong&gt; prove anything about what happened inside the model itself.&lt;/p&gt;

&lt;p&gt;It only proves what crossed the gateway boundary.&lt;/p&gt;

&lt;p&gt;It also doesn't prove the gateway operator is honest.&lt;/p&gt;

&lt;p&gt;If the operator is your adversary, you need an external transparency log or witness service (that's what the hosted version provides).&lt;/p&gt;

&lt;p&gt;The local receipt chain provides &lt;strong&gt;tamper evidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If someone edits history, the chain simply stops verifying.&lt;/p&gt;

&lt;p&gt;Blocked requests also generate receipts, turning "our filters caught it" into something that can actually be verified later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guardrails belong at the gateway
&lt;/h2&gt;

&lt;p&gt;Since every model request already passes through the gateway, it's the natural place for baseline security.&lt;/p&gt;

&lt;p&gt;The gateway can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;block prompt injection&lt;/li&gt;
&lt;li&gt;block destructive commands&lt;/li&gt;
&lt;li&gt;redact API keys&lt;/li&gt;
&lt;li&gt;redact PII&lt;/li&gt;
&lt;li&gt;score requests and responses&lt;/li&gt;
&lt;li&gt;include every decision in the signed receipt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything runs in-process.&lt;/p&gt;

&lt;p&gt;No second network request.&lt;/p&gt;

&lt;p&gt;Enabled by default.&lt;/p&gt;

&lt;p&gt;The guardrails are heuristic-based with scored confidence bands—not a silver bullet—and benchmark results against open-source alternatives at a fixed false-positive rate are published.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OpenAI-compatible gateway on :8787&lt;/span&gt;
npx @axiorank/gateway

&lt;span class="c"&gt;# Full offline demo (~15 seconds)&lt;/span&gt;
npx @axiorank/gateway demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is MIT licensed with zero runtime dependencies and includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;34 provider presets&lt;/li&gt;
&lt;li&gt;LangChain examples&lt;/li&gt;
&lt;li&gt;LiteLLM examples&lt;/li&gt;
&lt;li&gt;Vercel AI SDK examples&lt;/li&gt;
&lt;li&gt;Ollama examples&lt;/li&gt;
&lt;li&gt;CrewAI examples&lt;/li&gt;
&lt;li&gt;CI integration examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/AxioRank/gateway" rel="noopener noreferrer"&gt;https://github.com/AxioRank/gateway&lt;/a&gt;&lt;/p&gt;

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