<?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: João André Gomes Marques</title>
    <description>The latest articles on DEV Community by João André Gomes Marques (@jagmarques).</description>
    <link>https://dev.to/jagmarques</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3836862%2Fb366b35b-2375-486a-b867-2535919afb9f.png</url>
      <title>DEV Community: João André Gomes Marques</title>
      <link>https://dev.to/jagmarques</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jagmarques"/>
    <language>en</language>
    <item>
      <title>Asqav TypeScript SDK is live</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Mon, 27 Apr 2026 18:51:51 +0000</pubDate>
      <link>https://dev.to/jagmarques/asqav-now-ships-on-npm-typescript-agent-governance-is-live-310f</link>
      <guid>https://dev.to/jagmarques/asqav-now-ships-on-npm-typescript-agent-governance-is-live-310f</guid>
      <description>&lt;p&gt;If your AI agents run on TypeScript, you have probably noticed every governance and observability tool in the space treats Python as the default and JavaScript as an afterthought. We did the same thing for a while. Today we ship the fix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @asqav/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same REST API as the Python SDK, same ML-DSA-65 signatures generated server-side, same audit trail. The npm package is a thin client that hits api.asqav.com - all crypto stays on our infrastructure. Public surface mirrors Python one-for-one so you can write the same code in either language and get the same behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thirty-second example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@asqav/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ASQAV_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;support-bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;actionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stripe.refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;customer dispute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verificationUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// https://asqav.com/verify/sig_abc123&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That signature is real ML-DSA-65 (FIPS 204), anchored to Bitcoin via OpenTimestamps, retrievable from any compliance auditor without needing your infrastructure online. Same guarantees as the Python flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this unlocks
&lt;/h2&gt;

&lt;p&gt;The interesting frameworks for agents in TypeScript right now are &lt;a href="https://sdk.vercel.ai/" rel="noopener noreferrer"&gt;Vercel AI SDK&lt;/a&gt;, &lt;a href="https://js.langchain.com/" rel="noopener noreferrer"&gt;LangChain.js&lt;/a&gt;, and &lt;a href="https://mastra.ai/" rel="noopener noreferrer"&gt;Mastra&lt;/a&gt;. Each one gives you a tool-calling loop where the model picks an action, you execute it, and the result feeds back. None of them give you a tamper-evident record of what the agent actually did.&lt;/p&gt;

&lt;p&gt;With the npm SDK, wrapping a Vercel AI SDK tool to sign every call is six lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@asqav/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;support-bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refund&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue a refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;actionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stripe.refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refunds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every refund the model decides to issue now has a signature on the way out. Replace the wrapper at the framework level and every tool gets governance for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we waited
&lt;/h2&gt;

&lt;p&gt;Honest answer: shipping a second SDK doubles the surface to maintain. We held off until the Python SDK API stabilised so the TypeScript port could mirror it cleanly instead of forking immediately. The two are now version-locked at the API level - what works in one works in the other. Releases are independent (&lt;code&gt;py-v0.2.22&lt;/code&gt;, &lt;code&gt;ts-v0.1.0&lt;/code&gt;) so we can patch one without touching the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install matrix
&lt;/h2&gt;

&lt;p&gt;Both languages, same brand, same backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Python&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav

&lt;span class="c"&gt;# TypeScript / Node / Deno / Bun&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @asqav/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;github.com/jagmarques/asqav-sdk&lt;/a&gt; (monorepo with &lt;code&gt;python/&lt;/code&gt; and &lt;code&gt;typescript/&lt;/code&gt; side by side, MIT). Docs and dashboard: &lt;a href="https://asqav.com/docs" rel="noopener noreferrer"&gt;asqav.com/docs&lt;/a&gt;. Get an API key at &lt;a href="https://asqav.com" rel="noopener noreferrer"&gt;asqav.com&lt;/a&gt; and sign your first action in under a minute.&lt;/p&gt;

&lt;p&gt;If you are wiring this into Vercel AI SDK or LangChain.js right now and hit a rough edge, open an issue on the SDK repo and we will turn it around fast. The TypeScript surface is fresh and we want to harden it against real usage before more frameworks land on top.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Try asqav in 30 seconds</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Mon, 20 Apr 2026 12:38:26 +0000</pubDate>
      <link>https://dev.to/jagmarques/try-asqav-in-30-seconds-1m6h</link>
      <guid>https://dev.to/jagmarques/try-asqav-in-30-seconds-1m6h</guid>
      <description>&lt;p&gt;Most agent governance tools want you through a signup flow before you see a single screen. We shipped &lt;code&gt;asqav demo&lt;/code&gt; so you can see the product without an account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav[cli]
asqav demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That opens a local dashboard at &lt;code&gt;http://localhost:3030&lt;/code&gt; with four pre-loaded scenarios. No API key, no Docker, no cloud call. The HTTP server runs entirely inside the SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you see
&lt;/h2&gt;

