<?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: Ventse</title>
    <description>The latest articles on DEV Community by Ventse (@traceseal).</description>
    <link>https://dev.to/traceseal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3882611%2F9fea844c-d8a3-423f-9fd5-c12bd3e6960d.png</url>
      <title>DEV Community: Ventse</title>
      <link>https://dev.to/traceseal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/traceseal"/>
    <language>en</language>
    <item>
      <title>in-toto and SLSA for AI agents: what build provenance teaches us about agent records</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:46:18 +0000</pubDate>
      <link>https://dev.to/traceseal/in-toto-and-slsa-for-ai-agents-what-build-provenance-teaches-us-about-agent-records-4247</link>
      <guid>https://dev.to/traceseal/in-toto-and-slsa-for-ai-agents-what-build-provenance-teaches-us-about-agent-records-4247</guid>
      <description>&lt;p&gt;Software supply-chain security has spent the last few years answering a question that AI agents now raise again: given an artifact, how does someone who was not in the room find out how it was produced, and how far should they believe the answer? For builds, the answer the industry has converged on is an &lt;strong&gt;attestation&lt;/strong&gt;, usually an &lt;a href="https://github.com/in-toto/attestation" rel="noopener noreferrer"&gt;in-toto attestation&lt;/a&gt; carrying &lt;a href="https://slsa.dev/spec/v1.0/provenance" rel="noopener noreferrer"&gt;SLSA provenance&lt;/a&gt;. This piece reads those two specifications as a design guide for recording what an agent did, and is explicit about where the analogy holds and where it does not.&lt;/p&gt;

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

&lt;p&gt;The in-toto attestation framework defines an attestation as "authenticated metadata about one or more software artifacts". The &lt;a href="https://github.com/in-toto/attestation/blob/main/spec/README.md" rel="noopener noreferrer"&gt;current specification&lt;/a&gt; (v1.2) splits it into layers, each with one job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Predicate&lt;/strong&gt;: "arbitrary metadata about a subject artifact, with a type-specific schema". Provenance, a test result or a vulnerability scan are all predicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statement&lt;/strong&gt;: "binds the attestation to a particular subject and unambiguously identifies the types of the predicate".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Envelope&lt;/strong&gt;: "handles authentication and serialization", in other words the signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle&lt;/strong&gt;: a way of grouping several attestations together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;a href="https://github.com/in-toto/attestation/blob/main/spec/v1/statement.md" rel="noopener noreferrer"&gt;v1 Statement&lt;/a&gt; has four fields: &lt;code&gt;_type&lt;/code&gt;, always &lt;code&gt;https://in-toto.io/Statement/v1&lt;/code&gt;; &lt;code&gt;subject&lt;/code&gt;, a list of artifacts, each of which "MUST have &lt;code&gt;digest&lt;/code&gt; set"; &lt;code&gt;predicateType&lt;/code&gt;, a URI naming the schema of the predicate; and &lt;code&gt;predicate&lt;/code&gt; itself. Two rules in that document matter for anyone thinking about agents. Subjects "are assumed to be &lt;em&gt;immutable&lt;/em&gt;", and they "are matched purely by digest, regardless of content type". The statement is about specific bytes, not about a file name or a version label.&lt;/p&gt;

&lt;h2&gt;
  
  
  The envelope: DSSE and why it refuses canonical JSON
&lt;/h2&gt;

&lt;p&gt;in-toto attestations are normally signed with &lt;a href="https://github.com/secure-systems-lab/dsse" rel="noopener noreferrer"&gt;DSSE&lt;/a&gt;, the Dead Simple Signing Envelope. The &lt;a href="https://github.com/secure-systems-lab/dsse/blob/master/protocol.md" rel="noopener noreferrer"&gt;protocol&lt;/a&gt; signs a pre-authentication encoding of the payload rather than the payload alone:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAE(type, body) = "DSSEv1" + SP + LEN(type) + SP + type + SP + LEN(body) + SP + body
SIGNATURE = Sign(PAE(UTF8(PAYLOAD_TYPE), SERIALIZED_BODY))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Two design choices are worth understanding. First, the payload type is inside the signature. The &lt;a href="https://github.com/secure-systems-lab/dsse/blob/master/background.md" rel="noopener noreferrer"&gt;design notes&lt;/a&gt; explain that without an authenticated type, a signer and a verifier may interpret the same bytes differently, and give the example of a message produced as CBOR being read as protobuf. Second, DSSE deliberately avoids canonicalisation. The same document says a signing scheme "SHOULD avoid depending on canonicalization for security", warns that "two semantically different payloads could have the same canonical encoding", and prefers to "transmit the encoded byte stream exactly as it was signed" so that verification happens before any parsing.&lt;/p&gt;

&lt;p&gt;That second point is a direct critique of the approach our own receipts take. The &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;Traceseal receipt specification&lt;/a&gt; signs the canonical JSON encoding of the execution and provenance objects, which means a verifier has to parse the receipt and re-encode it before checking the signature. We mitigate this by keeping every field a string (the spec encodes &lt;code&gt;ok&lt;/code&gt; as &lt;code&gt;"true"&lt;/code&gt; rather than a JSON boolean for that reason) and by publishing a reference encoder, but the DSSE authors' objection is a fair one and it applies to us. If you are designing an agent record format from scratch today, signing the exact bytes with a typed envelope is the safer default.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SLSA provenance records
&lt;/h2&gt;

&lt;p&gt;SLSA describes provenance as "the verifiable information about software artifacts describing where, when and how something was produced", and models it as "an attestation that a particular build platform produced a set of software artifacts". It is an in-toto predicate with type &lt;code&gt;https://slsa.dev/provenance/v1&lt;/code&gt;, and it has two halves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;buildDefinition&lt;/code&gt;&lt;/strong&gt;, the input to the build: a &lt;code&gt;buildType&lt;/code&gt;, the &lt;code&gt;externalParameters&lt;/code&gt; "under external control, such as those set by a user or tenant", the &lt;code&gt;internalParameters&lt;/code&gt; under the platform's control, and the &lt;code&gt;resolvedDependencies&lt;/code&gt; pulled in at build time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;runDetails&lt;/code&gt;&lt;/strong&gt;, this particular execution: a &lt;code&gt;builder.id&lt;/code&gt;, metadata including &lt;code&gt;invocationId&lt;/code&gt;, &lt;code&gt;startedOn&lt;/code&gt; and &lt;code&gt;finishedOn&lt;/code&gt;, and any &lt;code&gt;byproducts&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Map that onto an agent run and most of it carries over with little strain. The task prompt and the user's instructions are external parameters. The model identifier, system prompt and tool configuration chosen by the operator are internal parameters. Retrieved documents, tool definitions and the code of any skill the agent loaded are resolved dependencies. The harness that ran the agent is the builder. The files it wrote, the messages it sent and the commits it made are subjects, identified by digest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The useful idea: who is trusted for which field
&lt;/h2&gt;

&lt;p&gt;The most transferable part of SLSA is not the schema but the way it assigns trust to each field. The provenance specification says external parameters "are untrusted; they MUST be included in the provenance and MUST be verified downstream", while internal parameters "are trusted because the platform is trusted". It also states that "the accuracy and completeness" of the build definition "are implied by &lt;code&gt;runDetails.builder.id&lt;/code&gt;", and that consumers "MUST accept only specific signer-builder pairs".&lt;/p&gt;

&lt;p&gt;Put plainly: a provenance document is only as good as the thing that wrote it, and the format makes that dependency explicit rather than hiding it. Agent logs rarely do this. A transcript mixes what the user asked for, what the model decided, what a tool returned and what the harness observed, all written by the same process and all presented with the same apparent authority. Separating "values supplied from outside, to be checked" from "values recorded by the platform, trusted if the platform is" is a small change in a record format, and it tells a reader exactly what they are being asked to believe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build levels, read as agent levels
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://slsa.dev/spec/v1.0/levels" rel="noopener noreferrer"&gt;SLSA v1.0 build track&lt;/a&gt; grades how hard the provenance is to fake:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build L1&lt;/strong&gt;: provenance exists. It "can be used to prevent mistakes but is trivial to bypass or forge", and it may be "incomplete and/or unsigned".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build L2&lt;/strong&gt;: the build runs on a hosted platform, "not an individual's workstation", and the provenance is signed by it. Forging it "requires an explicit 'attack', though this may be easy to perform".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build L3&lt;/strong&gt;: hardened builds. The platform must "prevent secret material used to sign the provenance from being accessible to the user-defined build steps", and runs must not be able to influence one another.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The L3 requirement is the one agent systems most often get wrong. In a build, the user-defined steps are the untrusted part. In an agent run, the untrusted part is the agent itself: its behaviour depends on a model and on whatever content it reads, and &lt;a href="https://traceseal.io/blog/indirect-prompt-injection/" rel="noopener noreferrer"&gt;indirect prompt injection&lt;/a&gt; means that content can steer it. If the agent process can reach the key that signs its own record, the record is at L1 in SLSA's terms no matter how good the cryptography is, because the thing being described can write the description. The &lt;a href="https://traceseal.io/blog/owasp-top-10-agentic-applications/" rel="noopener noreferrer"&gt;OWASP agentic guidance&lt;/a&gt; reaches the same conclusion from the other direction.&lt;/p&gt;

&lt;p&gt;Our own tooling follows this rule. Alibi runs each agent inside a bubblewrap sandbox and signs receipts on the host after the contained processes have exited; the key is never mounted inside the sandbox. &lt;a href="https://traceseal.io/blog/sandbox-an-ai-coding-agent/" rel="noopener noreferrer"&gt;Our sandboxing article&lt;/a&gt; covers the profile, including a DNS leak we found when we tested it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A quick test for any agent record:&lt;/strong&gt; could the agent, if it misbehaved, have produced a record that verifies but says something false? If the answer is yes, the signature is protecting the record from everyone except the party you most need it to be protected from.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where the analogy breaks
&lt;/h2&gt;

