<?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>One Receipt, Nine Regulators</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sat, 09 May 2026 22:50:17 +0000</pubDate>
      <link>https://dev.to/jagmarques/one-receipt-nine-regulators-gbk</link>
      <guid>https://dev.to/jagmarques/one-receipt-nine-regulators-gbk</guid>
      <description>&lt;p&gt;The IETF Internet-Draft for AI agent Compliance Receipts grew up. What started as a binding to EU AI Act Article 12 is now a bindings table across nine regulatory regimes: EU AI Act, DORA, NYDFS Part 500, Colorado AI Act, Texas TRAIGA, NIST AI RMF, CIRCIA, HIPAA Security Rule, and SEC 17 CFR 240.17a-4.&lt;/p&gt;

&lt;p&gt;The same wire envelope satisfies all of them. The same conformance vectors prove it. The same Audit Pack export carries the regime mapping a regulator needs without the Deployer writing a per-regime adapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things that follow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One vocabulary, nine retention floors.&lt;/strong&gt; A receipt that touches a HIPAA-covered Action retains for the floor HIPAA mandates. A DORA-bound Action retains for the DORA floor. The Audit Pack carries the regime tag inline so the cleanup engine does not need to guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-jurisdiction queries.&lt;/strong&gt; A Deployer subject to EU AI Act Article 26 AND NYDFS Part 500 can answer one query - "show me every Article 26-relevant Action that crossed a NYDFS boundary" - against one receipt store, not two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forward-compatible.&lt;/strong&gt; Adding the next regime is a binding-table edit, not an envelope rewrite. The wire format is stable; the regime mapping is data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;

&lt;p&gt;A Compliance Receipt is the same JSON envelope across jurisdictions. The regime tag travels in the Audit Pack metadata; the receipt body stays canonical:&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"protectmcp:decision"&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_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;"lei:529900T8BM49AURSDO55"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action_ref"&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:9f2e..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"previous_receipt_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:6c41..."&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:b71a..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"incident_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signed_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-05-10T09:14:22Z"&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 reference implementation runs at &lt;code&gt;api.asqav.com&lt;/code&gt;. The conformance harness walks the normative clauses against the live cloud and reports per-clause coverage. Verifying any receipt is a single unauthenticated GET.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asqav
asqav demo          &lt;span class="c"&gt;# produces a Compliance Receipt against the live cloud&lt;/span&gt;
curl https://api.asqav.com/api/v1/verify/&amp;lt;signature_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you build agent governance, the path from "logs everywhere" to "one verifiable receipt" goes through this draft.&lt;/p&gt;

&lt;p&gt;Draft: &lt;a href="https://datatracker.ietf.org/doc/draft-marques-asqav-compliance-receipts/" rel="noopener noreferrer"&gt;https://datatracker.ietf.org/doc/draft-marques-asqav-compliance-receipts/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>compliance</category>
      <category>opensource</category>
    </item>
    <item>
      <title>An IETF profile for AI agent compliance receipts</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Mon, 04 May 2026 11:58:11 +0000</pubDate>
      <link>https://dev.to/jagmarques/why-a-profile-not-a-fork-extending-the-ietf-signed-receipts-draft-for-eu-ai-act-2clg</link>
      <guid>https://dev.to/jagmarques/why-a-profile-not-a-fork-extending-the-ietf-signed-receipts-draft-for-eu-ai-act-2clg</guid>
      <description>&lt;p&gt;We published an IETF Internet-Draft, &lt;code&gt;draft-marques-asqav-compliance-receipts&lt;/code&gt;, that profiles the upstream &lt;code&gt;draft-farley-acta-signed-receipts&lt;/code&gt; envelope for EU AI Act and DORA bindings. This post explains what the profile does and why each piece is there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the profile does
&lt;/h2&gt;