&lt;p&gt;Four risky agent actions, each with the full context a reviewer would need to decide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code wants to rm -rf ~/projects.&lt;/strong&gt; High risk, destructive filesystem rule fires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fintech agent wants to send an 850 000 EUR wire.&lt;/strong&gt; Amount over threshold triggers HITL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevOps agent wants to scale production to zero.&lt;/strong&gt; Namespace + replicas rule blocks it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clinical agent wants to order CT with contrast, allergy not checked.&lt;/strong&gt; Medium risk, reviewer sees the gap.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each card renders six fields: action payload, agent reasoning chain, risk classification with reason, triggering policy name and rule, a required reason textarea (minimum ten characters), and an expected diff preview. Approve or Deny produces an HMAC-signed receipt. The dashboard re-verifies each receipt in the browser and shows whether the signature is valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we built it
&lt;/h2&gt;

&lt;p&gt;Agent governance sits at the top of a long conversation before engineers start wiring SDKs into their code. A demo that runs without a network, without an account, and without a container gives the technical buyer a working artifact to share in thirty seconds. The rest of the integration - real policies, cloud signatures, dashboard - follows once the team agrees this is the shape they want.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;The receipts in the demo use HMAC over canonical JSON. Production asqav uses ML-DSA-65 (FIPS 204) signatures and RFC 8785 JCS canonicalization. Swap &lt;code&gt;asqav demo&lt;/code&gt; for &lt;code&gt;asqav.init(api_key="sk_...")&lt;/code&gt; and you get the real cryptography, the multi-party signing, the incidents and compliance reports. The demo is a preview of the UX and the audit flow, not a stand-in for the production trust chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source and docs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;asqav demo&lt;/code&gt; lives in the SDK, Apache 2.0: &lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;github.com/jagmarques/asqav-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Approval card UX in the real dashboard: &lt;a href="https://www.asqav.com/docs/approvals" rel="noopener noreferrer"&gt;docs/approvals&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Attestation format (third-party verifiable, RFC 8785): &lt;a href="https://www.asqav.com/docs/attestation" rel="noopener noreferrer"&gt;docs/attestation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Governance metadata in A2A Agent Cards, shipping the superset</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sun, 19 Apr 2026 19:56:38 +0000</pubDate>
      <link>https://dev.to/jagmarques/governance-metadata-in-a2a-agent-cards-shipping-the-superset-78k</link>
      <guid>https://dev.to/jagmarques/governance-metadata-in-a2a-agent-cards-shipping-the-superset-78k</guid>
      <description>&lt;p&gt;A2A Agent Cards describe what an agent can do, where it lives, and how to talk to it. What they do not describe is governance posture. Two agents can hold identical cards and behave nothing alike in production.&lt;/p&gt;

&lt;p&gt;We shipped a reference implementation under &lt;code&gt;extensions.asqav.governance&lt;/code&gt; inside the standard AgentCard envelope. Three fields carry the posture: &lt;code&gt;trust_score&lt;/code&gt;, &lt;code&gt;retention_ttl_seconds&lt;/code&gt;, and &lt;code&gt;derivation_rights&lt;/code&gt;. Signed with the agent's own ML-DSA-65 keypair. Docs at &lt;a href="https://www.asqav.com/docs/a2a" rel="noopener noreferrer"&gt;asqav.com/docs/a2a&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The schema
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"research-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://agents.example.com/research"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&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;"asqav.governance"&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;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"agent_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agt_x7y8z9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"trust_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"retention_ttl_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2592000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"derivation_rights"&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;"retention_permitted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"derivative_works"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"third_party_sharing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"license_reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/licenses/research-v1"&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;"issued_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-19T12:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-26T12:00:00+00:00"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"public_key"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why trust_score decays
&lt;/h2&gt;

&lt;p&gt;A discrete L0 to L3 grade hides real signal. An agent that ran clean 90 days ago then went silent is epistemically different from one that just handled a thousand signed actions. A decaying score separates them. 45-day half-life on positive evidence: each successful signed action contributes weight &lt;code&gt;0.5 ^ ((now - t) / 45 days)&lt;/code&gt;. Negative events (suspensions, revocations) apply full-weight penalties that do not decay. Derivation reads signed records only, so independent verifiers get the same number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why derivation_rights needs license_reference
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;derivative_works: false&lt;/code&gt; alone means nothing across organizations. One side reads it as "do not train on my outputs," another as "do not fine tune downstream," another as "do not incorporate into product." The boolean is a coarse intent signal. &lt;code&gt;license_reference&lt;/code&gt; pins down the actual contract, whether that is a CC license, a bespoke DUA, or a proprietary TOS. Machine-readable gating and human-readable legal terms in the same envelope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key rotation
&lt;/h2&gt;

&lt;p&gt;Each agent signs its own extension with its own ML-DSA-65 keypair. The public key is embedded in the signed envelope at issuance time, not fetched by reference. When a key rotates, old attestations stay verifiable forever because the bytes needed to check the signature are already inside the envelope. A third party that captured an attestation in February does not have to call back to asqav in October to confirm it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.asqav.com/api/v1/public/attestation/agt_x7y8z9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Platform discovery is standard A2A: &lt;code&gt;GET /.well-known/agent.json&lt;/code&gt;. Per-agent cards at &lt;code&gt;GET /api/v1/agents/{id}/card&lt;/code&gt;. Parse like any A2A card, look inside &lt;code&gt;extensions&lt;/code&gt; if you care about posture.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Portable Trust for AI Agents</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sun, 19 Apr 2026 15:43:38 +0000</pubDate>
      <link>https://dev.to/jagmarques/portable-trust-for-ai-agents-38il</link>
      <guid>https://dev.to/jagmarques/portable-trust-for-ai-agents-38il</guid>
      <description>&lt;p&gt;When two autonomous agents from different organizations talk to each other, they have to decide whether to trust each other's actions. Today that decision is mostly opaque. One side has its own scoring, the other side has its own thresholds, and reconciliation across providers comes down to whose guardrails you ran through last.&lt;/p&gt;