&lt;p&gt;Build provenance can rely on three properties that agent runs do not have, and it is worth being exact about them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Builds aim to be repeatable; agent runs are not.&lt;/strong&gt; A verifier who distrusts build provenance can, in principle, rebuild from the declared inputs and compare digests. An agent run against a hosted model, with live tool results, cannot be replayed to the same outcome. The record is the only evidence there will ever be, which raises the stakes on getting the signer right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build inputs are artifacts; agent inputs are often conversations.&lt;/strong&gt; A resolved dependency has a digest. A web page the agent read at 14:02, or the response of an API that has since changed, can be hashed, but the hash only helps if someone kept the bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The subject of a build is the output; the subject of an agent run is often the action.&lt;/strong&gt; An email sent, a payment initiated or a ticket closed is not an immutable artifact with a digest in the sense the in-toto Statement assumes. You can hash a description of the action, but then you are attesting to the description, and the question becomes who wrote it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this makes the build world's tools wrong for agents. It means the trust model has to be carried over, not just the field names. And SLSA provenance, like our receipts, is an attestation and not a proof. Our specification says it directly: a receipt "proves that the operator's key signed the attestation. It does NOT prove the operator is honest." The same is true of any signed provenance. What the format buys you is that the claim is attributable, tamper-evident and checkable by a third party, which a log file is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take from this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identify subjects by digest.&lt;/strong&gt; Record the hash of every file an agent wrote and every payload it sent, not only a path or a name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split external from internal parameters.&lt;/strong&gt; Mark what came from the user or from content the agent read separately from what the operator configured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the signing key out of the agent's reach.&lt;/strong&gt; This is the single requirement that decides whether a record is evidence or self-description.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign exact bytes and the payload type&lt;/strong&gt; if you are designing a format now. DSSE's reasons are sound.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get an outside party involved in time and ordering.&lt;/strong&gt; A signature does not fix when something happened; see &lt;a href="https://traceseal.io/blog/trusted-timestamps-ai-agent-logs/" rel="noopener noreferrer"&gt;our piece on trusted timestamps&lt;/a&gt; and &lt;a href="https://traceseal.io/blog/transparency-log-witnesses/" rel="noopener noreferrer"&gt;on transparency log witnesses&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The receipt format, verifier and log are open: the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, the &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt;, and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://traceseal.io/blog/in-toto-slsa-provenance-ai-agents/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>supplychain</category>
    </item>
    <item>
      <title>EU AI Act Article 14: human oversight for AI agents, and what an oversight record should prove</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 13 Oct 2026 08:48:31 +0000</pubDate>
      <link>https://dev.to/traceseal/eu-ai-act-article-14-human-oversight-for-ai-agents-and-what-an-oversight-record-should-prove-1l68</link>
      <guid>https://dev.to/traceseal/eu-ai-act-article-14-human-oversight-for-ai-agents-and-what-an-oversight-record-should-prove-1l68</guid>
      <description>&lt;p&gt;"Human in the loop" is the usual answer to the question of who is in charge of an AI agent. Article 14 of the EU AI Act (&lt;a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj" rel="noopener noreferrer"&gt;Regulation (EU) 2024/1689&lt;/a&gt;) turns that answer into a legal requirement for high-risk systems, and it is more specific than the slogan. This piece covers what the article says, who carries the duty, when it applies now that the deadlines have moved, and the question most teams skip: if someone later asks whether a person really oversaw a particular agent action, what record could you show them?&lt;/p&gt;

&lt;h2&gt;
  
  
  When it applies: later than first planned
&lt;/h2&gt;

&lt;p&gt;Article 14 sits in Chapter III, Section 2, the requirements for high-risk AI systems. Following the 2026 Digital Omnibus amendments, the &lt;a href="https://artificialintelligenceact.eu/article/113/" rel="noopener noreferrer"&gt;consolidated text of Article 113&lt;/a&gt; now applies those requirements from &lt;strong&gt;2 December 2027&lt;/strong&gt; for systems that are high-risk under Article 6(2) and Annex III, and from &lt;strong&gt;2 August 2028&lt;/strong&gt; for systems that are high-risk under Article 6(1) and Annex I. The original date for most of them was 2 August 2026.&lt;/p&gt;

&lt;p&gt;The delay changes the timetable, not the obligation. It also does not move the transparency duties covered in our &lt;a href="https://traceseal.io/blog/eu-ai-act-article-50-ai-agents/" rel="noopener noreferrer"&gt;Article 50 piece&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whether an agent is high-risk depends on what it is used for, not on the fact that it is an agent. A coding agent that edits your repository is unlikely to be in scope. An agent that filters job applications or evaluates candidates is, because recruitment and selection are listed in &lt;a href="https://artificialintelligenceact.eu/annex/3/" rel="noopener noreferrer"&gt;Annex III, point 4(a)&lt;/a&gt;. So is one that evaluates the creditworthiness of natural persons, under point 5(b).&lt;/p&gt;

&lt;h2&gt;
  
  
  What Article 14 actually says
&lt;/h2&gt;

&lt;p&gt;The core requirement is in &lt;a href="https://artificialintelligenceact.eu/article/14/" rel="noopener noreferrer"&gt;Article 14(1)&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;High-risk AI systems shall be designed and developed in such a way, including with appropriate human-machine interface tools, that they can be effectively overseen by natural persons during the period in which they are in use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Article 14(3) says the oversight measures "shall be commensurate with the risks, level of autonomy and context of use". That is the clause that matters most for agents. A system that recommends is one thing. A system that plans and then acts on its own plan has a higher level of autonomy, and the oversight is expected to scale with it.&lt;/p&gt;

&lt;p&gt;Article 14(4) then lists what the people assigned to oversight must be enabled to do, "as appropriate and proportionate":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;(a) Understand and monitor.&lt;/strong&gt; Properly understand the system's capacities and limitations, and monitor its operation, including detecting and addressing anomalies, dysfunctions and unexpected performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(b) Resist automation bias.&lt;/strong&gt; Stay aware of the tendency to rely, or over-rely, on the system's output. The Act names this "automation bias".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(c) Interpret the output.&lt;/strong&gt; Correctly interpret what the system produced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(d) Decide not to use it.&lt;/strong&gt; In any particular situation, decide not to use the system, or "disregard, override or reverse" its output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(e) Stop it.&lt;/strong&gt; Intervene in its operation or interrupt it "through a 'stop' button or a similar procedure that allows the system to come to a halt in a safe state."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Article 14(5) adds a stricter rule for remote biometric identification under Annex III point 1(a). No action may be taken on an identification unless it has been "separately verified and confirmed by at least two natural persons". Few agent deployments will fall under it. It is still the one place where the Act spells out a two-person check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who carries the duty
&lt;/h2&gt;

&lt;p&gt;The work is split. Under Article 14(3), the &lt;strong&gt;provider&lt;/strong&gt; builds oversight measures into the system, or identifies measures the deployer should implement. The &lt;strong&gt;deployer&lt;/strong&gt; then has to staff them. &lt;a href="https://artificialintelligenceact.eu/article/26/" rel="noopener noreferrer"&gt;Article 26(2)&lt;/a&gt; is short:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deployers shall assign human oversight to natural persons who have the necessary competence, training and authority, as well as the necessary support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Breaching the deployer obligations in Article 26 falls under &lt;a href="https://artificialintelligenceact.eu/article/99/" rel="noopener noreferrer"&gt;Article 99(4)(e)&lt;/a&gt;. Fines there go up to €15 million or 3% of total worldwide annual turnover, whichever is higher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents strain the model
&lt;/h2&gt;

&lt;p&gt;Article 14(4)(d) speaks of overriding or reversing the system's output. For a model that recommends, the output is a suggestion, and a person can ignore it. For an agent, the output is often an action: an email sent, a record updated, a payment initiated. Some actions can be reversed afterwards. Many cannot.&lt;/p&gt;

&lt;p&gt;In our reading, that leaves agent deployments with two practical places to put oversight. The first is a gate &lt;em&gt;before&lt;/em&gt; any action that cannot be undone, where a named person sees what the agent proposes and approves or rejects it. The second is a stop mechanism that works while the agent runs, as Article 14(4)(e) describes. Reviewing a dashboard after the run meets neither. It is monitoring, which point (a) asks for as well, but it does not let anyone override an email that has already gone.&lt;/p&gt;

&lt;p&gt;Automation bias, point (b), gets worse with agents for a simple reason: volume. A reviewer approving the fortieth tool call of the morning is not overseeing it in any sense a regulator would recognise. That argues for gating fewer, higher-consequence actions rather than every step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an oversight record should show
&lt;/h2&gt;

&lt;p&gt;Article 14 states what oversight must make possible. It does not prescribe a record of each oversight decision. Logging duties for high-risk systems sit separately in Articles 12, 19 and 26(6), which we covered in &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;AI agent audit trails&lt;/a&gt;. In a dispute, though, "a person approved this" is a factual claim, and someone will ask you to back it up. Our view is that a useful record of an oversight decision answers five questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Who decided?&lt;/strong&gt; A named person, identified by something other than the agent's own credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What did they see?&lt;/strong&gt; The exact proposal put in front of them, or a hash of it, so a later reader can tell they were not shown something different.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What did they decide?&lt;/strong&gt; Approve, reject, modify, or stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did the decision come before the action?&lt;/strong&gt; An approval logged after the email went out is not oversight. That needs a time someone other than the agent's host vouches for. Our &lt;a href="https://traceseal.io/blog/trusted-timestamps-ai-agent-logs/" rel="noopener noreferrer"&gt;piece on trusted timestamps&lt;/a&gt; covers what that takes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the action match what was approved?&lt;/strong&gt; The inputs the agent finally executed with should be the inputs the person approved, not a later variant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The common failure is a record that cannot answer the first question honestly. If the approval is a row written by the agent platform, signed (if at all) with the platform's key, then it shows the platform says a person approved. That may be true. It is still the system under review describing itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where signed receipts help, and where they stop
&lt;/h2&gt;

&lt;p&gt;Traceseal receipts address question five directly, and they say plainly what they cannot do. A receipt binds the code that ran to hashes of its inputs and outputs, signed with the operator's ed25519 key, and anyone can check it offline:&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="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;traceseal-verify
&lt;span class="nv"&gt;$ &lt;/span&gt;traceseal-verify receipt.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the approver's decision covers the same inputs hash that appears in the receipt, a third party can confirm that what ran is what was approved. Change one byte of the inputs and the hashes no longer match.&lt;/p&gt;

&lt;p&gt;The published receipt format has no field for a human approval. A receipt shows who signed the execution record, not that a person looked at it first. The &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;receipt specification&lt;/a&gt; is explicit about the limit that matters here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The receipt proves that the operator's key signed the attestation. It does NOT prove the operator is honest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An oversight record therefore needs its own signature from a key the approving person controls and the agent cannot reach, made before the action and over the same inputs hash. It is the same separation the OWASP agentic guidance insists on for signing keys, which we covered in our &lt;a href="https://traceseal.io/blog/owasp-top-10-agentic-applications/" rel="noopener noreferrer"&gt;OWASP Top 10 piece&lt;/a&gt;. A receipt on its own does not give you that. A receipt beside an approver-signed decision lets a third party check the chain from "this person approved these inputs" to "these inputs are what ran".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The practical test:&lt;/strong&gt; pick one consequential action your agent took last month. Can you show, without trusting the agent platform's own database, who approved it, what they were shown, and that the approval came first?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What to do before December 2027
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Classify by use, not by technology.&lt;/strong&gt; Map each agent against Annex III. Most will fall outside it. For those that fall inside, Article 14 is a design requirement, and it is easier to build in than to retrofit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the irreversible actions.&lt;/strong&gt; List the actions each in-scope agent can take that cannot be undone. Those are where pre-action approval belongs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the stop.&lt;/strong&gt; Article 14(4)(e) asks for a halt "in a safe state". Interrupt a real run part-way through and see what state it leaves behind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name the overseers.&lt;/strong&gt; Article 26(2) asks for people with competence, training and authority. Write down who they are and what authority they actually hold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep approvals separate from the agent.&lt;/strong&gt; The approver's key should not be one the agent or its host can use. Otherwise the record is the system vouching for itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The receipt format, verifier and transparency log are open: the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, the &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt;, and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;. You can adopt the format without adopting us.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://traceseal.io/blog/eu-ai-act-article-14-human-oversight-ai-agents/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>compliance</category>
    </item>
    <item>
      <title>Trusted timestamps for AI agent logs: what RFC 3161 proves, and what it does not</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 06 Oct 2026 08:47:56 +0000</pubDate>
      <link>https://dev.to/traceseal/trusted-timestamps-for-ai-agent-logs-what-rfc-3161-proves-and-what-it-does-not-13ah</link>
      <guid>https://dev.to/traceseal/trusted-timestamps-for-ai-agent-logs-what-rfc-3161-proves-and-what-it-does-not-13ah</guid>
      <description>&lt;p&gt;Every record an AI agent leaves behind carries a time. A tool call was logged at 14:02:11, a file was written at 14:02:13, a receipt was signed at 14:02:14. When an action is disputed, the time is often the first thing that matters: was the agent's change made before or after the incident, before or after the approval, before or after the policy changed? Most of those times were written by the system under question, using its own clock. This piece looks at what it takes to make a time on an agent record something a third party can rely on, starting with the standard most people search for, &lt;a href="https://www.rfc-editor.org/rfc/rfc3161" rel="noopener noreferrer"&gt;RFC 3161&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A signature tells you who, not when
&lt;/h2&gt;

&lt;p&gt;A digital signature binds a key to a sequence of bytes. If the bytes include a time, the signature covers that time, but it says nothing about whether the time was true when the signer wrote it. An operator who wants to backdate a record simply writes an earlier time and signs it. The result verifies perfectly.&lt;/p&gt;

&lt;p&gt;Our own receipts have exactly this property, and the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;receipt specification&lt;/a&gt; says so. A receipt carries &lt;code&gt;execution.timestamp&lt;/code&gt; and &lt;code&gt;attestation.attested_at&lt;/code&gt;, both ISO 8601 UTC strings supplied by the operator. Section 6.3 notes that a verifier with access to the operator's hash-chained audit log can detect replayed or out-of-order receipts, while "a verifier without audit log access cannot detect replays; they can only verify that the receipt is internally consistent." A valid signature on a receipt proves which key signed it and that nothing has changed since. It does not prove the moment of signing to anyone who does not already trust the operator's clock.&lt;/p&gt;

&lt;p&gt;Closing that gap needs a party other than the signer to say something about time. There are two well-established ways to get one: a time-stamping authority, or a public append-only log that someone else is watching.&lt;/p&gt;

&lt;h2&gt;
  
  
  What RFC 3161 actually specifies
&lt;/h2&gt;

&lt;p&gt;RFC 3161, published in August 2001, defines the Time-Stamp Protocol. In its words, a time-stamping authority (TSA) "is a TTP that creates time-stamp tokens in order to indicate that a datum existed at a particular point in time." The client sends a hash, the TSA returns a signed token over that hash and the time. Section 2.1 lists what the TSA is required to do, and several of those requirements matter for agent records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It sees only a hash.&lt;/strong&gt; The TSA must "only time-stamp a hash representation of the datum" and must not examine the imprint beyond checking its length. You can timestamp a receipt that contains sensitive inputs without sending the inputs anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not record who asked.&lt;/strong&gt; Tokens must not include "any identification of the requesting entity". The token says that this hash existed at this time, not who held it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must use a trustworthy time source&lt;/strong&gt; and include a unique serial number in every token, a property the RFC says must survive a crash of the service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It names a policy.&lt;/strong&gt; Each token carries an identifier for the security policy it was issued under. The RFC deliberately leaves operational security to that policy rather than to the protocol, so what a token is worth depends on which TSA issued it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the token, the &lt;code&gt;genTime&lt;/code&gt; field holds the time in UTC, and an optional &lt;code&gt;accuracy&lt;/code&gt; field gives the margin either side. &lt;a href="https://www.rfc-editor.org/rfc/rfc5816" rel="noopener noreferrer"&gt;RFC 5816&lt;/a&gt; updated the protocol in 2010 so that the TSA's certificate can be identified with a hash other than SHA-1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Producing a request
&lt;/h2&gt;

&lt;p&gt;OpenSSL ships a client and a minimal TSA in &lt;a href="https://docs.openssl.org/3.5/man1/openssl-ts/" rel="noopener noreferrer"&gt;&lt;code&gt;openssl ts&lt;/code&gt;&lt;/a&gt;. Building a request is local and sends nothing over the network. This is the output on our build box with OpenSSL 3.5.7, with the ASCII column trimmed:&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="nv"&gt;$ &lt;/span&gt;openssl ts &lt;span class="nt"&gt;-query&lt;/span&gt; &lt;span class="nt"&gt;-data&lt;/span&gt; receipt.json &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-cert&lt;/span&gt; &lt;span class="nt"&gt;-no_nonce&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; receipt.tsq
&lt;span class="nv"&gt;$ &lt;/span&gt;openssl ts &lt;span class="nt"&gt;-query&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; receipt.tsq &lt;span class="nt"&gt;-text&lt;/span&gt;
Version: 1
Hash Algorithm: sha256
Message data:
    0000 - 03 07 47 87 d1 95 c2 ff-03 66 c8 a4 5f 44 90 e6
    0010 - 83 86 00 6b 26 dc 60 2a-0e 2a 3d ef a1 06 00 43
Policy OID: unspecified
Nonce: unspecified
Certificate required: &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request is posted to a TSA, which returns a response file. Verification takes the original data, the response and the TSA's CA certificate. The manual requires at least one of &lt;code&gt;-CAfile&lt;/code&gt;, &lt;code&gt;-CApath&lt;/code&gt; or &lt;code&gt;-CAstore&lt;/code&gt;:&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="nv"&gt;$ &lt;/span&gt;openssl ts &lt;span class="nt"&gt;-verify&lt;/span&gt; &lt;span class="nt"&gt;-data&lt;/span&gt; receipt.json &lt;span class="nt"&gt;-in&lt;/span&gt; receipt.tsr &lt;span class="nt"&gt;-CAfile&lt;/span&gt; tsa-ca.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have not sent a request to a public TSA for this article, so the response side is shown as the documented command rather than captured output. The &lt;code&gt;-no_nonce&lt;/code&gt; option we used for the local example removes the nonce; against a live service you would normally keep it, because the nonce is how a client with no reliable clock of its own checks that the response is fresh.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a token proves, and what it leaves open
&lt;/h2&gt;

&lt;p&gt;A valid token establishes one thing: the hashed data existed no later than &lt;code&gt;genTime&lt;/code&gt;, give or take the stated accuracy. That is a useful property, and it is narrower than it first looks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is an upper bound only.&lt;/strong&gt; The token says the receipt existed by 14:02:15. It does not say the agent acted at 14:02:11, or that the receipt was not prepared hours earlier and stamped later. If you also need a lower bound, the record has to contain something that could not have been known before a given moment, such as a value published by an outside party at a known time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It says nothing about the content.&lt;/strong&gt; The TSA never sees the receipt. A false record stamped promptly is still false; the token only fixes when it existed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering is limited by accuracy.&lt;/strong&gt; Section 2.4.2 states that, unless the TSA sets the &lt;code&gt;ordering&lt;/code&gt; flag, two tokens can only be ordered when the gap between their times is greater than the sum of their accuracies. Two agent actions a few hundred milliseconds apart may not be orderable from their tokens alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It depends on one key.&lt;/strong&gt; The security considerations warn that if a TSA's private key is compromised, "any token signed by the TSA using that private key cannot be trusted anymore", and backdated tokens become possible. They suggest tokens from two different TSAs as one defence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It ages.&lt;/strong&gt; The same section notes that a TSA key "will have a finite lifetime", so tokens that must outlast it should be time-stamped again later. Agent records kept for regulatory retention periods fall into this category.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the law gives timestamps weight
&lt;/h2&gt;

&lt;p&gt;In the EU, the eIDAS Regulation (&lt;a href="https://eur-lex.europa.eu/eli/reg/2014/910/oj" rel="noopener noreferrer"&gt;Regulation (EU) No 910/2014&lt;/a&gt;) gives electronic time stamps a defined legal status. In the &lt;a href="https://www.legislation.gov.uk/eur/2014/910/article/41/adopted" rel="noopener noreferrer"&gt;text as adopted&lt;/a&gt;, Article 41(1) says an electronic time stamp "shall not be denied legal effect and admissibility as evidence in legal proceedings solely on the grounds that it is in an electronic form or that it does not meet the requirements of the qualified electronic time stamp." Article 41(2) goes further for qualified stamps, which "shall enjoy the presumption of the accuracy of the date and the time it indicates and the integrity of the data to which the date and time are bound."&lt;/p&gt;

&lt;p&gt;A qualified stamp has to meet the requirements in &lt;a href="https://www.legislation.gov.uk/eur/2014/910/article/42/adopted" rel="noopener noreferrer"&gt;Article 42(1)&lt;/a&gt;: it binds date and time to the data so as to "reasonably preclude the possibility of the data being changed undetectably", it is "based on an accurate time source linked to Coordinated Universal Time", and it is signed or sealed by a qualified trust service provider. The practical difference is the burden of proof. A self-asserted time can be argued about. A qualified time stamp is presumed correct unless someone shows otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other route: a log someone else is watching
&lt;/h2&gt;

&lt;p&gt;A transparency log gets a time bound differently. Records are appended to a Merkle tree, and the log periodically publishes a signed checkpoint: its size and root hash. On its own, the log operator's checkpoint is another self-asserted statement. It becomes evidence about time when an independent witness cosigns it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://c2sp.org/tlog-cosignature" rel="noopener noreferrer"&gt;C2SP cosignature format&lt;/a&gt; puts a timestamp inside the witness's signature and defines what it means: "as of the specified time, the consistent tree head with the largest size the cosigner has observed for the log identified by the origin line has the specified root hash." Any entry included in that tree therefore existed no later than the cosigned time, on the word of a party that is not the log operator.&lt;/p&gt;

&lt;p&gt;Our own log works this way. At the time of writing, the checkpoint at &lt;a href="https://log.traceseal.io/checkpoint" rel="noopener noreferrer"&gt;log.traceseal.io/checkpoint&lt;/a&gt; is at tree size 24, and the copy held by the &lt;a href="https://witness.markovianprotocol.com/about" rel="noopener noreferrer"&gt;Markovian Protocol witness&lt;/a&gt; carries a cosignature with the timestamp 1787078705, which is 18 August 2026, 18:45:05 UTC. We checked the log signature and the cosignature against the published keys before publishing this article. So any of the first 24 entries existed by that moment, whatever times their own records claim. &lt;a href="https://traceseal.io/blog/transparency-log-witnesses/" rel="noopener noreferrer"&gt;Our piece on transparency log witnesses&lt;/a&gt; explains the checks a witness performs.&lt;/p&gt;

&lt;p&gt;The two routes are not equivalent. A TSA commits to a time-source policy, and a qualified one carries a legal presumption. A witness makes no claim about the quality of its clock beyond what its operator publishes; what it adds is that it refuses to cosign a tree that is inconsistent with one it has already seen, which defends against an operator showing different histories to different people. A TSA token does not give you that.&lt;/p&gt;

&lt;h2&gt;
  
  
  A workable setup for agent records
&lt;/h2&gt;

&lt;p&gt;For a team that needs agent actions to be datable by someone outside the team, the pieces fit together in a fairly direct way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sign each record at the point of action&lt;/strong&gt;, with a key the agent cannot reach, so the content and the signer are fixed. Our &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;primer on signed receipts&lt;/a&gt; covers why the key placement matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get an independent upper bound on time.&lt;/strong&gt; Either request an RFC 3161 token over the record's hash, or append the hash to a log whose checkpoints an independent witness cosigns. Where records may be used in EU proceedings, consider a qualified TSA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a lower bound if the order of events matters&lt;/strong&gt;, by including in the record a value that only became public at a known time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for renewal&lt;/strong&gt; if retention runs for years, by re-stamping before the TSA key or certificate expires.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traceseal currently covers the first step with signed receipts and the second through log inclusion and witness cosigning. It does not attach RFC 3161 tokens to receipts, and the receipt's own time fields remain operator-asserted. Anyone who needs a qualified timestamp today can request one over a receipt's hash, since the receipt is a stable file and the TSA never needs to see its content.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The question to ask of any agent log:&lt;/strong&gt; if you removed the times the system wrote about itself, what would still pin each action to a moment? If the answer is nothing, the log's times are claims, not evidence.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://traceseal.io/blog/trusted-timestamps-ai-agent-logs/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;. Traceseal issues signed execution receipts for AI agents: an open spec and an open verifier, one command to check.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>OWASP Top 10 for Agentic Applications: what it asks you to record, and who should sign it</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 29 Sep 2026 08:47:50 +0000</pubDate>
      <link>https://dev.to/traceseal/owasp-top-10-for-agentic-applications-what-it-asks-you-to-record-and-who-should-sign-it-k0g</link>
      <guid>https://dev.to/traceseal/owasp-top-10-for-agentic-applications-what-it-asks-you-to-record-and-who-should-sign-it-k0g</guid>
      <description>&lt;p&gt;In December 2025 the OWASP GenAI Security Project's Agentic Security Initiative published the &lt;a href="https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/" rel="noopener noreferrer"&gt;Top 10 for Agentic Applications&lt;/a&gt;. Most write-ups walk through the ten risks, and this one does too, briefly. It then reads the prevention sections for a narrower question: what the list asks you to record about your agents, and what properties it expects those records to have. The answer is more demanding than "keep logs", and the most useful sentence on the subject comes in the last entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ten risks in one pass
&lt;/h2&gt;

&lt;p&gt;The entries are numbered ASI01 to ASI10. Condensed from the document's own descriptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ASI01 Agent Goal Hijack.&lt;/strong&gt; An attacker redirects the agent's objectives or decision pathways, because agents "cannot reliably distinguish instructions from related content." The mechanics are in &lt;a href="https://traceseal.io/blog/indirect-prompt-injection/" rel="noopener noreferrer"&gt;our piece on indirect prompt injection&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI02 Tool Misuse and Exploitation.&lt;/strong&gt; Legitimate tools used harmfully through injection, misalignment, unsafe delegation or ambiguous instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI03 Identity and Privilege Abuse.&lt;/strong&gt; Escalation through delegation chains, role inheritance and cached credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI04 Agentic Supply Chain Vulnerabilities.&lt;/strong&gt; Third-party models, tools, plug-ins, MCP and A2A components or registries that are malicious or tampered with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI05 Unexpected Code Execution.&lt;/strong&gt; Code the agent generates, turned into remote code execution or local misuse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI06 Memory &amp;amp; Context Poisoning.&lt;/strong&gt; Persistent corruption of stored memory, summaries, embeddings and retrieval stores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI07 Insecure Inter-Agent Communication.&lt;/strong&gt; Weak authentication, integrity or authorisation between agents that coordinate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI08 Cascading Failures.&lt;/strong&gt; A single fault propagating across agents into system-wide harm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI09 Human-Agent Trust Exploitation.&lt;/strong&gt; An agent exploiting a person's trust so that they approve something they should not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI10 Rogue Agents.&lt;/strong&gt; Agents that deviate from their intended function or authorised scope, where each action "may individually appear legitimate".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The introductory letter frames all ten with a principle it calls Least-Agency: avoid autonomy you do not need, because "deploying agentic behavior where it is not needed expands the attack surface without adding value." In the same passage it says that "strong observability becomes non-negotiable". This piece picks up from that word, observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the logging requirement escalates
&lt;/h2&gt;

&lt;p&gt;Read the prevention sections in order and the logging advice does not stay at one level. It tightens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ASI01:&lt;/strong&gt; on an unexpected goal shift, "surface the deviation for review, and record it for audit." A record, of unspecified quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI02:&lt;/strong&gt; "Maintain immutable logs of all tool invocations and parameter changes." Now the record must not change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI08:&lt;/strong&gt; "Record all inter-agent messages, policy decisions, and execution outcomes in tamper-evident, time-stamped logs bound to cryptographic agent identities." Now any change must be detectable, and each entry is tied to a key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI09:&lt;/strong&gt; "Keep tamper-proof records of user queries and agent actions for audit and forensics."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI10:&lt;/strong&gt; "Maintain comprehensive, immutable and signed audit logs of all agent actions, tool calls, and inter-agent communication".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ASI08 gives the reason. It files the requirement under "logging and non-repudiation" and points back to threat T8, Repudiation and Untraceability, in OWASP's Agentic AI Threats and Mitigations guide, which it summarises as "the ability to trace, attribute, and audit cascading behaviors through resilient logging and non-repudiation mechanisms that prevent silent propagation."&lt;/p&gt;

&lt;p&gt;Non-repudiation is the word doing the work. A log supports it when the party that produced an entry cannot credibly deny producing it later, and nobody else can pass an entry off as theirs. An ordinary application log has neither property. Anyone with write access to the store, including a compromised process, can change it without leaving a trace. We set out the fields an agent log needs in &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;the audit trail piece&lt;/a&gt;. OWASP's list is concerned with whether anyone other than the author can believe those fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "immutable" is not enough
&lt;/h2&gt;

&lt;p&gt;The list says less about who writes the record, and ASI09 shows why that matters. Among its examples is "Explainability Fabrications: The agent fabricates plausible audit rationales to justify a risky configuration change." The same entry describes an agent that "acts as an untraceable 'bad influence,' manipulating the human into performing the final, audited action, making the agent's role in the compromise invisible to forensics."&lt;/p&gt;

&lt;p&gt;Take those two together. In the first, the agent writes a false entry into the audit trail. In the second, the audit trail is accurate: it records the human's action and misses the agent's part in it. Neither is fixed by immutability. Making a fabricated rationale immutable only preserves the fabrication. What helps is a record the agent did not write. It should describe what actually ran and what it produced, and be written at a boundary the agent cannot reach. That is the same objection to agent-written transcripts we raised in &lt;a href="https://traceseal.io/blog/soc-2-ai-agents-evidence/" rel="noopener noreferrer"&gt;the SOC 2 piece&lt;/a&gt;, and here OWASP makes it for us.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The distinction the list implies but does not state:&lt;/strong&gt; immutability protects a record after it is written. It says nothing about whether the writer was honest at the time. A log counts as evidence only if both hold.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The key belongs outside the agent
&lt;/h2&gt;

&lt;p&gt;ASI10 contains the sentence that says how to get there. After recommending signed behavioural manifests and per-run ephemeral credentials, it adds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All signing and attestation mechanisms assume hardened cryptographic key management (e.g., HSM/KMS-backed keys, least-privilege access, rotation and revocation). Keys must never be directly available to agents; instead, orchestrators should mediate signing operations so that a compromised agent cannot simply exfiltrate or misuse long-lived keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This constraint is what gives "signed logs" any value. If the agent process can reach the signing key, a hijacked agent (ASI01) or a rogue one (ASI10) can sign whatever it likes, and the signature proves only that the key was used. The signer has to sit on the far side of a boundary the agent cannot cross. In practice that means the orchestrator or host, with the key in a separate process, a keystore or an HSM.&lt;/p&gt;

&lt;p&gt;ASI04 applies the same logic to what the agent runs. Its advice is to "Sign and attest manifests, prompts, and tool definitions", to "verify provenance before install or activation" and to "auto-reject unsigned or unverified." ASI01 goes further and suggests evaluating an "intent capsule", which it calls "an emerging pattern to bind the declared goal, constraints, and context to each execution cycle in a signed envelope". The direction is the same across the list: bind what was meant to run, what did run and what came out, then sign it with a key the agent cannot use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a signed execution receipt fits
&lt;/h2&gt;

&lt;p&gt;That is the problem a Traceseal execution receipt is built for, so here is the mapping, limits included. The &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;receipt specification&lt;/a&gt; defines a signed JSON document in three parts. The execution section holds hashes of the signed skill manifest, the sandbox configuration, the inputs and the outputs, plus the exit code, the timing, and a hash of the matching entry in the operator's hash-chained audit log. The provenance section holds the publisher's key, per-file content hashes and a transparency log reference. The attestation is an ed25519 signature by the operator over both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ASI02 and ASI05:&lt;/strong&gt; the input, output and sandbox profile hashes record what a call received, what it returned and what configuration it ran under.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI04:&lt;/strong&gt; the manifest hash and publisher fingerprint tie the execution to one signed artefact. The transparency log reference lets a third party check that the manifest was published rather than swapped in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASI08 and ASI10:&lt;/strong&gt; the operator signature gives each entry the non-repudiation property the list asks for. In our runner the operator key never enters the sandbox, which is the arrangement ASI10 describes. A third party can check the signature with &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;one command&lt;/a&gt; and nothing but the receipt file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three limits, stated plainly. First, the specification says outright that a receipt "does NOT prove the operator is honest"; it is "an &lt;em&gt;attestation&lt;/em&gt;, not a &lt;em&gt;proof of execution&lt;/em&gt;". A receipt moves trust from the agent to the operator. That is the point, but the trust has moved rather than disappeared. Publishing to a &lt;a href="https://traceseal.io/blog/transparency-log-witnesses/" rel="noopener noreferrer"&gt;witnessed transparency log&lt;/a&gt; narrows what the operator can rewrite afterwards. Second, receipts detect nothing. A hijacked goal (ASI01) or a manipulated human (ASI09) still produces well-formed receipts. What they give you is a reliable account of what happened when you reconstruct it afterwards. Third, a receipt covers only what passes through the receipting boundary. An agent with a second route to a tool that bypasses it leaves no receipt for that route, as &lt;a href="https://traceseal.io/blog/sandbox-an-ai-coding-agent/" rel="noopener noreferrer"&gt;our sandbox write-up&lt;/a&gt; discusses.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the list leaves open
&lt;/h2&gt;

&lt;p&gt;The document is guidance, not a standard. It does not define a log format, say which component should hold the keys beyond "orchestrators", or explain how a third party should check a log that someone else kept. It uses "immutable", "tamper-evident" and "tamper-proof" in different entries without separating them, although they are different properties. The first prevents change. The second makes change detectable. The third is something no software log fully achieves. A team that cites the list in a security review will have to decide which one it means.&lt;/p&gt;

&lt;p&gt;The useful reading of the list is not that agentic systems need more logging, since they already produce plenty. It is that the record has to be one the agent could not have written differently, signed with a key the agent could not reach. Every other prevention section assumes that record exists when something goes wrong.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://traceseal.io/blog/owasp-top-10-agentic-applications/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;. Traceseal issues signed execution receipts for AI agents: an open spec and an open verifier, one command to check.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>owasp</category>
    </item>
    <item>
      <title>MCP security: what to log when an AI agent calls tools, and what the log can prove</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 22 Sep 2026 08:48:11 +0000</pubDate>
      <link>https://dev.to/traceseal/mcp-security-what-to-log-when-an-ai-agent-calls-tools-and-what-the-log-can-prove-4l5e</link>
      <guid>https://dev.to/traceseal/mcp-security-what-to-log-when-an-ai-agent-calls-tools-and-what-the-log-can-prove-4l5e</guid>
      <description>&lt;p&gt;The Model Context Protocol has become the default way an AI agent reaches a tool. That is convenient for the people wiring agents up and awkward for the people who later have to answer for what those agents did, because the protocol standardises the &lt;em&gt;call&lt;/em&gt; and says almost nothing about the &lt;em&gt;record&lt;/em&gt;. This piece goes through what the current spec actually asks you to log, where a tool call gets written down today, and what each of those records can and cannot establish when someone who does not trust you asks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consent is not a record
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/index" rel="noopener noreferrer"&gt;2025-11-25 revision of the specification&lt;/a&gt; is blunt about what a tool is. Under its key principles: "Tools represent arbitrary code execution and must be treated with appropriate caution," and "Hosts must obtain explicit user consent before invoking any tool." The &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/server/tools" rel="noopener noreferrer"&gt;tools section&lt;/a&gt; adds that clients should prompt for confirmation on sensitive operations and show tool inputs to the user before sending them to the server.&lt;/p&gt;

&lt;p&gt;All of that is about permission. None of it is about evidence. A consent dialog establishes that a person was shown a tool name and an argument block and clicked approve. It does not establish what the server did with those arguments, whether the result it returned reflected what changed, or whether the dialog the person saw was the one the model intended. Most hosts keep the consent decision in the same transcript store as everything else the model said, which puts it in the same position as any other line of that transcript: written by the runtime under review, editable by whoever administers the store. We went through why that fails an auditor's test in &lt;a href="https://traceseal.io/blog/soc-2-ai-agents-evidence/" rel="noopener noreferrer"&gt;the SOC 2 piece&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the spec does mention logging
&lt;/h2&gt;

&lt;p&gt;The protocol documents proper contain no logging requirement for tool calls. What exists lives in the &lt;a href="https://modelcontextprotocol.io/docs/2025-11-25/tutorials/security/security_best_practices" rel="noopener noreferrer"&gt;security best practices&lt;/a&gt; document, which sits alongside the authorisation spec rather than inside it, and it appears in three places.&lt;/p&gt;

&lt;p&gt;The first is the section on token passthrough, the anti-pattern where an MCP server forwards a client's upstream token to a downstream API without checking it was issued to the server. The document lists the consequences under a heading it calls "Accountability and Audit Trail Issues": the server cannot tell which client is calling, and "the downstream Resource Server's logs may show requests that appear to come from a different source with a different identity, rather than the MCP server that is actually forwarding the tokens." The spec's own words for the result are that "incident investigation, controls, and auditing" become more difficult. It is one of the clearer statements anywhere in the MCP corpus that a log written by the wrong party is worse than useful.&lt;/p&gt;

&lt;p&gt;The second is scope minimisation, where broad omnibus scopes are said to obscure audit trails because "a single omnibus scope masks user intent per operation." The mitigation includes a concrete instruction to servers: log elevation events, meaning the scope requested and the subset granted, with correlation IDs.&lt;/p&gt;

&lt;p&gt;The third is in the proxy guidance, where proxy services that spawn local servers over &lt;code&gt;stdio&lt;/code&gt; are told to "log all &lt;code&gt;stdio&lt;/code&gt; transport usage for security monitoring." That is a should, not a must, and it applies only to proxy architectures.&lt;/p&gt;

&lt;p&gt;So the sum of the spec's position is: log identity so the downstream system does not attribute your agent's actions to someone else, log scope changes so intent per operation is recoverable, and log process spawns if you proxy. Sensible, and thin. Nothing here says what a record of a tool call should contain, who should write it, or how anyone would check it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  An annotation is a claim, not a fact
&lt;/h2&gt;

&lt;p&gt;There is a detail in the tools spec that matters more for logging than it first appears. Tools can carry annotations, of which four are behavioural: &lt;code&gt;readOnlyHint&lt;/code&gt;, &lt;code&gt;destructiveHint&lt;/code&gt;, &lt;code&gt;idempotentHint&lt;/code&gt; and &lt;code&gt;openWorldHint&lt;/code&gt;. A host that wants to decide which calls need a human in the loop, or which calls to record in detail, will naturally reach for these. The &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/schema#toolannotations" rel="noopener noreferrer"&gt;schema&lt;/a&gt; says, in a note that is easy to skim past:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All properties in ToolAnnotations are hints. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like title). Clients should never make tool use decisions based on ToolAnnotations received from untrusted servers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tools section restates it as a requirement: clients must consider annotations untrusted unless they come from trusted servers. Which means a log line reading &lt;em&gt;read-only tool invoked, no confirmation required&lt;/em&gt; is not recording what the tool did. It is recording what the tool's author said the tool does, and the author is a party whose code the agent is about to execute. If the server was swapped, updated or &lt;a href="https://traceseal.io/blog/indirect-prompt-injection/" rel="noopener noreferrer"&gt;compromised through the content it serves&lt;/a&gt;, the annotation is exactly the field an attacker would set to &lt;code&gt;true&lt;/code&gt;. A record that inherits its risk classification from the server being classified has the same structural flaw as a transcript written by the agent being audited.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four places a tool call gets written down
&lt;/h2&gt;

&lt;p&gt;Follow one call through a typical deployment and it leaves traces in four places, each written by a different party with a different interest.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The host's transcript.&lt;/strong&gt; The model's request, the arguments it chose, the consent decision, and whatever the server returned. Written by the agent runtime. Complete in the sense of containing everything the model saw, and worthless as independent evidence for the same reason.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The MCP server's own log.&lt;/strong&gt; Whatever the server operator chose to record. If you run the server, it is under your control and therefore not independent. If a vendor runs it, it is under theirs, and you will get it in an incident only if they choose to give it to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The downstream system.&lt;/strong&gt; The repository, the database, the ticketing API. This is the closest thing to ground truth about effect, and the token passthrough section above explains the catch: unless the server authenticated correctly, this log may attribute the action to the wrong identity, or to a shared service account that a dozen agents use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The user's approval.&lt;/strong&gt; A click, or a standing allowlist. Usually stored in the host, so it collapses into the first bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of the four is signed by anyone. None is bound to the code that ran: the host records a tool &lt;em&gt;name&lt;/em&gt;, and tool names are only required to be unique within a server, so the same name can point at different code tomorrow. And none of them can be handed to a third party without also handing over the trust you place in whoever wrote it. That is the gap between having logs and having evidence, and it is the gap that &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;the receipts primer&lt;/a&gt; is about.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The test worth applying:&lt;/strong&gt; for each record of a tool call, ask who wrote it and whether they had the ability, and any reason, to write something else. If the answer to the first is "the agent" or "the tool", the record is testimony, not evidence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What a record would need
&lt;/h2&gt;

&lt;p&gt;If the aim is a record that survives that test, the shape is not exotic. Per call, capture the server's identity as a hash of its code or manifest rather than its display name, the tool name and a hash of the exact arguments, a hash of the result, the identity that authorised the call and how (interactive consent, allowlist, or scope elevation with its correlation ID), and the constraints the call ran under, meaning the sandbox profile if there was one. Then sign the whole thing with a key the agent process cannot reach, and put the signature somewhere append-only that a third party can check.&lt;/p&gt;

&lt;p&gt;That is deliberately the shape of a Traceseal execution receipt. The &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;specification&lt;/a&gt; binds a skill manifest hash, a sandbox profile hash, input and output hashes, exit code and timing under an ed25519 signature from an operator key that never enters the sandbox, and the verifier is &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;a single command&lt;/a&gt; that needs nothing but the receipt. The &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;walkthrough&lt;/a&gt; shows what an &lt;code&gt;[OK]&lt;/code&gt; does and does not establish.&lt;/p&gt;

&lt;p&gt;Two limits, stated plainly. A receipt covers what passes through the receipting boundary. A local MCP server launched over &lt;code&gt;stdio&lt;/code&gt; runs with the client's privileges, and the security document is explicit that without sandboxing it can read anything the client can; if the agent can reach a tool through a path that produces no receipt, the receipt for the path it did use proves nothing about the other one. And a receipt records that a hashed input produced a hashed output under a hashed policy. It does not say the output was correct or the policy adequate. &lt;a href="https://traceseal.io/blog/sandbox-an-ai-coding-agent/" rel="noopener noreferrer"&gt;Our sandbox write-up&lt;/a&gt; covers where a containment boundary stops being demonstrable, and the same line applies here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still open
&lt;/h2&gt;

&lt;p&gt;The spec does not define a log format for tool calls, does not require servers to identify their code by hash, and leaves annotation trust to each client's judgement of which servers are "trusted". Every host currently records tool use in its own shape, which means two organisations that both "log all MCP calls" have records that cannot be compared, let alone verified by the same tool. Whether that changes depends on the protocol's maintainers and the vendors shipping hosts, not on anything a single deployment can do.&lt;/p&gt;

&lt;p&gt;What a single deployment can do is stop treating the transcript as the record. The spec has already conceded that tools are arbitrary code execution and that a tool's own description of itself cannot be trusted. The consistent next step is to conclude that the tool's, and the agent's, description of what happened cannot be trusted either, and to keep a record they did not write.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://traceseal.io/blog/mcp-security-tool-call-logging/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;. Traceseal issues signed execution receipts for AI agents: an open spec and an open verifier, one command to check.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>mcp</category>
      <category>agents</category>
    </item>
    <item>
      <title>SOC 2 for AI agents: what evidence an auditor can actually accept</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 15 Sep 2026 08:47:10 +0000</pubDate>
      <link>https://dev.to/traceseal/soc-2-for-ai-agents-what-evidence-an-auditor-can-actually-accept-16pl</link>
      <guid>https://dev.to/traceseal/soc-2-for-ai-agents-what-evidence-an-auditor-can-actually-accept-16pl</guid>
      <description>&lt;p&gt;At some point in the next twelve months, an auditor is going to ask what your AI agents did. Not what they were allowed to do — what they did. If you sell software to companies that care about SOC 2, the question arrives through your own audit; if you buy it, the question arrives through a vendor questionnaire. Either way, the answer "we have the chat logs" is about to stop being good enough, and it is worth understanding why before the fieldwork starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where agents land in the criteria
&lt;/h2&gt;

&lt;p&gt;SOC 2 does not have an AI section. A report is issued against the AICPA's &lt;a href="https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022" rel="noopener noreferrer"&gt;2017 Trust Services Criteria, with the points of focus revised in 2022&lt;/a&gt;, and nothing in that document mentions agents, models or prompts. That is not a loophole. The criteria are written around &lt;em&gt;systems&lt;/em&gt; and &lt;em&gt;system components&lt;/em&gt;, and an agent that reads a ticket, edits a repository and opens a pull request is a system component in every sense the criteria care about. It holds credentials, it changes production, and it does so without a human approving each step.&lt;/p&gt;

&lt;p&gt;Three criteria do most of the work. &lt;strong&gt;CC6&lt;/strong&gt; covers logical access: what the agent can reach, with which credentials, and who granted them. &lt;strong&gt;CC8&lt;/strong&gt; covers change management: whether changes the agent makes to infrastructure, data or code go through the same authorisation, testing and approval controls as changes a person makes. And &lt;strong&gt;CC7&lt;/strong&gt; covers system operations. The wording of &lt;a href="https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022" rel="noopener noreferrer"&gt;CC7.2&lt;/a&gt; is the one to read twice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The entity monitors system components and the operation of those components for anomalies that are indicative of malicious acts, natural disasters, and errors affecting the entity's ability to meet its objectives; anomalies are analyzed to determine whether they represent security events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To monitor the operation of a component for anomalies, you need a record of its operation that you can trust more than the component itself. For a database or a load balancer that is routine. For an agent, whose "operation" is a sequence of tool calls chosen at runtime by a model, it is the whole problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  ISO/IEC 42001 says the same thing more directly
&lt;/h2&gt;

&lt;p&gt;If you are also looking at &lt;a href="https://www.iso.org/standard/81230.html" rel="noopener noreferrer"&gt;ISO/IEC 42001:2023&lt;/a&gt;, the AI management system standard, the requirement is explicit rather than implied. Annex A control A.6.2.8, &lt;em&gt;AI system recording of event logs&lt;/em&gt;, requires the organisation to determine at which phases of the AI system's life cycle event logs are recorded, and sets a floor: at minimum, while the system is in use. The standard's text is paywalled, so we will not quote further, but the shape is clear. Logging the agent while it is operating is not an optional hardening step under 42001; it is a control you either have or do not.&lt;/p&gt;

&lt;p&gt;Both frameworks converge on one demand. You need a record of what the agent did, produced in a way that the auditor can rely on. Which raises the question of what "rely on" means.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a transcript is not evidence
&lt;/h2&gt;

&lt;p&gt;Most teams' first answer is the conversation log: the prompts, the model's responses, the tool calls and their results, stored by whatever agent framework they run. It feels complete, and for debugging it often is. For an auditor it has three problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is written by the thing being audited.&lt;/strong&gt; The agent runtime produces the transcript, and the agent runtime is the component whose behaviour is in question. A model that has been &lt;a href="https://traceseal.io/blog/indirect-prompt-injection/" rel="noopener noreferrer"&gt;hijacked by the content it read&lt;/a&gt; will still produce a tidy transcript; it will just be a transcript of the wrong actions. An auditor testing CC7.2 wants a monitoring record that would catch the anomaly, not one that the anomaly gets to write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It describes intent, not effect.&lt;/strong&gt; A tool-call record says the agent asked to run &lt;code&gt;git push&lt;/code&gt;. It does not say the push succeeded, what commit went where, or whether the working tree it pushed matched the diff shown to the reviewer. The distinction between what an agent reported and what happened on disk is the entire premise of &lt;a href="https://traceseal.io/blog/alibi-grade-agents-on-ground-truth/" rel="noopener noreferrer"&gt;grading agents on ground truth&lt;/a&gt;, and it is also the distinction an auditor is trained to draw.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It can be edited after the fact by anyone with write access to the store.&lt;/strong&gt; A Type 2 report covers a period, commonly six to twelve months, and the auditor samples from it. If the transcript store is a database your platform team administers, the auditor has to test the controls over &lt;em&gt;that&lt;/em&gt; before the transcripts are worth anything. We covered why a hash chain alone does not close this gap in &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;the receipts primer&lt;/a&gt;: a chain proves order and continuity, but whoever holds the chain can regenerate it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The test an auditor actually applies:&lt;/strong&gt; can I obtain this evidence, or something that corroborates it, from a source other than the system under review? A transcript fails that test by construction. It is the system under review describing itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What passes
&lt;/h2&gt;

&lt;p&gt;Audit evidence has to be &lt;strong&gt;reperformable&lt;/strong&gt;. The auditor picks a sample — twenty agent runs from March, say — and needs to establish, for each one, what ran, under what constraints, and with what result, in a way that does not depend on taking your word for it. That maps onto four properties a record of agent activity needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bound to the code that ran.&lt;/strong&gt; A hash of the exact skill or tool code, so the auditor can confirm the sampled run used the approved version and not something patched in between. This is what makes the record useful under CC8 as well as CC7.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bound to the constraints it ran under.&lt;/strong&gt; A hash of the sandbox profile or execution policy, so "the agent could not reach the network" is a checkable claim rather than an assertion. What a sandbox can and cannot prove on its own is in &lt;a href="https://traceseal.io/blog/sandbox-an-ai-coding-agent/" rel="noopener noreferrer"&gt;our sandbox write-up&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bound to inputs and outputs.&lt;/strong&gt; Hashes rather than contents, so the auditor can confirm a specific input produced a specific output without the record itself leaking customer data. This matters for the confidentiality criteria: an evidence trail that stores raw prompts and outputs creates a second copy of every sensitive thing the agent touched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signed by a key the agent cannot use.&lt;/strong&gt; The attestation has to come from outside the agent's reach, otherwise you are back to the transcript problem. And the auditor has to be able to check the signature with a public tool, on their own machine, without asking you to run anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is, deliberately, the structure of a Traceseal execution receipt. The &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;specification&lt;/a&gt; is short enough to hand to an auditor: an &lt;code&gt;execution&lt;/code&gt; block with the skill manifest hash, sandbox profile hash, input and output hashes, exit code and timing; a &lt;code&gt;provenance&lt;/code&gt; block that names who signed the code and where it sits in the transparency log; and an &lt;code&gt;attestation&lt;/code&gt; block with an ed25519 signature over both from an operator key that never enters the sandbox. The verifier is &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;on PyPI&lt;/a&gt;, needs only the receipt file, and prints what the signature does and does not establish. The walkthrough is in &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;How to verify what an AI agent actually did&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Two honest limits. A receipt proves the operator attested that &lt;em&gt;this code&lt;/em&gt; ran under &lt;em&gt;this policy&lt;/em&gt; and produced &lt;em&gt;this output hash&lt;/em&gt;; it does not prove the output was correct or the policy was sensible. Those remain judgement calls, which is what the auditor is for. And a receipt only covers what was executed through the receipting boundary. If the agent can also act through a path that does not produce receipts, the auditor will find it, and should.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do before fieldwork
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Put agents on the system description.&lt;/strong&gt; If an agent holds credentials or changes production, it belongs in the scoped system, named as a component, with its access documented under CC6. Auditors find undisclosed components far less forgivable than disclosed ones with gaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route every consequential action through one execution boundary&lt;/strong&gt; that produces a signed record, and make it the only path. One receipted path and one unreceipted path is worse than either alone, because it invites the question of which one was used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the records for the whole audit period, and then some.&lt;/strong&gt; A Type 2 sample can reach back to the first day of the period. The EU AI Act's six-month log retention floor for high-risk systems, covered in &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;our audit-trail piece&lt;/a&gt;, is a reasonable minimum even where the Act does not apply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test your own evidence before the auditor does.&lt;/strong&gt; Pick twenty runs at random, verify each receipt with the public tool, and confirm the manifest hash matches an approved release. If you cannot do that in an afternoon, the auditor cannot do it in a week.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SOC 2 has never asked whether your systems are clever. It asks whether you can show, to someone who has no reason to believe you, that they did what you say they did. Agents make that harder, because for the first time the component in question decides its own actions. The answer is not a better transcript. It is a record the agent did not write.&lt;/p&gt;




&lt;p&gt;The receipt format, the verifier and the transparency log are open: the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, the &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt; and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;. Originally published at &lt;a href="https://traceseal.io/blog/soc-2-ai-agents-evidence/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>compliance</category>
      <category>devops</category>
    </item>
    <item>
      <title>The EU AI Liability Directive is dead: what actually governs AI agent harm now</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 01 Sep 2026 08:49:12 +0000</pubDate>
      <link>https://dev.to/traceseal/the-eu-ai-liability-directive-is-dead-what-actually-governs-ai-agent-harm-now-34f4</link>
      <guid>https://dev.to/traceseal/the-eu-ai-liability-directive-is-dead-what-actually-governs-ai-agent-harm-now-34f4</guid>
      <description>&lt;h1&gt;
  
  
  The EU AI Liability Directive is dead: what actually governs AI agent harm now
&lt;/h1&gt;

&lt;p&gt;On &lt;strong&gt;6 October 2025&lt;/strong&gt;, a short notice appeared in the EU's Official Journal confirming what the European Commission had already signalled eight months earlier: the &lt;a href="https://www.europarl.europa.eu/legislative-train/theme-a-europe-fit-for-the-digital-age/file-ai-liability-directive" rel="noopener noreferrer"&gt;AI Liability Directive&lt;/a&gt; was not coming. The withdrawal followed a Commission decision at its 2533rd meeting on 16 July 2025, and closed a proposal that had been on the table since 2022 — the one piece of EU law written specifically to make it easier to prove an AI system caused you harm.&lt;/p&gt;

&lt;p&gt;If you run agents that touch EU users, or sell into the EU, the practical question is not "what did we lose." It is: &lt;strong&gt;what governs an AI agent causing harm now, and does it cover the harm your agent could actually cause?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the directive was meant to do
&lt;/h2&gt;

&lt;p&gt;The proposed AI Liability Directive would have complemented the AI Act rather than duplicated it, easing a claimant's path to compensation for AI-caused harm along two specific mechanisms: a court's power to order &lt;strong&gt;disclosure of evidence&lt;/strong&gt; from a provider or deployer of a high-risk AI system, and a &lt;strong&gt;rebuttable presumption of a causal link&lt;/strong&gt; between an AI system's non-compliance and the resulting harm, where the claimant could show fault was likely but proving causation was disproportionately hard given how the system worked. It was designed for exactly the case that makes agents hard to litigate: the claimant cannot see inside the system, and the operator holds all the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it was dropped
&lt;/h2&gt;

&lt;p&gt;The Commission's &lt;a href="https://iapp.org/news/a/european-commission-withdraws-ai-liability-directive-from-consideration" rel="noopener noreferrer"&gt;2025 work programme&lt;/a&gt;, adopted 11 February 2025, listed the directive for withdrawal with a one-line justification: "no foreseeable agreement." Member states and industry groups had spent two years unable to converge on the scope and mechanics, and the withdrawal landed alongside a broader simplification push that also dropped the ePrivacy Regulation. The Commission's own text left the door open rather than closing it: it "will assess whether another proposal should be tabled or another type of approach should be chosen." As of this piece, no replacement has been tabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  What already applies instead
&lt;/h2&gt;

&lt;p&gt;The gap is not empty. The EU's overhauled &lt;strong&gt;Product Liability Directive&lt;/strong&gt;, &lt;a href="https://eur-lex.europa.eu/eli/dir/2024/2853/oj" rel="noopener noreferrer"&gt;Directive (EU) 2024/2853&lt;/a&gt;, was adopted separately and was never at risk — it entered into force in the weeks after its publication on 18 November 2024, and member states have until &lt;strong&gt;9 December 2026&lt;/strong&gt; to transpose it. Unlike the withdrawn directive, it is not AI-specific, but it was substantially rewritten with software in mind: the definition of a covered "product" now explicitly extends to software, firmware and AI systems, closing a long-standing ambiguity about whether a defective model was a "product" at all.&lt;/p&gt;

&lt;p&gt;The mechanics carry over some of what the withdrawn directive was trying to do. A court can compel a defendant to &lt;strong&gt;disclose relevant evidence&lt;/strong&gt; once a claimant has made a sufficiently plausible case. And where the claimant can show a defect is likely but proving it is excessively difficult because of the product's technical complexity — or where the defendant fails to comply with a disclosure order — the product is &lt;strong&gt;presumed defective&lt;/strong&gt;. Practically, that is close to what the AI Liability Directive would have delivered for high-risk AI: the burden moves toward whoever holds the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap it doesn't close
&lt;/h2&gt;

&lt;p&gt;The catch is scope. The Product Liability Directive exists to protect natural persons, and the damage it compensates is &lt;strong&gt;death, personal injury, damage to property kept for private use, and destruction or corruption of data not used for professional purposes&lt;/strong&gt;. It expressly excludes &lt;strong&gt;pure economic loss&lt;/strong&gt; and property "used exclusively for professional purposes." A consumer whose smart device is bricked by a faulty AI update is covered. A business whose AI agent misfires a transaction, corrupts a business dataset, or ships a bad deployment is not — that is a commercial, economic loss, and it stays exactly where the withdrawn directive would have reached: general national contract and tort law, unharmonised across 27 member states.&lt;/p&gt;

&lt;p&gt;That is the case most companies running agents actually have. Agents deployed in a business context, causing business harm, to another business. The one EU proposal aimed squarely at that gap is the one that got withdrawn.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means if you run an agent
&lt;/h2&gt;

&lt;p&gt;Two different regimes now apply depending on who got hurt, and neither hands a B2B agent operator's counterparty an easy route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consumer-facing harm&lt;/strong&gt; — falls under the revised Product Liability Directive from December 2026 for newly placed products. Courts there can already order disclosure and will presume a defect against whoever cannot produce a credible answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business-to-business, economic harm&lt;/strong&gt; — the case a coding agent, a data pipeline agent, or an internal automation agent is most likely to produce — stays on national contract and tort law, with no EU-wide presumption to lean on either way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In both cases, the practical position is the same: nobody is going to take a log file's word for what an agent did. Where the Product Liability Directive applies, the mechanism rewards whoever can actually satisfy a disclosure order with something a court finds credible, not just present. Where it doesn't apply, and the dispute runs on ordinary national rules of evidence, the party who can independently prove what happened is arguing from a position the other side cannot easily contest. A &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;signed execution receipt&lt;/a&gt; — a canonical record of inputs, outputs, timing and sandbox policy, signed with a key the agent never touches — is built to be exactly that kind of artefact: something you hand over under a disclosure order, or into evidence, rather than something a claimant successfully argues you should be presumed to be hiding. What such a receipt does and does not establish, and the fields an audit trail needs regardless of which regime ends up applying, are covered in &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;How to verify what an AI agent actually did&lt;/a&gt; and &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;AI agent audit trails&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The uncomfortable read:&lt;/strong&gt; the one EU proposal written specifically for AI harm was withdrawn for lack of agreement. What replaced it either doesn't reach the B2B case most agents create, or rewards whoever can actually produce evidence once a court asks for it. Either way, "trust our logs" was never the safer bet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What to do before December 2026
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Map your exposure.&lt;/strong&gt; Separate agent deployments that touch consumers and personal data from ones that only produce business-to-business, economic outcomes — they now sit under genuinely different legal regimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't wait for the transposition deadline.&lt;/strong&gt; The Product Liability Directive only covers products placed on the market after 9 December 2026; anything shipped earlier, and anything B2B, is already governed by whatever your national law and your contracts say today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the evidence trail regardless of which regime applies.&lt;/strong&gt; Disclosure orders and presumptions of defect both cut against whoever cannot produce a credible record. That is true whether a court is applying the new EU rules or your counterparty's home jurisdiction's ordinary contract law.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The receipt format, the verifier and the transparency log are open: the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, the &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt; and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;. Regulation in this area is still being written and rewritten around you. The evidence you can produce when someone finally asks is not something you can backdate.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://traceseal.io/blog/eu-ai-liability-directive-withdrawn/" rel="noopener noreferrer"&gt;traceseal.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>legal</category>
      <category>compliance</category>
      <category>security</category>
    </item>
    <item>
      <title>Indirect prompt injection: how an AI agent gets hijacked by the data it reads</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:50:13 +0000</pubDate>
      <link>https://dev.to/traceseal/indirect-prompt-injection-how-an-ai-agent-gets-hijacked-by-the-data-it-reads-5ceo</link>
      <guid>https://dev.to/traceseal/indirect-prompt-injection-how-an-ai-agent-gets-hijacked-by-the-data-it-reads-5ceo</guid>
      <description>&lt;p&gt;An agent that reads a web page, an issue tracker, an inbox or a pull-request description is reading text that somebody else wrote. &lt;strong&gt;Indirect prompt injection&lt;/strong&gt; is what happens when that text contains instructions addressed to the model rather than to the reader, and the model carries them out using the agent's tools and the agent's permissions.&lt;/p&gt;

&lt;p&gt;The uncomfortable part is not that the attack exists. It is that the people who study it hardest do not claim to have solved it. This piece covers the mechanism, what the standard mitigations genuinely buy you, and what a team can do about the residue: making what the agent actually did provable after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direct and indirect
&lt;/h2&gt;

&lt;p&gt;OWASP's &lt;a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/" rel="noopener noreferrer"&gt;LLM01:2025 Prompt Injection&lt;/a&gt; entry splits the class in two. A &lt;strong&gt;direct&lt;/strong&gt; injection is one where a user's prompt input "directly alters the behavior of the model in unintended or unexpected ways". An &lt;strong&gt;indirect&lt;/strong&gt; injection occurs "when an LLM accepts input from external sources, such as websites or files" whose content alters the model's behaviour.&lt;/p&gt;

&lt;p&gt;The threat model changes completely between the two. A direct injection requires the attacker to be talking to your system. An indirect one does not. The attacker writes something once, somewhere your agent will eventually read, and waits for the agent to come to them.&lt;/p&gt;

&lt;p&gt;OWASP also records that these inputs "do not need to be human-visible/readable, as long as the content is parsed by the model". An HTML comment, pale text on a pale background, a document's metadata or wording rendered inside an image all qualify. A human reviewing the same source may see nothing at all.&lt;/p&gt;

&lt;p&gt;The technique was named by Simon Willison in &lt;a href="https://simonwillison.net/2022/Sep/12/prompt-injection/" rel="noopener noreferrer"&gt;September 2022&lt;/a&gt;. Its indirect form was set out systematically in &lt;a href="https://arxiv.org/abs/2302.12173" rel="noopener noreferrer"&gt;Not what you've signed up for&lt;/a&gt; (Greshake et al., February 2023), which argued that LLM-integrated applications "blur the line between data and instructions" and demonstrated working attacks against production systems, including Bing's GPT-4-powered chat and code-completion engines. The authors showed that "processing retrieved prompts can act as arbitrary code execution".&lt;/p&gt;

&lt;h2&gt;
  
  
  Why there is no boundary to enforce
&lt;/h2&gt;

&lt;p&gt;Classical injection bugs have a structural fix. SQL injection ends when queries are parameterised, because the database is then told, in a channel the attacker cannot reach, which bytes are code and which are data. Prompt injection has no equivalent. Instructions and content arrive at the model as one undifferentiated token sequence. As Willison puts it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;LLMs are unable to reliably distinguish the importance of instructions based on where they came from. Everything eventually gets glued together into a sequence of tokens and fed to the model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Delimiters, tagging of untrusted regions and system prompts that say "ignore any instructions in the content below" all raise the cost of an attack, sometimes considerably. What none of them provide is a rule the model is architecturally obliged to obey. They are strong suggestions to a system that treats every token as a suggestion.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lethal trifecta
&lt;/h2&gt;

&lt;p&gt;Willison's &lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/" rel="noopener noreferrer"&gt;lethal trifecta&lt;/a&gt; framing (June 2025) is the clearest way to work out whether a given agent is exposed. Three capabilities together create the risk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access to private data&lt;/strong&gt; — which is usually the entire point of giving the agent tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exposure to untrusted content&lt;/strong&gt; — any route by which attacker-controlled text or images reach the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ability to communicate externally&lt;/strong&gt; in a way that could carry data back out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"If your agent combines these three features," Willison writes, "an attacker can easily trick it into accessing your private data and sending it to that attacker." The awkward observation is that most agents worth deploying hold all three by design. A coding agent that reads a private repository, browses documentation and opens a pull request has the full set. Dropping one leg is a real mitigation and frequently an unacceptable product decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the mitigations actually buy you
&lt;/h2&gt;

&lt;p&gt;OWASP lists seven countermeasures: constraining model behaviour, defining and validating output formats, input and output filtering, least-privilege access, human approval for high-risk actions, segregating external content, and adversarial testing. Each is worth doing. The entry prefaces all of them with a sentence that deserves quoting exactly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given the stochastic influence at the heart of the way models work, it is unclear if there are fool-proof methods of prevention for prompt injection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;NIST arrives somewhere similar from another direction. &lt;a href="https://csrc.nist.gov/pubs/ai/100/2/e2025/final" rel="noopener noreferrer"&gt;AI 100-2 E2025&lt;/a&gt;, &lt;em&gt;Adversarial Machine Learning: A Taxonomy and Terminology of Attacks and Mitigations&lt;/em&gt; (March 2025), describes its own purpose as identifying "current challenges in the life cycle of AI systems" and describing "corresponding methods for mitigating and managing the consequences of those attacks". Managing consequences is the language of an attack that landed.&lt;/p&gt;

&lt;p&gt;So the design question is not only how to stop injection. It is what your system can still tell you once one gets through.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when the target is an agent
&lt;/h2&gt;

&lt;p&gt;Against a chatbot, a successful injection produces wrong text and a bad afternoon. Against an agent it produces actions: a file written, a secret read, an API called, a branch pushed, a message sent on your behalf. OWASP's impact list includes "executing arbitrary commands in connected systems" and "providing unauthorized access to functions available to the LLM".&lt;/p&gt;

&lt;p&gt;Two things follow. First, blast radius is set by the permissions you granted, not by the model's judgement, because the model's judgement is the component under attack. Second, and less widely appreciated: &lt;strong&gt;the agent's own account of events becomes attacker-influenced output&lt;/strong&gt;. If the injected text says "do X, then report that you did Y", the transcript says Y. Reasoning traces, tool-call summaries and self-reports are all generated downstream of the compromise. They are the last artefacts that should be treated as evidence, and often the only ones a team has kept.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the regulation already assumes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://artificialintelligenceact.eu/article/15/" rel="noopener noreferrer"&gt;Article 15&lt;/a&gt; of the &lt;a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj" rel="noopener noreferrer"&gt;EU AI Act&lt;/a&gt; requires that high-risk AI systems "shall be resilient against attempts by unauthorised third parties to alter their use, outputs or performance by exploiting system vulnerabilities". The technical measures it calls for must, where appropriate, "prevent, detect, respond to, resolve and control for" AI-specific attacks, expressly including "inputs designed to cause the AI model to make a mistake".&lt;/p&gt;

&lt;p&gt;Article 15 binds high-risk systems, so it will not reach every agent deployment. The drafting is instructive regardless: prevention is one verb out of five. Detection, response and resolution each presuppose an attack that succeeded, and each requires a record that stays trustworthy after the system it describes has been compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two controls that survive a failed prevention
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Containment.&lt;/strong&gt; A kernel-namespace sandbox bounds what an injected instruction can reach, whatever the model decides to do. We published a working bubblewrap profile in &lt;a href="https://traceseal.io/blog/sandbox-an-ai-coding-agent/" rel="noopener noreferrer"&gt;How to sandbox an AI coding agent&lt;/a&gt;, along with the DNS leak we only found by running it. A sandbox limits consequences. It will not tell you that an injection arrived.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A record made outside the agent.&lt;/strong&gt; A &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;signed execution receipt&lt;/a&gt; is a canonical record of inputs, outputs, timing and sandbox policy, stored as SHA-256 hashes and signed with an ed25519 key the sandboxed process never sees. The agent does not write it and cannot amend it. A third party checks it offline, with no access to your systems:&lt;br&gt;
&lt;/p&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;pip &lt;span class="nb"&gt;install &lt;/span&gt;traceseal-verify
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;traceseal-verify receipt.json
&lt;span class="go"&gt;[OK] receipt.json — operator signature verified
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of this prevents injection. What it does is make the aftermath answerable. Given a suspected compromise last Tuesday, what did the agent read, what did it touch, and can you demonstrate that to somebody who has no reason to take your word for it? The walkthrough of what such a check does and does not establish is in &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;How to verify what an AI agent actually did&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to record so an injection stays provable
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The exact content the agent read&lt;/strong&gt;, hashed at fetch time. If the poisoned page is quietly edited afterwards, the hash still pins what was actually served to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every tool call and its real arguments&lt;/strong&gt;, captured at the tool boundary rather than from the model's description of what it intended.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The sandbox policy in force&lt;/strong&gt; during the run, so containment becomes something a reader can check rather than something you assert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observed side effects&lt;/strong&gt; — files written, network destinations reached, commits created — recorded from outside the agent process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A signature over all of it&lt;/strong&gt;, made with a key the agent cannot reach, so the record's integrity does not depend on the agent having behaved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The field-level detail, including the EU AI Act's log-retention floor, is in &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;AI agent audit trails&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The question to sit with:&lt;/strong&gt; if one of your agents read a poisoned document last week, would you learn about it from your own records, or from the person its data ended up with?&lt;/p&gt;

&lt;p&gt;The receipt format, the verifier and the transparency log are open: the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, the &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt; and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;. Prevention research will keep moving, and it should. In the meantime, the attacks that get through are the ones your evidence has to account for.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
    </item>
    <item>
      <title>Transparency log witnesses: how cosigned checkpoints make a log tamper-evident</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 18 Aug 2026 08:49:30 +0000</pubDate>
      <link>https://dev.to/traceseal/transparency-log-witnesses-how-cosigned-checkpoints-make-a-log-tamper-evident-2c4n</link>
      <guid>https://dev.to/traceseal/transparency-log-witnesses-how-cosigned-checkpoints-make-a-log-tamper-evident-2c4n</guid>
      <description>&lt;p&gt;On 17 August 2026, at 14:17:40 UTC, an independent witness operated by &lt;a href="https://witness.markovianprotocol.com/checkpoints" rel="noopener noreferrer"&gt;Markovian Protocol&lt;/a&gt; cosigned the checkpoint of our public transparency log. Nothing about the log changed that afternoon. What changed is what we can prove about it: from that moment, if we ever rewrote the log's history, a third party outside our infrastructure would be holding cryptographic evidence of the version we abandoned.&lt;/p&gt;

&lt;p&gt;This post explains what a transparency log witness is, what a cosigned checkpoint actually proves, and the obligations you take on when one starts watching your log. Most of it we learned by going through the process rather than by reading about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap witnesses close: a log can lie to you specifically
&lt;/h2&gt;

&lt;p&gt;An append-only log is the standard answer to "how do I show my records existed before the dispute did". You publish entries into a Merkle tree, the tree has a root hash, and anyone can check that a given entry is included. Certificate Transparency (&lt;a href="https://datatracker.ietf.org/doc/html/rfc6962" rel="noopener noreferrer"&gt;RFC 6962&lt;/a&gt;) runs the web's certificate issuance through this structure; the &lt;a href="https://go.dev/ref/mod#checksum-database" rel="noopener noreferrer"&gt;Go checksum database&lt;/a&gt; does it for module hashes; &lt;a href="https://docs.sigstore.dev/logging/overview/" rel="noopener noreferrer"&gt;Sigstore's Rekor&lt;/a&gt; does it for software signatures.&lt;/p&gt;

&lt;p&gt;But a Merkle tree on its own has a hole, and RFC 6962 was upfront about it: detecting a misbehaving log requires people to compare notes on what the log showed them. A log operator who controls the serving infrastructure can present one version of the tree to the auditor and a different version to everyone else. Each view is internally consistent, each proof checks out, and the two views never meet. This is the &lt;strong&gt;split-view attack&lt;/strong&gt;, and no amount of hashing inside the log prevents it, because the operator is the one doing the hashing.&lt;/p&gt;

&lt;p&gt;For our purposes the operator's honesty is exactly what is in question. Traceseal exists because &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;logs written by the party under scrutiny are not evidence&lt;/a&gt;. Our transparency log anchors signed agent receipts, and until last week it had the same weakness as every self-hosted log: you had to trust us not to serve you a private version of history.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a checkpoint is
&lt;/h2&gt;

&lt;p&gt;A checkpoint is a small signed statement of the log's current state, in the format specified by &lt;a href="https://c2sp.org/tlog-checkpoint" rel="noopener noreferrer"&gt;c2sp.org/tlog-checkpoint&lt;/a&gt;: the log's name (its origin), the number of entries, and the root hash of the tree at that size, wrapped in a &lt;a href="https://c2sp.org/signed-note" rel="noopener noreferrer"&gt;signed note&lt;/a&gt;. Ours currently reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;log.traceseal.io
19
OhQzzSdQHnpofrO/F2reZkiIDDPjUEM+wuGNc4mlD84=

— log.traceseal.io vLzsyMryP2doyfOtISb5...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nineteen entries. Certificate Transparency logs hold billions, and the format is the same, which is rather the point: the protocol does not care whether it is guarding the web PKI or a small receipts log. A checkpoint commits the operator to one specific history up to one specific size. What it cannot do alone is stop the operator issuing two different commitments to two different audiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a witness actually does
&lt;/h2&gt;

&lt;p&gt;A witness is a service, run by someone other than the log operator, that keeps the latest checkpoint it has seen for each log it follows. The protocol, &lt;a href="https://c2sp.org/tlog-witness" rel="noopener noreferrer"&gt;c2sp.org/tlog-witness&lt;/a&gt; (v1.0.0, March 2026), is short enough to read over coffee. The core of it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Witnesses verify that the checkpoint is consistent with their previously recorded state of the log (if any), and return a timestamped cosignature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Consistent" carries the whole load. When our log grows and asks the witness to cosign the new checkpoint, it must supply a consistency proof: a Merkle proof that the tree at the new size contains the tree the witness already recorded as a prefix. If the proof fails — if any old entry was altered or removed — the witness refuses. The &lt;a href="https://c2sp.org/tlog-cosignature" rel="noopener noreferrer"&gt;cosignature&lt;/a&gt; it returns on success embeds its own timestamp, so the statement a verifier ends up with is precise: &lt;strong&gt;at this time, this witness saw this exact view of the log, and that view extended every view the witness had seen before.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A split view now requires deceiving the witness as well, and the witness's records are not ours to edit. That is the entire trick. No new cryptography, just a signature from someone whose incentives are not our incentives.&lt;/p&gt;

&lt;p&gt;One implementation detail that cost us an afternoon: the log's own &lt;code&gt;/checkpoint&lt;/code&gt; endpoint serves only the log's signature. The cosignature lives on the witness's copy, at &lt;code&gt;/&amp;lt;sha256(origin)&amp;gt;/checkpoint&lt;/code&gt; on the witness's host. If you fetch your own checkpoint and wonder where the witness signature went, it was never going to be there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a cosignature does not prove
&lt;/h2&gt;

&lt;p&gt;Witnessing is content-blind, and it is worth being blunt about the edges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It says nothing about what the entries mean.&lt;/strong&gt; A witness checks append-only consistency, not truth. A log of fabricated receipts, faithfully appended and never rewritten, will be cosigned without complaint. Content verification is a separate job — ours is done by &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;signature checks on each receipt&lt;/a&gt;, and the honest-limits caveats in that post still apply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One witness is one extra party to corrupt.&lt;/strong&gt; If the log operator and the witness collude, the split view returns. The mitigation is a quorum of independent witnesses, which is why a shared &lt;a href="https://github.com/transparency-dev/witness-network" rel="noopener noreferrer"&gt;witness network&lt;/a&gt; is being built around the C2SP protocols rather than each log recruiting its own friendly observer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A cosigned checkpoint covers the log up to that size.&lt;/strong&gt; Entries appended since the last cosignature are committed by the log's own signature only, until the next witnessing round picks them up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The part nobody advertises: witnessing binds you
&lt;/h2&gt;

&lt;p&gt;Before the pin, our append-only claim was policy. We could have quietly regenerated the log, re-signed it, and nobody outside would have been the wiser. After the pin it is a commitment with teeth, and the teeth point at us.&lt;/p&gt;

&lt;p&gt;The log must now stay strictly append-only forever, because the witness's copy of every cosigned checkpoint is permanent evidence against any rewrite. And the log's signing key can no longer rotate casually: the witness pins the key it verified, and a checkpoint signed by an unknown key gets refused. We put both obligations in writing to the witness's operator — no history rewrites, and no key rotation without coordinating first — and they were confirmed back to us as the terms of witnessing.&lt;/p&gt;

&lt;p&gt;Worth sitting with: a tamper-evident log is not a feature you add. It is a constraint you accept. If you are not prepared to be caught by your own infrastructure, external witnessing is not for you — which is precisely what makes it persuasive when you do accept it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check our log yourself
&lt;/h2&gt;

&lt;p&gt;None of this asks for trust in the telling. Both views are public:&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="nv"&gt;$ &lt;/span&gt;curl https://log.traceseal.io/checkpoint
&lt;span class="nv"&gt;$ &lt;/span&gt;curl https://witness.markovianprotocol.com/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'log.traceseal.io'&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/checkpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bodies should match byte for byte, with the witness's cosignature as an extra line on the second. We have published the verifier we run daily — it checks the log's Ed25519 signature, the witness cosignature against the witness's public key, and that live and pinned bodies agree — as &lt;code&gt;lib/tlog_witness.py&lt;/code&gt; in the &lt;a href="https://github.com/Traceseal/alibi" rel="noopener noreferrer"&gt;Alibi repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits in an agent accountability stack
&lt;/h2&gt;

&lt;p&gt;A signed receipt proves an execution record has not changed since it was sealed. &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;An audit trail&lt;/a&gt; tells you what to put in that record. The transparency log proves the receipt existed before anyone had a reason to dispute it. The witness closes the last gap in that chain: it stops the log itself being quietly replaced. Each layer covers a failure the previous one cannot.&lt;/p&gt;

&lt;p&gt;One witness is not the end state; a quorum of independent witnesses is. What ours bought is the step from "trust us" to "trust us, or catch us out through a party we do not control". How many witnesses a receipts log needs before a court or an auditor treats its timestamps as settled is a question nobody has an answer to yet. We would rather be early to finding out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://traceseal.io/blog/transparency-log-witnesses/" rel="noopener noreferrer"&gt;Traceseal blog&lt;/a&gt;. The receipt format, verifier and transparency log are open: &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt;, &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>AI agent audit trails: what to record, and what your records can prove</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 11 Aug 2026 08:51:37 +0000</pubDate>
      <link>https://dev.to/traceseal/ai-agent-audit-trails-what-to-record-and-what-your-records-can-prove-4e0e</link>
      <guid>https://dev.to/traceseal/ai-agent-audit-trails-what-to-record-and-what-your-records-can-prove-4e0e</guid>
      <description>&lt;p&gt;An AI agent does not just answer questions. It calls tools, edits files, opens pull requests, moves money, sends messages. Sooner or later one of those actions gets questioned, by a customer, an auditor, a regulator, or your own incident review, and what you can say at that point depends entirely on what you recorded at the time.&lt;/p&gt;

&lt;p&gt;Building a useful audit trail for an agent is really two problems, and teams usually only notice the first one. &lt;strong&gt;Completeness&lt;/strong&gt;: did you capture the right events? &lt;strong&gt;Credibility&lt;/strong&gt;: can anyone other than you believe the record? This post covers both, including the record-keeping duties the EU AI Act now attaches to high-risk systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an AI agent audit trail should record
&lt;/h2&gt;

&lt;p&gt;Chat transcripts are not an audit trail. The transcript records what the model said; the questions that matter later are about what the system &lt;em&gt;did&lt;/em&gt;. A trail worth keeping records, for every run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Which code and model ran.&lt;/strong&gt; Agent and skill versions, the model identifier, and content hashes of the code that executed, not just a version string someone can retag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The inputs.&lt;/strong&gt; The task or prompt, the configuration, and the policy in force. Store sensitive payloads as SHA-256 digests rather than plaintext: a hash proves the input was what you say it was without retaining the data itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every tool call.&lt;/strong&gt; Name, arguments, result and exit status. Tool calls are where an agent touches the world; a trail that omits them records the commentary and drops the actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What changed.&lt;/strong&gt; Files written, diffs applied, messages sent, records created, again as digests, so the artefact can be checked against the record later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing.&lt;/strong&gt; Start and end of the run and of each step. This is not gold-plating: for biometric systems, &lt;a href="https://artificialintelligenceact.eu/article/12/" rel="noopener noreferrer"&gt;Article 12(3) of the EU AI Act&lt;/a&gt; requires logging "the period of each use of the system (start date and time and end date and time)" explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human involvement.&lt;/strong&gt; Which steps ran under an approval, and who gave it. When something goes wrong, the first question after "what happened" is "who signed it off".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The regulatory floor: EU AI Act record-keeping
&lt;/h2&gt;

&lt;p&gt;If your agent falls in the Act's high-risk category, record-keeping stops being a best practice and becomes a duty with numbers attached. Three articles of &lt;a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj" rel="noopener noreferrer"&gt;Regulation (EU) 2024/1689&lt;/a&gt; do the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://artificialintelligenceact.eu/article/12/" rel="noopener noreferrer"&gt;Article 12&lt;/a&gt;&lt;/strong&gt; requires high-risk AI systems to "technically allow for the automatic recording of events (logs) over the lifetime of the system", covering events relevant to identifying risk, post-market monitoring, and monitoring of operation by deployers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://artificialintelligenceact.eu/article/19/" rel="noopener noreferrer"&gt;Article 19&lt;/a&gt;&lt;/strong&gt; obliges providers to keep those automatically generated logs, to the extent they are under their control, for a period appropriate to the system's purpose and &lt;strong&gt;at least six months&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://artificialintelligenceact.eu/article/26/" rel="noopener noreferrer"&gt;Article 26(6)&lt;/a&gt;&lt;/strong&gt; places the mirror-image duty on deployers: keep the logs your high-risk system generates, again for at least six months, longer where other EU or national law (data protection in particular) requires it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On timing: under &lt;a href="https://artificialintelligenceact.eu/article/113/" rel="noopener noreferrer"&gt;Article 113&lt;/a&gt; the Act applies generally from 2 August 2026, which brings the obligations for high-risk systems listed in Annex III with it; systems that are high-risk because they are safety components of regulated products (Article 6(1)) follow from 2 August 2027.&lt;/p&gt;

&lt;p&gt;Most coding and operations agents are not high-risk systems under the Act. But the transparency duties in &lt;a href="https://traceseal.io/blog/eu-ai-act-article-50-ai-agents/" rel="noopener noreferrer"&gt;Article 50 already apply&lt;/a&gt; to systems that interact with people or generate content, and if you are going to keep records at all, the Act's six-month retention floor is the obvious number to copy rather than invent your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  A complete log can still prove nothing
&lt;/h2&gt;

&lt;p&gt;Suppose you record all of the above faithfully. You now have a trail that is excellent for debugging and incident response — and still worthless the moment someone outside your organisation needs convincing.&lt;/p&gt;

&lt;p&gt;The problem is authorship. Application logs are written by the same system they describe, stored on infrastructure the operator controls, and editable by anyone with write access to the store. When the record's author is the party whose conduct is in question, the record cannot clear them. Security practice has long treated missing or unprotected logs as a top-tier weakness (it is &lt;a href="https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/" rel="noopener noreferrer"&gt;A09 in the OWASP Top 10&lt;/a&gt;), but integrity is a separate property from existence. A log that exists, is complete, and could have been rewritten yesterday answers "what happened?" only for people who already trust you.&lt;/p&gt;

&lt;p&gt;We have written up the full argument in &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;why logs are not evidence&lt;/a&gt;; the short version is that WORM storage, centralised log collectors and hash chains each move the trust boundary without removing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the trail verifiable
&lt;/h2&gt;

&lt;p&gt;The fix is mechanical, and none of it is exotic cryptography:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hash every artefact&lt;/strong&gt; the run touched, so the record commits to specific bytes rather than descriptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serialise the record as canonical JSON&lt;/strong&gt;, so there is exactly one byte sequence a signature can cover.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign it at execution time&lt;/strong&gt; with an ed25519 key held outside the agent's reach, so neither the agent nor a later intruder can rewrite history without breaking the seal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anchor it in a transparency log&lt;/strong&gt;, so the record provably existed before the dispute did.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That turns your audit trail into a receipt anyone can check offline, with no access to your systems:&lt;br&gt;
&lt;/p&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;pip &lt;span class="nb"&gt;install &lt;/span&gt;traceseal-verify
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;traceseal-verify receipt.json
&lt;span class="go"&gt;[OK] receipt.json — operator signature verified
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We walk through &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;what that verification actually proves&lt;/a&gt; in an earlier post, and the honest limits matter here too. A signed receipt proves the record has not changed since it was sealed and who sealed it. It does not prove the record is complete: an action taken outside the instrumented path leaves no receipt at all. That is why receipts pair naturally with &lt;a href="https://traceseal.io/blog/sandbox-an-ai-coding-agent/" rel="noopener noreferrer"&gt;sandboxing the agent&lt;/a&gt; — the sandbox narrows what can happen off the record, and the receipt seals what happened on it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The test worth applying:&lt;/strong&gt; pick one agent action from last month and try to show a third party what happened, without asking them to trust your database. If the exercise ends at "here is a row in our logging system", you have an audit trail. You do not yet have evidence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A checklist you can apply this week
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Record tool calls and artefacts, not just conversation.&lt;/li&gt;
&lt;li&gt;Hash sensitive inputs and outputs instead of hoarding them.&lt;/li&gt;
&lt;li&gt;Retain records for at least six months, the floor set by &lt;a href="https://artificialintelligenceact.eu/article/19/" rel="noopener noreferrer"&gt;Article 19&lt;/a&gt; and &lt;a href="https://artificialintelligenceact.eu/article/26/" rel="noopener noreferrer"&gt;Article 26(6)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Sign records when they are made. A trail you start keeping after the dispute begins says nothing about what came before.&lt;/li&gt;
&lt;li&gt;Rehearse retrieval: if you have never pulled a specific run's record under time pressure, you do not know whether you can.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The receipt format, verifier and transparency log are open — the &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, the &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;verifier on PyPI&lt;/a&gt;, and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;public log&lt;/a&gt;. You can adopt the format without adopting us.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://traceseal.io/blog/ai-agent-audit-trail/" rel="noopener noreferrer"&gt;Traceseal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>compliance</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to sandbox an AI coding agent (and what a sandbox cannot prove)</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Tue, 04 Aug 2026 08:50:37 +0000</pubDate>
      <link>https://dev.to/traceseal/how-to-sandbox-an-ai-coding-agent-and-what-a-sandbox-cannot-prove-h9b</link>
      <guid>https://dev.to/traceseal/how-to-sandbox-an-ai-coding-agent-and-what-a-sandbox-cannot-prove-h9b</guid>
      <description>&lt;p&gt;The question usually arrives after the run rather than before it. An agent has spent forty minutes in a repository, the diff is larger than anyone expected, and somebody asks whether it touched anything outside the working directory. The instinct is to search the transcript. The trouble with that instinct is the one this blog keeps returning to: the transcript is written by the process you are asking about, so a clean transcript is exactly what a misbehaving run produces.&lt;/p&gt;

&lt;p&gt;The durable answer is to make the action impossible rather than to watch for it. This post is a working profile for doing that on Linux, an honest account of where it leaked when we tested it, and the point at which a sandbox stops helping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a sandbox is, underneath the word
&lt;/h2&gt;

&lt;p&gt;"Sandbox" is used loosely enough to mean anything from a Docker container to a permissions prompt. On Linux it has a specific meaning, assembled from three kernel facilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namespaces&lt;/strong&gt; give a process a private view of a global resource: its own mount table, process tree, or network stack (&lt;a href="https://man7.org/linux/man-pages/man7/namespaces.7.html" rel="noopener noreferrer"&gt;namespaces(7)&lt;/a&gt;). &lt;a href="https://man7.org/linux/man-pages/man7/user_namespaces.7.html" rel="noopener noreferrer"&gt;User namespaces&lt;/a&gt; are the piece that matters for developer tooling, because they let an ordinary account create the others without root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seccomp&lt;/strong&gt; filters system calls, so a process can be denied whole classes of kernel interface rather than denied a file (&lt;a href="https://man7.org/linux/man-pages/man2/seccomp.2.html" rel="noopener noreferrer"&gt;seccomp(2)&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Landlock&lt;/strong&gt; lets an unprivileged process restrict its own filesystem access and never get it back, which is the right shape for a wrapper that launches something it does not trust (&lt;a href="https://docs.kernel.org/userspace-api/landlock.html" rel="noopener noreferrer"&gt;kernel documentation&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/containers/bubblewrap" rel="noopener noreferrer"&gt;Bubblewrap&lt;/a&gt; is a small setuid-or-userns tool that composes the first two into a single command. It is what Flatpak uses underneath, it needs no daemon, and it starts in milliseconds, which matters when the thing you are wrapping is invoked hundreds of times a day. Everything below was run against bubblewrap 0.11.0 on Debian.&lt;/p&gt;

&lt;h2&gt;
  
  
  A profile that actually runs
&lt;/h2&gt;

&lt;p&gt;The shape you want for a coding agent is narrow: it may read the system, it may write to one directory, and it may not reach the network.&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="nv"&gt;$ &lt;/span&gt;bwrap &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--ro-bind&lt;/span&gt; / / &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bind&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--dev&lt;/span&gt; /dev &lt;span class="nt"&gt;--proc&lt;/span&gt; /proc &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--unshare-net&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--unshare-pid&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--die-with-parent&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--chdir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--&lt;/span&gt; your-agent-cli run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line by line: &lt;code&gt;--ro-bind / /&lt;/code&gt; mounts the entire host filesystem read-only, so the agent keeps the toolchain, libraries and language runtimes it needs and can modify none of them. &lt;code&gt;--bind "$PWD" "$PWD"&lt;/code&gt; punches one writable hole at the working directory. &lt;code&gt;--unshare-net&lt;/code&gt; puts the process in a fresh network namespace with no interface but loopback. &lt;code&gt;--unshare-pid&lt;/code&gt; stops it seeing or signalling other processes on the box, and &lt;code&gt;--die-with-parent&lt;/code&gt; means an orphaned agent is killed rather than left running.&lt;/p&gt;

&lt;p&gt;Two classes of action stop being things you monitor and become things that cannot happen. A write outside the working directory fails at the kernel:&lt;br&gt;
&lt;/p&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;&lt;span class="nb"&gt;touch&lt;/span&gt; /home/tim/SHOULD_NOT_EXIST
&lt;span class="go"&gt;touch: cannot touch '/home/tim/SHOULD_NOT_EXIST': Read-only file system
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there is no route off the machine, so a connection to a raw address fails to establish. That is a better guarantee than any amount of transcript review, because it does not depend on the agent's cooperation, its honesty, or your diligence in reading the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it leaked
&lt;/h2&gt;

&lt;p&gt;Testing the profile above produced a result we did not expect. Egress was properly gone: &lt;code&gt;ip route&lt;/code&gt; was empty inside the namespace and &lt;code&gt;curl&lt;/code&gt; to a bare IP address failed to connect. But name resolution still worked.&lt;br&gt;
&lt;/p&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;getent hosts example.com
&lt;span class="go"&gt;2606:4700:10::6814:179a  example.com
2606:4700:10::ac42:93f3  example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The explanation is in &lt;code&gt;/etc/nsswitch.conf&lt;/code&gt;. Debian's hosts line includes the &lt;code&gt;resolve&lt;/code&gt; module, which does not send a DNS packet at all: it talks to systemd-resolved over a Unix socket at &lt;code&gt;/run/systemd/resolve/io.systemd.Resolve&lt;/code&gt;. That socket arrived inside the sandbox as an ordinary file, carried in by the read-only bind of &lt;code&gt;/&lt;/code&gt;. Masking the directory confirms it, and the resolution stops:&lt;br&gt;
&lt;/p&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;bwrap ... &lt;span class="nt"&gt;--unshare-net&lt;/span&gt; &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /run/systemd/resolve &lt;span class="nt"&gt;--&lt;/span&gt; getent hosts example.com
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c"&gt;# no output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The general lesson is worth more than the specific fix. A network namespace governs interfaces, routes and sockets in the network stack. It has nothing to say about a Unix socket to a host daemon that still has full network access, and a filesystem bind will hand you one of those without mentioning it. Anyone auditing an agent sandbox should assume the same is true of the container runtime socket, the SSH agent socket, and the D-Bus session bus.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Worth stating plainly: we wrote the confident version of the paragraph above first, then ran it, and the run disagreed. A sandbox profile that has not been driven end to end is a hypothesis about isolation, not a control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Three further limits are structural rather than fixable. The working directory is writable by design, and for most repositories that is where the interesting secrets live: &lt;code&gt;.env&lt;/code&gt; files, and &lt;code&gt;.git/config&lt;/code&gt; remotes with an access token embedded in the URL. Environment variables cross the boundary untouched unless you add &lt;code&gt;--clearenv&lt;/code&gt;, so an API key exported in the parent shell is available inside. And the kernel is shared, which is the standing caveat on all container-style isolation and the reason &lt;a href="https://csrc.nist.gov/pubs/sp/800/190/final" rel="noopener noreferrer"&gt;NIST SP 800-190&lt;/a&gt; treats it as weaker than a virtual machine boundary. Docker's &lt;a href="https://docs.docker.com/engine/network/drivers/none/" rel="noopener noreferrer"&gt;&lt;code&gt;none&lt;/code&gt; network driver&lt;/a&gt; is the same idea with the same caveats, if a container is already in your stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part nobody budgets for
&lt;/h2&gt;

&lt;p&gt;Isolation changes what the agent does, not only what it is able to do, and this is a genuine operational cost rather than a footnote. An agent that cannot reach the network will still try to install a package. When that fails, it improvises: it writes a stub, or vendors something from a cache, or quietly adjusts a test so the missing dependency stops mattering. The run does not stop; it goes sideways.&lt;/p&gt;

&lt;p&gt;The second effect is on diagnosis. A run that failed because the sandbox blocked it and a run that failed because the task was wrong produce output that looks much the same, and the agent's own explanation of which one happened is not evidence either. Teams adopting sandboxing tend to lose a week to this before they start logging the policy denials separately from the task failures. It is worth doing on day one. This is also why &lt;a href="https://traceseal.io/blog/alibi-grade-agents-on-ground-truth/" rel="noopener noreferrer"&gt;grading agents against ground truth on disk&lt;/a&gt; beats grading them on what they said they did.&lt;/p&gt;

&lt;h2&gt;
  
  
  From control to evidence
&lt;/h2&gt;

&lt;p&gt;Here is the limit of everything above. A sandbox constrains; it does not communicate. You now know the agent could not write outside the repository. A customer, an auditor or a regulator asking the same question next quarter still has nothing but your description of a profile you say you applied.&lt;/p&gt;

&lt;p&gt;Closing that gap is what an execution receipt is for. The &lt;a href="https://github.com/traceseal/traceseal-verify/blob/main/RECEIPT-SPEC.md" rel="noopener noreferrer"&gt;receipt specification&lt;/a&gt; carries a &lt;code&gt;sandbox_profile_hash&lt;/code&gt; field: a SHA-256 hash of the sandbox configuration, meaning the bubblewrap argument list plus the environment. A verifier holding a known-good profile can hash it and check whether the run used that profile or a different one, offline, with &lt;a href="https://pypi.org/project/traceseal-verify/" rel="noopener noreferrer"&gt;one command&lt;/a&gt; and no access to your systems. Combined with the signature over the whole record, an altered profile cannot be presented as the original. For what the signature covers and what an &lt;code&gt;[OK]&lt;/code&gt; means, see &lt;a href="https://traceseal.io/blog/verify-what-an-ai-agent-actually-did/" rel="noopener noreferrer"&gt;the walkthrough&lt;/a&gt; and &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;the primer on why logs are not evidence&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What that does not do is more interesting, and the specification says so itself rather than leaving a reader to discover it. Among the things a valid receipt does &lt;em&gt;not&lt;/em&gt; prove:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That the sandbox actually enforced the declared profile (the receipt records what the operator &lt;em&gt;claims&lt;/em&gt; the sandbox was; a compromised operator could lie).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Section 6.1 of the same document puts it more bluntly still: the receipt is "an &lt;em&gt;attestation&lt;/em&gt;, not a &lt;em&gt;proof of execution&lt;/em&gt; in the zero-knowledge sense". An operator who never ran bubblewrap at all can sign a receipt naming a profile hash they took from the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still open
&lt;/h2&gt;

&lt;p&gt;That last gap does not close by thinking harder about receipt design, and we are not going to pretend a signature solves it. It closes only by moving the seal outside the party being trusted, and each route out has a real cost. A second party can run or observe the sandbox and cosign the record, which works and requires someone willing to be that party. Hardware-rooted attestation can bind the measurement to a chip rather than an assertion, at the price of a much heavier deployment. A third-party execution environment removes the operator's discretion entirely, along with the operator's control of their own infrastructure.&lt;/p&gt;

&lt;p&gt;Which of those becomes normal is going to be settled by procurement departments and insurers rather than by cryptographers, and probably not this year. In the meantime the honest position is that a signed profile hash moves an unverifiable claim to a checkable one and stops short of proof, which is still a considerable distance from a log file nobody can check at all.&lt;/p&gt;

&lt;p&gt;The regulatory direction is at least consistent with the effort. &lt;a href="https://artificialintelligenceact.eu/article/15/" rel="noopener noreferrer"&gt;Article 15&lt;/a&gt; of &lt;a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj" rel="noopener noreferrer"&gt;Regulation (EU) 2024/1689&lt;/a&gt; requires high-risk systems to be designed for an appropriate level of accuracy, robustness and cybersecurity across their lifecycle. A demonstrable containment boundary is easier to argue than a policy document describing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions to ask of your own setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Has the profile been run, or only written?&lt;/strong&gt; Try to write outside the workdir and try to reach the network from inside it. Ours resolved DNS through a socket we had not thought about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What sockets came in with the filesystem?&lt;/strong&gt; Enumerate what is under &lt;code&gt;/run&lt;/code&gt; and &lt;code&gt;/var/run&lt;/code&gt; in the mount. Each one is a channel your network namespace does not cover.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is in the environment?&lt;/strong&gt; If you have not used &lt;code&gt;--clearenv&lt;/code&gt;, every secret in the parent shell is inside the sandbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are policy denials logged separately from task failures?&lt;/strong&gt; If not, you cannot tell a blocked agent from a confused one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can anyone outside your team check which profile ran?&lt;/strong&gt; If the answer is that you would tell them, that is a description, not a record.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The receipt format, the verifier and the &lt;a href="https://log.traceseal.io" rel="noopener noreferrer"&gt;transparency log&lt;/a&gt; are open. You can adopt the format without adopting us.&lt;/p&gt;

</description>
      <category>security</category>
      <category>linux</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>Alibi: grade coding agents on what actually happened on disk</title>
      <dc:creator>Ventse</dc:creator>
      <pubDate>Thu, 30 Jul 2026 08:36:27 +0000</pubDate>
      <link>https://dev.to/traceseal/alibi-grade-coding-agents-on-what-actually-happened-on-disk-4lan</link>
      <guid>https://dev.to/traceseal/alibi-grade-coding-agents-on-what-actually-happened-on-disk-4lan</guid>
      <description>&lt;p&gt;Ask a coding agent whether it finished the job and it will usually say yes. The closing summary may be fluent, itemised and confident. None of that proves what happened.&lt;/p&gt;

&lt;p&gt;We have written before about why &lt;a href="https://traceseal.io/blog/signed-execution-receipts-primer/" rel="noopener noreferrer"&gt;logs are not evidence&lt;/a&gt;: a record produced by the system it describes inherits that system's honesty. An agent's account of its own run has the same defect one layer up.&lt;/p&gt;

&lt;p&gt;So we built &lt;a href="https://github.com/Traceseal/alibi" rel="noopener noreferrer"&gt;Alibi&lt;/a&gt;, an MIT-licensed harness that ignores the account entirely. Alibi runs the same missions through agentic CLIs and grades each run against ground truth on disk: the files that exist and the changes that were made. It never grades what the agent says it did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The grading rule
&lt;/h2&gt;

&lt;p&gt;Each configured CLI, currently Claude Code and OpenAI Codex, receives an identical mission brief inside its own &lt;a href="https://github.com/containers/bubblewrap" rel="noopener noreferrer"&gt;bubblewrap sandbox&lt;/a&gt;. Each lane gets a private working tree, an isolated home and an isolated &lt;code&gt;/tmp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the process exits, Alibi inspects the tree:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the requested file exist?&lt;/li&gt;
&lt;li&gt;Does it contain what the mission required?&lt;/li&gt;
&lt;li&gt;Did the specified tests pass?&lt;/li&gt;
&lt;li&gt;Did anything change outside the permitted area?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent's exit message is not consulted. A mission passes if the disk says it passed.&lt;/p&gt;

&lt;p&gt;The sandbox also makes containment measurable. A write outside the allowed tree is a recorded violation, not a judgement about style. Alibi retains the working directory, sandbox home, temporary files and logs after the run, so someone can derive the grade again and dispute it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Alibi refuses to claim
&lt;/h2&gt;

&lt;p&gt;Alibi is not a leaderboard. The mission set is small and the runs happened on our machines. A handful of missions is a probe, not a census, so we are not publishing rankings from it.&lt;/p&gt;

&lt;p&gt;It also says nothing about consent behaviour. Lanes run with approvals granted by design because the object of study is execution, not permission seeking. How a CLI behaves when it has to ask first is a separate question.&lt;/p&gt;

&lt;p&gt;The grades remain Alibi's judgement. Every assertion is ordinary code in the mission definition. You can read a check, run it again and dispute it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The grader cannot be trusted either
&lt;/h2&gt;

&lt;p&gt;A grading harness has the same structural problem as the agents it grades: it produces the record of its own run. If our scores mattered to anyone, we could quietly edit them.&lt;/p&gt;

&lt;p&gt;Alibi now supports signed Traceseal receipts. Point one environment variable at an Ed25519 key and each CLI and mission result is sealed into a receipt containing hashes of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the mission definition&lt;/li&gt;
&lt;li&gt;the result record&lt;/li&gt;
&lt;li&gt;the retained evidence tree&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A run-level receipt chains those results into one verifiable unit. Signing happens on the host after the contained processes exit, and the key is never mounted inside the sandbox.&lt;/p&gt;

&lt;p&gt;A second party can act as a witness. The witness tool derives every hash again from the retained evidence, checks each signature and the chain, and cosigns only if everything matches. It refuses to cosign with the original signer's key.&lt;/p&gt;

&lt;p&gt;There is an important limit. Two keys on one machine prove key separation, not operator separation. Full independence requires the witness to verify on hardware the original signer cannot write to.&lt;/p&gt;

&lt;p&gt;The signature means "this run occurred as described." The scores remain Alibi's judgement. Signing makes them tamper-evident, not true.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to ask of any agent benchmark
&lt;/h2&gt;

&lt;p&gt;First, what record was it graded against? If the answer is the agent's final message, the benchmark measured prose.&lt;/p&gt;

&lt;p&gt;Second, could the operator rewrite the results afterwards? A results table in a repository the grader controls is still a log.&lt;/p&gt;

&lt;p&gt;Third, can you run it yourself? The missions, assertions and harness should be readable and executable without the author's cooperation.&lt;/p&gt;

&lt;p&gt;Finally, what was the consent model? Auto-approved runs measure capability. They do not measure conduct, and a result that blurs the two flatters everyone.&lt;/p&gt;

&lt;p&gt;Alibi is our attempt to answer those questions cleanly for our own testing. Whether its missions generalise beyond the behaviours we care about remains open. The repository is small enough to read in an afternoon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Traceseal/alibi" rel="noopener noreferrer"&gt;Read the missions, dispute the assertions and grade your own agent on GitHub.&lt;/a&gt;&lt;/p&gt;

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