&lt;p&gt;The upstream draft specifies a generic signed receipt envelope for AI agent actions: a wire format, a canonicalization rule, a signature suite, and an optional hash chain. It is intentionally regulation-agnostic. The Asqav profile takes that envelope as-is and adds four things on top of it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It tightens fields that the upstream draft marks OPTIONAL into REQUIRED for any receipt that claims the profile, including &lt;code&gt;payload_digest&lt;/code&gt;, &lt;code&gt;action_ref&lt;/code&gt;, and &lt;code&gt;policy_digest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It sets a retention floor tied to the underlying regulation: six months for high-risk AI Act receipts, five years for DORA receipts.&lt;/li&gt;
&lt;li&gt;It mandates dual-anchoring. Every receipt carries an RFC 3161 timestamp and an OpenTimestamps witness.&lt;/li&gt;
&lt;li&gt;It adds two extension fields, &lt;code&gt;risk_class&lt;/code&gt; and &lt;code&gt;incident_class&lt;/code&gt;, drawn from controlled vocabularies that match the regulatory text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A receipt that conforms to the Asqav profile is, by construction, a conformant upstream receipt. The signature validates with stock libraries. The chain validates with stock verifiers. A verifier that does not know about EU AI Act can still walk the receipt and confirm cryptographic facts; it just cannot attest the compliance bindings. Cryptographic validity and compliance attestation are different claims, and they fail independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete binding
&lt;/h2&gt;

&lt;p&gt;Article 12(2)(c) of the EU AI Act requires operators of high-risk systems to monitor the operation of the system and detect substantial modifications. The profile binds Article 12(2)(c) to the &lt;code&gt;policy_digest&lt;/code&gt; field. A change in &lt;code&gt;policy_digest&lt;/code&gt; between two otherwise-comparable Actions, same &lt;code&gt;issuer_id&lt;/code&gt;, same &lt;code&gt;action_ref&lt;/code&gt; family, same &lt;code&gt;risk_class&lt;/code&gt;, is treated as a candidate substantial-modification event. The verifier surfaces those candidates. A human decides whether the change was substantial in the regulatory sense. The receipt log is the evidence trail. The binding lives in the field-level conformance rule, not in a separate PDF.&lt;/p&gt;

&lt;p&gt;DORA Article 17 works the same way. The upstream draft says retention is out of scope. The profile sets a five-year floor for any receipt whose &lt;code&gt;issuer_id&lt;/code&gt; is bound to a Financial Entity scope, and ties that floor to the OpenTimestamps anchor so deletion before the floor expires is detectable from the chain alone. If a receipt's anchor proves it existed five years ago and the producer cannot show it now, that is a finding. The supervisor does not have to take anyone's word for it.&lt;/p&gt;

&lt;p&gt;The current draft has eleven such bindings, six against the AI Act and five against DORA. The pattern is the same in every case: pick the regulatory obligation, identify the receipt field that already carries the relevant fact, write a conformance rule that a verifier can mechanically check.&lt;/p&gt;

&lt;p&gt;Read the draft on the &lt;a href="https://datatracker.ietf.org/doc/draft-marques-asqav-compliance-receipts/" rel="noopener noreferrer"&gt;IETF Datatracker&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Verify is not just true or false, granular outcomes on /verify</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sun, 03 May 2026 18:16:42 +0000</pubDate>
      <link>https://dev.to/jagmarques/verify-is-not-just-true-or-false-granular-outcomes-on-verify-25dg</link>
      <guid>https://dev.to/jagmarques/verify-is-not-just-true-or-false-granular-outcomes-on-verify-25dg</guid>
      <description>&lt;p&gt;Until last week the public verify endpoint at &lt;code&gt;GET /api/v1/verify/{signature_id}&lt;/code&gt; returned a single boolean: &lt;code&gt;{verified: true}&lt;/code&gt; or &lt;code&gt;{verified: false}&lt;/code&gt;. That works for the buyer's first-glance UX. It does not work for a regulator or a court trying to figure out what actually went wrong.&lt;/p&gt;

&lt;p&gt;An older trust-data-infrastructure design we read for context lays this out: their verify can return Valid Document, Invalid Signature, Incorrect Hash, Invalid Receiver Public Key, or Valid Document (Adulteration Not Detected). Each label is a different forensic story. Invalid Receiver Public Key means substitution or man-in-the-middle. Incorrect Hash means transit corruption. Invalid Signature means key compromise.&lt;/p&gt;