&lt;p&gt;A recent A2A protocol discussion surfaced the underlying gap. If every layer introduces its own score, grade, or tier, two independent systems cannot deterministically arrive at the same decision, even when every individual signal was valid and signed. What is missing is a notion of decision identity, something that lets different representations be verified as equivalent outcomes rather than as locally consistent ones.&lt;/p&gt;

&lt;p&gt;Governance Attestation is our answer. It is a single signed JSON per agent that any party can fetch, verify offline with ML-DSA-65, and act on without calling our API. One canonical document, one deterministic trust level, one mapping published in our docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is in an attestation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&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;"version"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agt_x7y8z9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asqav"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trust_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"L2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capability_manifest_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:3f2a..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"policy_digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:91c4..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compliance_attestations"&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="s2"&gt;"eu_ai_act_art12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"soc2_audit"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issued_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-19T12:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-26T12:00:00+00:00"&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;p&gt;The body is canonical JSON, signed with the agent's post-quantum ML-DSA-65 key. Signature, public key, and verify URL come back alongside the body. Two independent verifiers recomputing the canonical bytes of the body get the same digest, run the same signature check, and reach the same decision. That is what deterministic trust looks like at the protocol layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trust levels
&lt;/h2&gt;

&lt;p&gt;Four canonical levels, derived deterministically from the agent's state. No subjective scoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L0 Unknown&lt;/strong&gt; - default. No signed activity in the last 30 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L1 Monitored&lt;/strong&gt; - at least one signed action in the last 30 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2 Governed&lt;/strong&gt; - L1, and the owning organization has at least one enabled policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L3 Autonomous&lt;/strong&gt; - L2, and no revocation or suspension in the last 90 days.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the rules are public, an auditor can reconstruct the level from the raw signed records. If they disagree with the outcome, the argument is about the rules, not about whose internal scoring they trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The public verify path
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.asqav.com/api/v1/public/attestation/agt_x7y8z9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No auth. No API key. Any third party fetches the signed envelope. The signature verifies offline against the returned public key using ML-DSA-65. The canonical body tells them the trust level, capability hash, policy digest, and the exact window the attestation is valid for.&lt;/p&gt;

&lt;h2&gt;
  
  
  A2A Agent Card integration
&lt;/h2&gt;

&lt;p&gt;For agents exchanging Agent Cards through A2A, drop the attestation URL and hash into your card. Recipients verify the governance posture before accepting a request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"research-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"governance"&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;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asqav"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attestation_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://asqav.com/attestation/agt_x7y8z9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attestation_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:..."&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Issuing an attestation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST /api/v1/agents/&lt;span class="o"&gt;{&lt;/span&gt;agent_id&lt;span class="o"&gt;}&lt;/span&gt;/attestation
X-API-Key: sk_live_...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A fresh attestation expires after seven days, at which point you issue a new one. That window is short on purpose. Trust is a function of recent behavior, not a static label attached once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this exists on Enterprise
&lt;/h2&gt;

&lt;p&gt;Governance Attestation is a cross-organization trust primitive. It only pays off when you are operating agents that talk to other organizations' agents, usually under contract, usually with legal and compliance sitting on the other side of the table. That is the shape of Enterprise deployments.&lt;/p&gt;

&lt;p&gt;For single-organization setups, audit trails, policy enforcement, and the public verify endpoint still cover everything.&lt;/p&gt;




&lt;p&gt;Full docs: &lt;a href="https://www.asqav.com/docs/attestation" rel="noopener noreferrer"&gt;asqav.com/docs/attestation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Sign the intent, the decision, and the execution</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Wed, 15 Apr 2026 17:36:20 +0000</pubDate>
      <link>https://dev.to/jagmarques/sign-the-intent-the-decision-and-the-execution-amc</link>
      <guid>https://dev.to/jagmarques/sign-the-intent-the-decision-and-the-execution-amc</guid>
      <description>&lt;p&gt;Most governance tools produce a single signed receipt after the fact. The action happened, here is the proof. That approach covers you in the simplest case, but it leaves two critical moments completely unrecorded.&lt;/p&gt;

&lt;p&gt;The first is intent. Before an agent runs anything, it declares what it wants to do. That declaration is worth signing on its own, because it tells you what the agent was trying to accomplish regardless of whether it was allowed to proceed. The second is the policy decision itself, the moment your rules evaluated the request and returned an approval or a denial. If you only sign the final action, you lose both of these, and you are left with no proof that governance actually ran before the agent acted.&lt;/p&gt;

&lt;p&gt;Three-phase signing treats each of these moments as a separate signed record in a chain. The intent gets signed first, capturing the agent, the action, and the context it provided. The decision gets signed next, recording whether the policy approved or denied the request and which rules were evaluated. If the request was approved, the execution gets signed last, confirming the action completed.&lt;/p&gt;

&lt;p&gt;The interesting case is when the policy blocks the action. You still end up with two signed records: the intent and the denial. That means you have a tamper-proof record showing that the agent tried to do something it was not allowed to do, and that your governance layer caught it. For compliance, this is just as valuable as a successful execution chain, because auditors want to see that controls are working, not just that actions happened.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;

&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orchestrator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_with_phases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data:delete:users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cleanup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# signed: agent wanted to delete users
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# policy evaluated and approved/denied
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# signed: action completed (only if approved)
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;approved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# True/False
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each phase in the chain is individually verifiable. You can hand an auditor the intent signature alone and they can confirm it is authentic without needing the rest of the chain. Or you can export the full chain as a single bundle and let them walk through the entire lifecycle of the action from request to completion.&lt;/p&gt;

&lt;p&gt;This also changes how you think about denied actions. Instead of treating a policy denial as a silent non-event, it becomes a first-class record in your audit trail. Over time, the pattern of denied intents tells you as much about your agents as the approved ones do, because it shows you what they are consistently trying to do and being stopped from doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  CrewAI GuardrailProvider
&lt;/h2&gt;

&lt;p&gt;On a related note, we recently shipped a &lt;code&gt;GuardrailProvider&lt;/code&gt; integration for CrewAI that plugs three-phase signing directly into crew task execution. If you are building with CrewAI, the guardrail provider evaluates each agent action against your policies and produces the same signed chain automatically, so you get full intent, decision, and execution records without changing how your crews are structured.&lt;/p&gt;

&lt;p&gt;Source on &lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If something breaks, open an issue.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Trace agent actions across workflows and kill everything in one call</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Wed, 15 Apr 2026 17:05:58 +0000</pubDate>
      <link>https://dev.to/jagmarques/trace-agent-actions-across-workflows-and-kill-everything-in-one-call-aa7</link>
      <guid>https://dev.to/jagmarques/trace-agent-actions-across-workflows-and-kill-everything-in-one-call-aa7</guid>
      <description>&lt;p&gt;Two problems kept coming up while I was building multi-step agent workflows.&lt;/p&gt;

&lt;p&gt;First, when an orchestrator delegates to sub-agents, the audit trail turns into a mess. You get a flat list of signed actions with no way to tell which ones belong to the same workflow. Was that &lt;code&gt;data:read&lt;/code&gt; from the reporting pipeline or the cleanup job? No idea.&lt;/p&gt;

&lt;p&gt;Second, when something goes sideways in production, you need to stop every agent in your organization immediately instead of figuring out which one is causing the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trace correlation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;trace_id&lt;/code&gt; solves the first problem. Generate one ID for the workflow, then pass it to every &lt;code&gt;sign()&lt;/code&gt; call. Every action in that workflow shares the same trace, so you can reconstruct exactly what happened and in what order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;

&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orchestrator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Generate a trace ID for the whole workflow
&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_trace_id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Every action in this workflow shares the same trace
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task:start&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fetch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task:delegate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sub-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sig_abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task:complete&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;parent_id&lt;/code&gt; parameter links delegated actions back to the signature that spawned them. So you get a tree, not a list. The orchestrator signed &lt;code&gt;task:delegate&lt;/code&gt;, and the sub-agent's actions reference that signature as their parent.&lt;/p&gt;

&lt;p&gt;This matters for compliance. When an auditor asks "show me everything this workflow did," you filter by &lt;code&gt;trace_id&lt;/code&gt; and get a complete, ordered chain of signed actions. No guessing, no log correlation hacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emergency halt
&lt;/h2&gt;

&lt;p&gt;The second problem is simpler but scarier. An agent starts behaving unpredictably, or you detect a security incident, and you need a kill switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Something goes wrong? Kill everything.
&lt;/span&gt;&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emergency_halt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security incident&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One call. Every agent registered under your organization gets revoked immediately. Their signatures stop verifying, their scope tokens become invalid, and any in-flight actions get rejected. The halt reason gets recorded in the audit trail so you have a clear record of when and why you pulled the plug.&lt;/p&gt;

&lt;p&gt;You can also halt a specific agent or agent group instead of the whole org. But the point of &lt;code&gt;emergency_halt()&lt;/code&gt; with no target is that you do not need to figure out which agent is the problem when things are on fire. Stop everything, investigate after.&lt;/p&gt;

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

&lt;p&gt;Agent workflows are getting more complex. An orchestrator calls a planner, the planner calls three workers, each worker calls external APIs. Without trace correlation, you are debugging in the dark. Without an emergency halt, you are hoping nothing goes wrong.&lt;/p&gt;

&lt;p&gt;Both features ship in the latest version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav &lt;span class="nt"&gt;--upgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full docs at &lt;a href="https://www.asqav.com/docs/" rel="noopener noreferrer"&gt;asqav.com/docs&lt;/a&gt;. Source on &lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If something breaks, open an issue.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Stop replay attacks on AI agent tokens</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Wed, 15 Apr 2026 06:31:20 +0000</pubDate>
      <link>https://dev.to/jagmarques/stop-replay-attacks-on-ai-agent-tokens-38nj</link>
      <guid>https://dev.to/jagmarques/stop-replay-attacks-on-ai-agent-tokens-38nj</guid>
      <description>&lt;p&gt;Scope tokens let you prove what an AI agent is allowed to do. I wrote about portable scope tokens a few days ago. They travel with requests so the receiving service can verify permissions without calling back to your org.&lt;/p&gt;