&lt;p&gt;A boolean cannot carry that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we added
&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;"verified"&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;"verification_detail"&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;"signer_key_match"&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;"signature_valid"&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;"algorithm_match"&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;"agent_active"&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;"validation_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"valid"&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;p&gt;Four sub-checks plus a stable label. The label resolves to one of: &lt;code&gt;valid&lt;/code&gt;, &lt;code&gt;invalid_signature&lt;/code&gt;, &lt;code&gt;signer_key_changed&lt;/code&gt;, &lt;code&gt;algorithm_mismatch&lt;/code&gt;, &lt;code&gt;agent_inactive&lt;/code&gt;, &lt;code&gt;agent_unknown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The field is optional. Older clients that do not know about it ignore it. New clients render the per-axis breakdown. Backwards-compatible by construction.&lt;/p&gt;

&lt;p&gt;The public verify HTML at &lt;code&gt;asqav.com/verify/&amp;lt;signature_id&amp;gt;&lt;/code&gt; got a small grid below the existing fields, conditional on the API returning the detail.&lt;/p&gt;

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

&lt;p&gt;A regulator asks: when did this signature go bad? Was it the day the key was rotated? You want a label, not a bool.&lt;/p&gt;

&lt;p&gt;A court admissibility question: was this record signed by the agent the policy claims signed it, or by something with a different public key? You want &lt;code&gt;signer_key_match&lt;/code&gt; as its own line.&lt;/p&gt;

&lt;p&gt;PR with the diff: &lt;a href="https://github.com/jagmarques/asqav/pull/141" rel="noopener noreferrer"&gt;github.com/jagmarques/asqav#141&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Every rejection leaves a trail, persistent log of rejected attempts</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sun, 03 May 2026 18:11:20 +0000</pubDate>
      <link>https://dev.to/jagmarques/every-rejection-leaves-a-trail-persistent-log-of-rejected-attempts-516o</link>
      <guid>https://dev.to/jagmarques/every-rejection-leaves-a-trail-persistent-log-of-rejected-attempts-516o</guid>
      <description>&lt;p&gt;A rejection is data. Until last week we were throwing it away.&lt;/p&gt;

&lt;p&gt;If an attacker submitted a forged signature against the public verify endpoint, we returned &lt;code&gt;404 signature_not_found&lt;/code&gt; and that was the entire footprint. Same for a cross-org access attempt on the replay endpoint, an agent that got suspended mid-run trying to sign once more, or a probe walking through random &lt;code&gt;sig_*&lt;/code&gt; ids.&lt;/p&gt;

&lt;p&gt;An older trust-data-infrastructure project we read during a cold audit is explicit about this: log every invocation including the invalid ones, "para auditoria futura". It is the right call. We borrowed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What landed
&lt;/h2&gt;

&lt;p&gt;One new table, &lt;code&gt;rejected_attempts&lt;/code&gt;. Indexed on &lt;code&gt;(organization_id, created_at)&lt;/code&gt; and &lt;code&gt;(agent_id, created_at)&lt;/code&gt; for fast org-scoped time-window scans.&lt;/p&gt;

&lt;p&gt;One helper, &lt;code&gt;log_rejected_attempt(db, request, endpoint, failure_reason, ...)&lt;/code&gt;. Captures IP, User-Agent, X-Request-ID. Truncates user-supplied bytes. Commits its own row. Swallows persistence failures so logging cannot become a 5xx.&lt;/p&gt;

&lt;p&gt;Wired today at four sites: &lt;code&gt;GET /verify/{record_id}&lt;/code&gt; (signature and approval not-found branches), &lt;code&gt;GET /signatures/{id}&lt;/code&gt; and &lt;code&gt;POST /signatures/{id}/replay&lt;/code&gt; (not-found and cross-org branches). Sign and countersign sites in &lt;code&gt;routes/agents.py&lt;/code&gt; come next.&lt;/p&gt;