&lt;p&gt;But there is a problem I did not cover. If someone intercepts a valid scope token, they can replay it. The token is signed, unexpired, and carries legitimate permissions. Nothing stops a second use.&lt;/p&gt;

&lt;h2&gt;
  
  
  The replay problem
&lt;/h2&gt;

&lt;p&gt;Say your agent creates a scope token with &lt;code&gt;data:read&lt;/code&gt; permission and a one-hour TTL. It sends a request to a partner API with the token attached. An attacker sitting on the network copies that token. They now have 59 minutes to make their own requests using the same token, with the same permissions.&lt;/p&gt;

&lt;p&gt;The signature still verifies. The TTL has not expired. The actions list matches. From the receiver's perspective, the replayed request looks identical to the original.&lt;/p&gt;

&lt;p&gt;Short TTLs reduce the window but do not close it. Even a 60-second TTL gives an attacker 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nonce-based replay protection
&lt;/h2&gt;

&lt;p&gt;The fix in v0.2.14 is straightforward. Every scope token now includes a unique nonce, a random string generated at creation time. The receiver tracks which nonces it has already seen. If a nonce shows up twice, the token gets rejected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api-caller&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_scope_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data:read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Each token has a unique nonce
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# "a1b2c3d4..."
&lt;/span&gt;
&lt;span class="c1"&gt;# Receiver side
&lt;/span&gt;&lt;span class="n"&gt;seen_nonces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_replay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seen_nonces&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Replay detected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;seen_nonces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern is simple: generate, check, reject duplicates. The nonce set only needs to hold entries until the token's TTL expires. After that, the token is invalid anyway, so you can clean up old nonces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use shorter TTLs
&lt;/h2&gt;

&lt;p&gt;Shorter TTLs help. But they create a different problem. Your agent needs to mint new tokens more frequently, adding latency to every request. And even a 1-second TTL is vulnerable to automated replay within that window.&lt;/p&gt;

&lt;p&gt;Nonces and TTLs work together. The TTL limits how long the nonce set needs to persist. The nonce ensures each token is truly single-use regardless of TTL length.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation notes
&lt;/h2&gt;

&lt;p&gt;For most setups, an in-memory set works fine. If you are running multiple receiver instances behind a load balancer, you need a shared store like Redis so all instances see the same nonce set.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;is_replay&lt;/code&gt; helper handles the check and cleanup for you. Pass in the nonce and your set. It returns &lt;code&gt;True&lt;/code&gt; if the nonce was already seen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Production setup with Redis
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_nonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nonce:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Replay detected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install or upgrade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav &lt;span class="nt"&gt;--upgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs at &lt;a href="https://www.asqav.com/docs/" rel="noopener noreferrer"&gt;asqav.com/docs&lt;/a&gt;. Source on &lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If something breaks, open an issue.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Portable scope tokens: prove what your agent can do without calling home</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Tue, 14 Apr 2026 12:40:52 +0000</pubDate>
      <link>https://dev.to/jagmarques/portable-scope-tokens-prove-what-your-agent-can-do-without-calling-home-27p6</link>
      <guid>https://dev.to/jagmarques/portable-scope-tokens-prove-what-your-agent-can-do-without-calling-home-27p6</guid>
      <description>&lt;p&gt;Your agent authenticates with an API key. The external service knows who is calling. But it has no idea what the agent is actually allowed to do.&lt;/p&gt;

&lt;p&gt;This is the gap between authentication and authorization in agent-to-service communication. Auth tokens prove identity. They do not prove permission. When agent A calls external service B, B can verify that A is a legitimate caller, but it cannot verify that A has been authorized to read customer data, trigger a payment, or access a specific endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with calling home
&lt;/h2&gt;

&lt;p&gt;The traditional fix is for service B to call back to A's organization and ask "is this agent allowed to do this?" That works when both services are inside the same network. It falls apart when agents interact with third-party APIs, partner services, or any system outside the org boundary. The callback adds latency, creates a runtime dependency, and requires the external service to know how to reach your authorization system in the first place.&lt;/p&gt;

&lt;p&gt;Most teams skip this entirely. They give agents broad API keys and hope the agent only does what it should. That is not a security model. That is wishful thinking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope tokens
&lt;/h2&gt;

&lt;p&gt;A scope token is a signed, portable proof of what an agent is authorized to do. You create it before the agent makes an outgoing request. It lists the specific actions the agent is permitted to perform, has a time-to-live, and is signed by &lt;a href="https://www.asqav.com" rel="noopener noreferrer"&gt;Asqav&lt;/a&gt; as an independent third party.&lt;/p&gt;

&lt;p&gt;The token travels with the request. The receiving service verifies the signature using Asqav's public key, checks the action list and expiry, and makes its access decision. No callback to your org. No shared secret. No runtime dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;

&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api-caller&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create scope token before calling external API
&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_scope_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data:read:customer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api:call:external&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Attach to outgoing request
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://partner-api.com/data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_header&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token gets attached as an &lt;code&gt;X-Agent-Scope&lt;/code&gt; header. On the partner side, verification is one call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Partner side - verify without calling your org
&lt;/span&gt;&lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify_scope_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Agent-Scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data:read:customer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Verified: agent is authorized for this scope
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What makes this different from OAuth scopes
&lt;/h2&gt;

&lt;p&gt;OAuth scopes are declared during the authorization flow and embedded in the access token. The resource server trusts the authorization server that issued the token. Scope tokens work differently. They are issued per-request (or per-session), signed by an independent third party, and verifiable by anyone with the public key. The receiving service does not need a pre-existing trust relationship with your authorization server.&lt;/p&gt;

&lt;p&gt;This matters for agent-to-agent communication. When your agent calls a partner's agent, there is no shared OAuth provider. Scope tokens give both sides a common verification mechanism without requiring either to adopt the other's auth infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical constraints
&lt;/h2&gt;

&lt;p&gt;Scope tokens are intentionally short-lived. The &lt;code&gt;ttl&lt;/code&gt; parameter sets the expiry in seconds. A token created with &lt;code&gt;ttl=3600&lt;/code&gt; is valid for one hour. After that, the receiving service rejects it. This limits the blast radius if a token leaks.&lt;/p&gt;

&lt;p&gt;Actions follow a hierarchical format: &lt;code&gt;resource:operation:target&lt;/code&gt;. The format is flexible enough to model most permission structures but specific enough to verify programmatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full docs at &lt;a href="https://www.asqav.com/docs/" rel="noopener noreferrer"&gt;asqav.com/docs&lt;/a&gt;. Source on &lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If something does not work the way you expect, open an issue.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Replay what your AI agent did, step by step</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Tue, 14 Apr 2026 09:12:47 +0000</pubDate>
      <link>https://dev.to/jagmarques/replay-what-your-ai-agent-did-step-by-step-53j7</link>
      <guid>https://dev.to/jagmarques/replay-what-your-ai-agent-did-step-by-step-53j7</guid>
      <description>&lt;p&gt;If you're running AI agents in production, you probably have some form of audit trail already. Maybe signed receipts, maybe logs, maybe just hope. But here's the thing, when something goes wrong or an auditor shows up, nobody wants to read raw JSON.&lt;/p&gt;

&lt;p&gt;They want to walk through what the agent did. Step by step. In order.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with raw receipts
&lt;/h2&gt;

&lt;p&gt;I've been building &lt;a href="https://github.com/asqav/asqav" rel="noopener noreferrer"&gt;asqav&lt;/a&gt; for a while now, and the signed action receipts work great as cryptographic proof. Each action gets signed, each signature is chained to the previous one, and you get tamper-evident records of everything your agent did.&lt;/p&gt;

&lt;p&gt;But proof and readability are different things. When you're debugging an incident at 2am or preparing for a compliance review, you need a timeline you can actually follow. Not a folder of JSON blobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  asqav.replay()
&lt;/h2&gt;

&lt;p&gt;The new replay feature takes your signed receipts and turns them into a walkable timeline. It fetches all actions for a given session, orders them chronologically, verifies the hash chain is intact, and hands you something you can actually read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;

&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;timeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agt_abc123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sess_xyz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Human-readable summary of what the agent did
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# Each step in order
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;timeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Verify nothing was tampered with
&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;timeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chain_integrity&lt;/span&gt;  &lt;span class="c1"&gt;# True if hash chain is valid
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chain integrity check is the key part. It doesn't just replay events. It re-verifies every link in the hash chain. If someone deleted a step, modified an action, or inserted something after the fact, it catches it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline compliance bundles
&lt;/h2&gt;

&lt;p&gt;Not every auditor wants to hit your API. Some want a self-contained package they can review independently. That's what export bundles are for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Export a compliance bundle with all signatures
&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export_bundle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eu_ai_act_art12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Anyone can replay from the bundle - no API access needed
&lt;/span&gt;&lt;span class="n"&gt;timeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replay_from_bundle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bundle includes all the signed actions, the public keys needed to verify them, and metadata about which compliance framework it targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters now
&lt;/h2&gt;

&lt;p&gt;The EU AI Act Article 12 requires that high-risk AI systems maintain logs that enable "the reconstruction of the system's activity." Signed receipts are the proof that your agent did what it says it did. Replay is how you present that proof to someone who needs to understand it.&lt;/p&gt;

&lt;p&gt;This isn't theoretical compliance. If you're deploying agents in the EU (or selling to companies that operate there), you'll need reconstructable audit trails. The regulation is already in force for some categories.&lt;/p&gt;

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