&lt;h2&gt;
  
  
  How operators query it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET /api/v1/observability/rejected-attempts&lt;/code&gt;, Pro+ tier, scoped to your organization. Filters: &lt;code&gt;failure_reason&lt;/code&gt;, &lt;code&gt;agent_id&lt;/code&gt;, time window, pagination.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with the data
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Probe alerting. N rejections from one IP in M minutes is a probe.&lt;/li&gt;
&lt;li&gt;Suspended-agent retries. A revoked or decommissioned agent that keeps trying is misconfigured or compromised.&lt;/li&gt;
&lt;li&gt;Cross-org access attempts. A bearer token from one org hitting a signature_id from another org is the kind of thing you find out about months later. Now you find out the same hour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PR with the diff, the migration, and tests: &lt;a href="https://github.com/jagmarques/asqav/pull/140" rel="noopener noreferrer"&gt;github.com/jagmarques/asqav#140&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Per-agent hash chain, with a verifier that re-derives</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sun, 03 May 2026 18:05:22 +0000</pubDate>
      <link>https://dev.to/jagmarques/per-agent-hash-chain-with-a-verifier-that-re-derives-3g9b</link>
      <guid>https://dev.to/jagmarques/per-agent-hash-chain-with-a-verifier-that-re-derives-3g9b</guid>
      <description>&lt;p&gt;We hash-chain every signature record so a tamper or a delete is detectable on a later integrity check. The chain has been live for months. It worked. It also had two real problems we did not surface until we did a cold audit against an older trust-data-infrastructure (TDI) project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 1: the chain was global
&lt;/h2&gt;

&lt;p&gt;Every new &lt;code&gt;SignatureRecord&lt;/code&gt; took its &lt;code&gt;previous_hash&lt;/code&gt; from the most recent record across the entire database. Not the most recent record for the same agent. Not the most recent record for the same organization. The most recent record, period.&lt;/p&gt;

&lt;p&gt;That makes a single global chain across every tenant. If tenant A's record is mutated or removed, the chain breaks at the next record from any tenant. A long-running customer with an active integration could have their chain show as broken because a different customer in a different country had a row deleted by a cleanup job.&lt;/p&gt;

&lt;p&gt;It also has a write hot spot. Two parallel signs across any two tenants both read &lt;code&gt;MAX(id)&lt;/code&gt;, both compute the same &lt;code&gt;previous_hash&lt;/code&gt;, both insert. The integrity check then flags the second insert as broken even though no one tampered with anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 2: the verifier never re-derived
&lt;/h2&gt;

&lt;p&gt;The verifier walked records in order and checked that each row's &lt;code&gt;previous_hash&lt;/code&gt; matched the prior row's stored &lt;code&gt;record_hash&lt;/code&gt;. That catches a deleted row. It does not catch a row mutation that also rewrites its own &lt;code&gt;record_hash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix is to re-derive &lt;code&gt;record_hash&lt;/code&gt; from the canonical fields and compare. If the recomputed hash differs from the stored one, that row was mutated.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we shipped
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Scope chain heads per-agent.&lt;/li&gt;
&lt;li&gt;Re-derive &lt;code&gt;record_hash&lt;/code&gt; in the verifier.&lt;/li&gt;
&lt;li&gt;Tag legacy cross-agent links as &lt;code&gt;legacy_global&lt;/code&gt; rather than treat them as broken.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No backfill in this PR. Old records keep their global-chain values; new records use per-agent from this commit forward.&lt;/p&gt;

&lt;p&gt;PR with the diff and tests: &lt;a href="https://github.com/jagmarques/asqav/pull/138" rel="noopener noreferrer"&gt;github.com/jagmarques/asqav#138&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run a similar chain anywhere (audit log, ledger, payments), the two checks above are worth a 30-minute look. Global ordering and stored-hash comparison both feel safe and both have failure modes that quietly do not surface until someone audits them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Govern AI agents from your CLI</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sat, 02 May 2026 13:40:18 +0000</pubDate>
      <link>https://dev.to/jagmarques/govern-ai-agents-from-your-cli-5f4e</link>
      <guid>https://dev.to/jagmarques/govern-ai-agents-from-your-cli-5f4e</guid>
      <description>&lt;p&gt;The Asqav CLI used to be a thin wrapper around the Python SDK. As of this release, the CLI is the primary integration surface, with feature parity between the Python and TypeScript distributions and a stable command set you can wire into Makefiles, GitHub Actions, and pre-commit hooks.&lt;/p&gt;

&lt;p&gt;Install via pip or npm, both ship the same subcommands:&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]
&lt;span class="c"&gt;# or&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @asqav/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What you can do without leaving the terminal
&lt;/h2&gt;