&lt;p&gt;asqav is open source and the SDK is on PyPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The replay feature ships in the next release. If you want to get started with signed audit trails today, the &lt;a href="https://asqav.com/docs" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; takes about five minutes.&lt;/p&gt;

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

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>One-click compliance bundles for AI agent audits</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Mon, 13 Apr 2026 15:28:51 +0000</pubDate>
      <link>https://dev.to/jagmarques/one-click-compliance-bundles-for-ai-agent-audits-4o3p</link>
      <guid>https://dev.to/jagmarques/one-click-compliance-bundles-for-ai-agent-audits-4o3p</guid>
      <description>&lt;p&gt;An auditor walks in and asks for evidence that your AI agents are governed. You have signing data scattered across API responses, CSVs from different time ranges, and a vague explanation of how your Merkle verification works. Good luck putting that together under time pressure.&lt;/p&gt;

&lt;p&gt;This is the compliance evidence problem. You have the data, but packaging it into something an auditor can actually verify takes hours of manual work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compliance bundles in asqav 0.2.11
&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;export_bundle&lt;/code&gt; function takes a list of signatures and a compliance framework identifier, then returns a self-contained JSON document with a Merkle root. One file. Everything an auditor needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;

&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;compliance-demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Collect signatures from your agent pipeline
&lt;/span&gt;&lt;span class="n"&gt;signatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;signatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql-read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;signatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http-external&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api.openai.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;signatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file-write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/reports/q1.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;# Package into a compliance bundle
&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export_bundle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eu_ai_act_art12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audit-q1-2026.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output JSON contains the framework metadata, every signature receipt, individual receipt hashes, and a Merkle root computed over all of them. An auditor can independently verify the root by re-hashing the receipts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four supported frameworks
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;framework&lt;/code&gt; parameter accepts four values out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eu_ai_act_art12&lt;/code&gt; - EU AI Act Article 12 record-keeping requirements&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eu_ai_act_art14&lt;/code&gt; - EU AI Act Article 14 human oversight requirements&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dora_ict&lt;/code&gt; - Digital Operational Resilience Act (DORA) for financial services&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;soc2&lt;/code&gt; - SOC 2 Type II audit evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each framework maps to specific metadata that gets embedded in the bundle, so the auditor knows exactly which standard the evidence targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is in the bundle
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ComplianceBundle&lt;/code&gt; dataclass gives you several ways to work with the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Inspect before saving
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merkle_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# SHA-256 Merkle root
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipt_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Number of signatures included
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;framework&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# "eu_ai_act_art12"
&lt;/span&gt;
&lt;span class="c1"&gt;# Export options
&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audit.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Write to disk
&lt;/span&gt;&lt;span class="n"&gt;json_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;# Get JSON string
&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;       &lt;span class="c1"&gt;# Get plain dict
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verification section of the JSON includes the Merkle root, the hash algorithm (SHA-256), and individual receipt hashes so anyone can rebuild the tree and confirm nothing was tampered with.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it fits into a real workflow
&lt;/h2&gt;

&lt;p&gt;You probably already have signatures from your agent pipeline. The typical flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your agents run and sign actions through asqav (LangChain, CrewAI, whatever framework you use)&lt;/li&gt;
&lt;li&gt;At the end of an audit period, pull signatures via &lt;code&gt;asqav.export_audit_json()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Feed them into &lt;code&gt;asqav.export_bundle(signatures, "eu_ai_act_art12")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hand the auditor one JSON file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more exporting CSVs. No more explaining your signing pipeline. The bundle is self-describing and independently verifiable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;asqav&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.2.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compliance module works entirely client-side. Merkle root computation happens locally using SHA-256, so no extra API calls are needed beyond the signatures you already have.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://asqav.com" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Test AI agent governance without touching production</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Mon, 13 Apr 2026 15:24:18 +0000</pubDate>
      <link>https://dev.to/jagmarques/test-ai-agent-governance-without-touching-production-19ie</link>
      <guid>https://dev.to/jagmarques/test-ai-agent-governance-without-touching-production-19ie</guid>
      <description>&lt;p&gt;You built an AI agent pipeline. It works. Users depend on it. Now someone asks you to add governance: audit trails, policy checks, cryptographic signing. Reasonable request. But the thought of plugging a new library into a production pipeline makes your stomach turn.&lt;/p&gt;

&lt;p&gt;What if it adds latency? What if a signing failure kills a chain run? What if a policy misconfiguration blocks legitimate actions?&lt;/p&gt;

&lt;p&gt;This is the observe mode problem. You need to see what governance would do before you let it do anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution: observe mode
&lt;/h2&gt;

&lt;p&gt;asqav 0.2.11 ships an &lt;code&gt;observe&lt;/code&gt; flag on every framework adapter. When enabled, the handler logs every action it would sign but makes zero API calls. Your pipeline runs exactly as before. You just get visibility into what governance would look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asqav.extras.langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsqavCallbackHandler&lt;/span&gt;

&lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# observe=True: logs actions, never calls the signing API
&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsqavCallbackHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;callbacks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;span class="c1"&gt;# Check your logs for lines like:
# OBSERVE: would sign chain:start with context {...}
# OBSERVE: would sign llm:start with context {...}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler hooks into every chain start, tool call, and LLM interaction. With &lt;code&gt;observe=True&lt;/code&gt;, each event gets logged with the exact action type and context that would have been signed. No network calls. No latency. No risk.&lt;/p&gt;

&lt;p&gt;Once you are comfortable with what you see in the logs, flip &lt;code&gt;observe=False&lt;/code&gt; and governance goes live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic patterns instead of glob strings
&lt;/h2&gt;