&lt;p&gt;The CLI exposes the operations a reviewer or a DevOps engineer needs day-to-day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;asqav preflight&lt;/code&gt; runs an action through the policy engine in dry-run mode, prints the decision and the triggering rule, and exits non-zero if blocked. Useful in CI before merging an agent change.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asqav replay&lt;/code&gt; reconstructs the receipt timeline for a workflow and verifies every signature against the public key. Works offline against an exported bundle.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asqav budget&lt;/code&gt; shows the current per-agent spend, the rate-limit window, and the next reset. Pipes into Slack alerts cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asqav approve&lt;/code&gt; resolves a pending approval card from the terminal. Reason text required, captured into the signed receipt.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asqav compliance export&lt;/code&gt; bundles receipts, policy versions, and incident records into a portable archive ready for auditor handoff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tier-gated commands, multi-party signing flows, retention policies, and SCITT submission are namespaced under &lt;code&gt;asqav enterprise&lt;/code&gt; and surface a clear license-required error on Free.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short demo session
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asqav login
&lt;span class="go"&gt;Paste API key: sk_***

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asqav preflight &lt;span class="nt"&gt;--agent&lt;/span&gt; fintech-bot &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;    --action wire_transfer --amount 850000 --currency EUR
DECISION: APPROVAL_REQUIRED
&lt;/span&gt;&lt;span class="gp"&gt;RULE:     wire.amount &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;500000
&lt;span class="go"&gt;NEXT:     asqav approve act_01HZX...

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asqav approve act_01HZX9 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;    --reason "Verified counterparty + invoice 2026-INV-1812"
RECEIPT:  rcpt_01HZXA2K... (ML-DSA-65)
STATUS:   verified

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asqav replay trace_q4_payroll
&lt;span class="go"&gt;12 actions, 12 signatures verified
0 incidents, 0 budget violations

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asqav compliance &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--since&lt;/span&gt; 2026-04-01 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;    --out evidence-2026-04.zip
wrote 1247 receipts, 14 policy versions, 3 incidents
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command writes to stdout in a parseable shape (&lt;code&gt;--json&lt;/code&gt; for machine output) and exits non-zero on policy violation. The CLI is the contract you need for Makefile-driven environments and for build agents that should fail closed when an action is blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same commands, two languages
&lt;/h2&gt;

&lt;p&gt;The Python and TypeScript CLIs read the same config file (&lt;code&gt;~/.asqav/config.toml&lt;/code&gt;), use the same auth scheme, and produce identical output. Pick whichever fits your stack. A Vercel deployment running the TypeScript CLI in a GitHub Action talks to the same backend as a Python service running the Python CLI in a cron job, against the same signed receipts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it in
&lt;/h2&gt;

&lt;p&gt;The CLI is meant to be boring infrastructure. Three patterns we use ourselves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pre-commit hook: &lt;code&gt;asqav preflight&lt;/code&gt; against the staged agent change, blocks the commit if the policy rejects it.&lt;/li&gt;
&lt;li&gt;GitHub Action job: &lt;code&gt;asqav compliance export&lt;/code&gt; on a weekly schedule, uploads the zip as a release artifact.&lt;/li&gt;
&lt;li&gt;On-call paging: &lt;code&gt;asqav replay&lt;/code&gt; on the failing trace ID, attached to the incident channel inside two minutes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Source and docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python CLI: &lt;a href="https://pypi.org/project/asqav/" rel="noopener noreferrer"&gt;pypi.org/project/asqav&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TypeScript CLI: &lt;a href="https://www.npmjs.com/package/@asqav/cli" rel="noopener noreferrer"&gt;npmjs.com/package/@asqav/cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Command reference: &lt;a href="https://www.asqav.com/docs/cli" rel="noopener noreferrer"&gt;docs/cli&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open a terminal, run &lt;code&gt;asqav preflight&lt;/code&gt; against your next agent change, and the dashboard becomes the optional surface, not the required one.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>ML-DSA receipts in COSE for SCITT transparency services</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sat, 02 May 2026 13:35:11 +0000</pubDate>
      <link>https://dev.to/jagmarques/ml-dsa-receipts-in-cose-for-scitt-transparency-services-12lg</link>
      <guid>https://dev.to/jagmarques/ml-dsa-receipts-in-cose-for-scitt-transparency-services-12lg</guid>
      <description>&lt;p&gt;SCITT, the Supply Chain Integrity, Transparency, and Trust working group at IETF, is the standards track for append-only logs that hold cryptographic statements about software, models, and now AI agent actions. Transparency services consume signed statements, append them to a Merkle log, and hand back inclusion proofs. The receipts have to be in a format the service understands. That format is COSE.&lt;/p&gt;

&lt;p&gt;Asqav now exports any signed receipt as a COSE_Sign1 envelope on demand, so you can submit Asqav-signed agent actions to a SCITT transparency service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SCITT
&lt;/h2&gt;

&lt;p&gt;An organisation that wants third-party-verifiable evidence of agent behaviour does not want to run its own append-only log. SCITT separates the signer from the log operator. The signer says "I claim this happened". The transparency service says "I have witnessed and ordered this claim". A consumer checks both signatures and gets non-repudiable, ordered evidence without trusting either party alone.&lt;/p&gt;

&lt;p&gt;For AI Act Article 12 records, DORA operational logs, and SOC 2 system descriptions, this two-party setup is what auditors expect for the next decade. Self-hosted log files do not pass that bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  What COSE_Sign1 looks like
&lt;/h2&gt;

&lt;p&gt;COSE, RFC 9052, is the CBOR analogue of JOSE. A COSE_Sign1 message is a four-element CBOR array:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;protected header, a serialised CBOR map with the algorithm and any required parameters&lt;/li&gt;
&lt;li&gt;unprotected header, a CBOR map for hints like the key ID&lt;/li&gt;
&lt;li&gt;payload, the bytes being signed (canonicalised receipt JSON)&lt;/li&gt;
&lt;li&gt;signature, the raw ML-DSA-65 signature over the Sig_structure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The whole envelope is wrapped with CBOR tag 18, which is how COSE labels a single-signer message. ML-DSA is registered with COSE algorithm identifiers in the IANA COSE Algorithms registry under the post-quantum group. Asqav uses ML-DSA-65, which corresponds to FIPS 204 parameter set 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  The export endpoint
&lt;/h2&gt;

&lt;p&gt;The on-demand export does not change the original receipt. It re-serialises the canonical JSON payload into a COSE_Sign1 envelope and re-signs with the same ML-DSA-65 key. The original JSON receipt and the COSE form are both first-class records, both verifiable independently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ASQAV_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/cose"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://api.asqav.com/v1/receipts/&lt;span class="o"&gt;{&lt;/span&gt;receipt_id&lt;span class="o"&gt;}&lt;/span&gt;/cose &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; receipt.cose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is the raw COSE_Sign1 bytes. Submit that to your SCITT transparency service of choice and you get an inclusion receipt back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying it locally
&lt;/h2&gt;

&lt;p&gt;You do not need a SCITT service to inspect what came back. The CBOR is parseable with any COSE library. Here is a check using &lt;code&gt;cbor2&lt;/code&gt; in Python:&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;cbor2&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;receipt.cose&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;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cbor2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# tagged value, tag 18 = COSE_Sign1
&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="n"&gt;protected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unprotected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;

&lt;span class="n"&gt;protected_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cbor2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;protected&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alg label:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protected_map&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# 1 = alg
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload bytes:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signature bytes:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# ML-DSA-65 sig is 3309 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payload is the same RFC 8785 canonical JSON object you get from the regular receipt endpoint, so a verifier can re-extract it and compare against the JSON form for free. The signature is over the standard COSE Sig_structure, which means any compliant COSE verifier with an ML-DSA-65 implementation will accept it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One key, two formats
&lt;/h2&gt;