&lt;p&gt;Another friction point: action type strings. In older versions you had to remember namespaces like &lt;code&gt;data:delete:*&lt;/code&gt; or &lt;code&gt;api:call:external:*&lt;/code&gt;. Now you can use semantic labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asqav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Before: agent.sign("data:delete:*", {"table": "users"})
# Now:
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql-destructive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK resolves &lt;code&gt;sql-destructive&lt;/code&gt; to &lt;code&gt;data:delete:*&lt;/code&gt; internally. Other built-in patterns include &lt;code&gt;sql-read&lt;/code&gt;, &lt;code&gt;file-write&lt;/code&gt;, &lt;code&gt;http-external&lt;/code&gt;, &lt;code&gt;shell-execute&lt;/code&gt;, &lt;code&gt;pii-access&lt;/code&gt;, and more. You can still use raw glob strings if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preflight with plain-English explanations
&lt;/h2&gt;

&lt;p&gt;Before 0.2.11, &lt;code&gt;agent.preflight()&lt;/code&gt; returned a boolean and a list of reason codes. Now it includes a human-readable explanation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preflight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql-destructive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cleared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# False
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# "Blocked: action not permitted by current policy rules"
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# ["blocked by policy: no-delete-production"]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for debugging policy configurations and for showing end users why an agent action was denied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;asqav&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.2.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK is open source, MIT licensed, and works with LangChain, CrewAI, LlamaIndex, OpenAI Agents, smolagents, DSPy, LiteLLM, and Haystack. Start with observe mode, review the logs, then go live when you are ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jagmarques/asqav-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://asqav.com" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Layer 1 is identity, Layer 2 is attestation</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sun, 12 Apr 2026 17:01:12 +0000</pubDate>
      <link>https://dev.to/jagmarques/layer-1-is-identity-layer-2-is-attestation-353b</link>
      <guid>https://dev.to/jagmarques/layer-1-is-identity-layer-2-is-attestation-353b</guid>
      <description>&lt;p&gt;AI agents are getting identity systems. DIDs, Ed25519 signatures, certificate-based auth. The tooling is growing fast. Microsoft shipped Entra agent identity. AgentNexus brought decentralized identifiers to autonomous agents. This is good work.&lt;/p&gt;

&lt;p&gt;But identity only answers one question: who is this agent?&lt;/p&gt;

&lt;p&gt;It does not answer the harder question: what did this agent actually do, and can it prove it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Two layers, two problems
&lt;/h2&gt;

&lt;p&gt;You can think of agent governance as a stack with two layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 is identity.&lt;/strong&gt; The agent holds a signing key. It can prove who it is to other systems. DID documents, Ed25519 keypairs, X.509 certificates. These all live here. The agent controls the key and uses it to authenticate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 is attestation.&lt;/strong&gt; A separate system, one the agent does not control, certifies what the agent did. Server-side signatures, certifying proxies, neutral third-party receipts. The signing key never touches the agent.&lt;/p&gt;

&lt;p&gt;The distinction matters more than it looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The student grading their own exam
&lt;/h2&gt;

&lt;p&gt;Layer 1 alone has a structural problem. If the agent controls the signing key, a compromised agent can forge its own identity proofs. It can sign falsified logs. It can claim it did things it never did, or hide things it actually did.&lt;/p&gt;

&lt;p&gt;Self-attestation is a student grading their own exam. It works when trust is high and stakes are low. For anything serious (compliance, audit trails, legal evidence) you need a proctor.&lt;/p&gt;

&lt;p&gt;Layer 2 gives you the proctor. The attestation comes from something the agent cannot tamper with. A server-side signer. A certifying proxy sitting between the agent and the outside world. A receipt generated by infrastructure the agent never touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why compliance needs both
&lt;/h2&gt;

&lt;p&gt;Under the EU AI Act, Article 12 requires automatic logging for high-risk AI systems. But self-generated logs from the agent itself are weak evidence. If the agent produced the log and signed it with its own key, what stops a compromised agent from producing a clean log?&lt;/p&gt;

&lt;p&gt;Tamper-evident records need to come from outside the agent. That is what Layer 2 provides.&lt;/p&gt;

&lt;p&gt;You need Layer 1 to know who the agent is. You need Layer 2 to know what it did and that it cannot deny it. Identity plus attestation. Both layers, working together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The current gap
&lt;/h2&gt;

&lt;p&gt;Most teams building agents today are focused on Layer 1. Identity is well-understood. We have decades of PKI experience to draw from. The patterns are familiar.&lt;/p&gt;

&lt;p&gt;Layer 2 is less explored. Tools like ArkForge are building certifying proxies. asqav takes a server-side ML-DSA-65 approach where the signing key stays on the server. These are early but they point in the right direction.&lt;/p&gt;

&lt;p&gt;The gap is real. Teams are shipping agents with strong identity and no independent attestation. That is like having a passport with no customs stamps. You can prove who you are but not where you have been.&lt;/p&gt;

&lt;p&gt;If you are building agent infrastructure, Layer 2 deserves as much attention as Layer 1. It is harder to get right. It is also where compliance actually lives.&lt;/p&gt;

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