&lt;p&gt;The ML-DSA-65 key that signs your normal receipts is the same key that signs the COSE form. Auditors only have to learn one public key, and they can pick whichever envelope format their tooling supports. The two are bit-equivalent in payload, only the framing differs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SCITT working group: &lt;a href="https://datatracker.ietf.org/wg/scitt/about/" rel="noopener noreferrer"&gt;datatracker.ietf.org/wg/scitt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RFC 9052, COSE: &lt;a href="https://datatracker.ietf.org/doc/rfc9052/" rel="noopener noreferrer"&gt;rfc9052&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;FIPS 204, ML-DSA: &lt;a href="https://csrc.nist.gov/pubs/fips/204/final" rel="noopener noreferrer"&gt;csrc.nist.gov/pubs/fips/204&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Asqav docs, COSE export endpoint: &lt;a href="https://www.asqav.com/docs/cose-export" rel="noopener noreferrer"&gt;docs/cose-export&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run a SCITT transparency service in front of your Asqav signer and you have a two-party audit trail that any third party can verify, without contacting either of you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Audit-trail receipts you can verify offline</title>
      <dc:creator>João André Gomes Marques</dc:creator>
      <pubDate>Sat, 02 May 2026 11:07:15 +0000</pubDate>
      <link>https://dev.to/jagmarques/audit-trail-receipts-you-can-verify-offline-nac</link>
      <guid>https://dev.to/jagmarques/audit-trail-receipts-you-can-verify-offline-nac</guid>
      <description>&lt;p&gt;Buyers ask for an audit trail. You hand them a CSV of timestamps. They ask if the timestamps are tamper-evident. You hand them a SOC 2 report. None of that lets them verify a single agent action without your help.&lt;/p&gt;

&lt;p&gt;Asqav now ships a public per-agent profile so anyone with the agent ID can pull the receipt history and check the signatures themselves. No login, no API key, no call back to your stack.&lt;/p&gt;

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

&lt;p&gt;Every agent registered against an Asqav org gets a stable URL of the form &lt;code&gt;asqav.com/a/&lt;/code&gt;. The profile renders three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent's identity claim, which is the agent name, owner org, and the public key fingerprint used for ML-DSA-65 signatures.&lt;/li&gt;
&lt;li&gt;A trust-level badge, L0 to L3, computed from policy coverage, receipt continuity, and incident history.&lt;/li&gt;
&lt;li&gt;A reverse-chronological list of recent signed receipts with action type, timestamp, and the verification status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each receipt links to the canonical JSON, the COSE_Sign1 form, and a one-click "verify in browser" path that pulls the public key, runs ML-DSA verification in WASM, and shows pass or fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The embed badge
&lt;/h2&gt;

&lt;p&gt;The point of a public profile is that other people can point to it. Asqav ships an SVG badge and an iframe so you can drop trust evidence into a README, a docs page, or a procurement portal.&lt;/p&gt;

&lt;p&gt;The SVG badge is a static image served from the API. It always reflects the current trust level, which means a regression in policy coverage updates the badge automatically the next time it is fetched.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[

](https://asqav.com/a/agent_abc123)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The iframe is the embed version of the full profile. It renders the latest receipts, the trust level, and the verify-in-browser link. Drop it in your repo or in a vendor security page and reviewers do not need to leave that page to check the signatures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The embed sends a CSP &lt;code&gt;frame-ancestors&lt;/code&gt; response that lets it render in any host. There is no JavaScript reaching out to your stack. The verifier path uses the public ML-DSA key and the canonical receipt format, so a reviewer can save the page and re-verify offline.&lt;/p&gt;

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

&lt;p&gt;An auditor or a procurement reviewer is rarely on the same network as the producer of the receipts. The whole point of a signed audit trail is that you can hand over the receipts and the public key and walk away. The profile, the badge, and the iframe are all wrappers around that property. There is no privileged endpoint, no rate-limited verification API, and no shared secret. The signatures are checked against a public key.&lt;/p&gt;

&lt;p&gt;That is what makes this useful for EU AI Act Article 12 evidence packs, DORA operational resilience reviews, and SOC 2 system descriptions. The reviewer leaves the meeting able to spot-check any line in the trail.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Public profile: &lt;a href="https://www.asqav.com/a/demo" rel="noopener noreferrer"&gt;asqav.com/a/demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Badge for the demo agent: &lt;a href="https://api.asqav.com/api/v1/agents/demo/badge.svg" rel="noopener noreferrer"&gt;badge.svg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Embed it in your README using the iframe markup above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cryptography is the same ML-DSA-65 used everywhere else in Asqav. The new piece is making the verification surface public, so a buyer can check the chain without needing your help.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <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>
  </channel>
</rss>
