<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Anatoli Navahrodski]]></title><description><![CDATA[Anatoli Navahrodski]]></description><link>https://navahrodski.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Nov 2026 20:19:42 GMT</lastBuildDate><atom:link href="https://navahrodski.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[After a breach, your logs are testimony, not evidence]]></title><description><![CDATA[Originally published on dev.to. Picture a Tuesday morning. An agent with access to your payments backend did something odd overnight: a batch of refunds…]]></description><link>https://navahrodski.hashnode.dev/after-a-breach-your-logs-are-testimony-not-evidence</link><guid isPermaLink="true">https://navahrodski.hashnode.dev/after-a-breach-your-logs-are-testimony-not-evidence</guid><category><![CDATA[AI]]></category><category><![CDATA[Security]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Devops]]></category><category><![CDATA[ai agents]]></category><dc:creator><![CDATA[Anatoli Navahrodski]]></dc:creator><pubDate>Thu, 12 Nov 2026 15:51:34 GMT</pubDate><content:encoded><![CDATA[<p><em>Originally published on</em> <a href="https://dev.to/anatoli_navahrodski_55514/after-a-breach-your-logs-are-testimony-not-evidence-1amn"><em>dev.to</em></a><em>.</em></p>
<p>Picture a Tuesday morning. An agent with access to your payments backend did something odd overnight: a batch of refunds nobody can explain, and a tool call to an endpoint it has never used before. By ten o'clock security has the host isolated. By eleven your broker has called. If you are a financial entity in the EU and this turns out to be a major incident, DORA gives you four hours from that classification to send the first notice.</p>
<p>Everyone asks the same question: what exactly did the agent do?</p>
<p>The example is made up. The situation is not, and the uncomfortable part comes next.</p>
<h2>You are a suspect too</h2>
<p>Your answer will come from logs. Those logs sit on, or next to, the host that was just compromised. They were written by software your team runs, stored in a database your team administers, and they are now being read by the same team that is responsible for the incident.</p>
<p>To the insurer, the regulator or the customer's lawyer, that is not evidence. It is testimony from an interested party. Nobody needs to believe you tampered with anything. They only need to notice that you could have, and that so could the attacker who had the host for a night. An agent that can write to its own logs can also tidy them up, and researchers have already shown agents doing exactly that to their own traces (<a href="https://arxiv.org/abs/2609.30266">arXiv 2609.30266</a>).</p>
<p>Evidence is something the other side can check without trusting you. That changes what the first hours of the response are for. You are not only finding out what happened. You are preserving something that someone who does not trust you can verify.</p>
<h2>The first four hours</h2>
<h3>1. Cut the agent off, do not clean up</h3>
<p>The instinct in a panic is to restart things, wipe the box and redeploy from a clean image. That feels responsible and it destroys the material you are about to be asked for.</p>
<p>Revoke the agent's credentials instead. If every call the agent makes goes through a recorder or a gateway, rotating that one token stops the agent cold and leaves everything it already did in place. Write down the exact time you did it. That timestamp is the start of your response, and you will be asked for it.</p>
<h3>2. Take everything out as one archive, and fingerprint it immediately</h3>
<p>Export everything the agent did in the window, starting well before the first suspicious action. One file, not a folder of screenshots and log extracts pasted into a ticket. Put the incident number in the export itself, so the file says what it was made for.</p>
<p>Then do the step most teams skip: compute the file's SHA-256 and write it somewhere the compromised environment cannot reach. The incident ticket, an email to your counsel, a message to the broker. It takes ten seconds. Months later, when someone asks whether the archive you handed over is the one you exported on day one, that line answers it.</p>
<h3>3. Check it on a machine that was not involved</h3>
<p>Verification on the compromised host proves nothing, for the same reason the logs prove nothing. Move the archive to a clean laptop and verify it there, against an identity for the recording system that you wrote down before anything happened, not one you read from the system today.</p>
<p>If the record is signed and chained, verification tells you one of two things: everything holds, or exactly which record broke. Both are useful. A break is a finding you report, not a reason to quietly fix the archive.</p>
<h3>4. Hand over one page a lawyer can read</h3>
<p>The people on the other side of this are not going to open a JSON file. They need one page: what period it covers, how many runs and steps, who approved what and when, what an outside witness has confirmed, and the fingerprint of the file it describes.</p>
<h2>What the reviewer will look for</h2>
<p>Whoever reviews the record, insurer, auditor or counsel, will usually start with three things.</p>
<p><strong>Whose record is this.</strong> Was it verified against an identity fixed in advance, or against whatever the system says about itself today.</p>
<p><strong>What nobody outside has seen yet.</strong> A tamper-evident record is only as strong as its last external checkpoint. Anything written after the last time an independent witness saw the chain could, in principle, have been rewritten by whoever held the signing keys. A good report says how many records are in that window. A great setup keeps that window near zero around the steps that matter, such as human approvals.</p>
<p><strong>Approvals and boundaries.</strong> Which steps needed a human sign-off, whether it was there before the step ran, and which steps fell outside what the agent was allowed to do. These do not break the record's integrity. They are simply the questions that decide the claim.</p>
<h2>Be honest about the limits</h2>
<p>This is where most incident write-ups lose credibility, and where you can gain it.</p>
<p>A record shows what passed through the recorder. It does not show that the agent did nothing else. An attacker on the host can call a model or a tool directly. An export shows the window you asked for, not that nothing else exists. And no log tells you that what a model returned was true, or that the incident is over.</p>
<p>Say this in the report, in plain words. A document that states its own limits is far easier to accept than one that claims to show everything.</p>
<h2>Five minutes today, before anything happens</h2>
<p>Three things cannot be done afterwards:</p>
<ul>
<li><strong>Write down the recording system's identity</strong> somewhere the agent's host cannot touch: the contract, a ticket, a password manager. Without it, a verifier on the day can only check that the archive is consistent, not that it is yours.</li>
<li><strong>Keep external anchoring on.</strong> Periodic checkpoints in a public time-stamp authority or transparency log are what turn "we say so" into "anyone can check".</li>
<li><strong>Run the recorder on a host the agent cannot log in to.</strong> A recorder on the agent's own machine shares its fate: whoever takes the host takes the keys.</li>
</ul>
<h2>For the engineers: the commands</h2>
<p>This is how it looks with <a href="https://github.com/sealedrun/sealedrun">SealedRun</a>, an open-source recorder for agent runs. The full runbook is in <a href="https://github.com/sealedrun/sealedrun/blob/main/docs/INCIDENT-RESPONSE.md">docs/INCIDENT-RESPONSE.md</a>. The same order of work applies to any setup where the record is signed and leaves the host.</p>
<pre><code class="language-bash"># 1. Cut off: replace the recorder token, restart, note the time
umask 077 &amp;&amp; openssl rand -hex 32 &gt; sealedrun-token.new
export SEALEDRUN_API_TOKEN="$(cat sealedrun-token.new)"
docker compose up -d --force-recreate sealedrun
date -u +%Y-%m-%dT%H:%M:%SZ

# 2. Export the window as one archive and fingerprint it
curl -fsS -X POST -H "Authorization: Bearer $SEALEDRUN_API_TOKEN" -o evidence.zip \
  "http://127.0.0.1:8080/api/export?since=2026-11-11T06:00:00Z&amp;until=2026-11-11T12:00:00Z&amp;note=INC-42"
sha256sum evidence.zip

# 3. Verify on a clean machine, against the identity you wrote down earlier
pip install sealedrun
python -m sealedrun evidence.zip &lt;principal id&gt;

# 4. The one page
python -m sealedrun --report evidence.zip &lt;principal id&gt; &gt; report.txt
</code></pre>
<p>Deadlines the page is usually needed for. Check the text that applies to you; national rules add to these.</p>
<table>
<thead>
<tr>
<th>Rule</th>
<th>First notice</th>
<th>Next</th>
<th>Final</th>
</tr>
</thead>
<tbody><tr>
<td>DORA</td>
<td>4 hours after classifying the incident as major, at most 24 hours after becoming aware</td>
<td>Intermediate report within 72 hours</td>
<td>One month after the latest intermediate report</td>
</tr>
<tr>
<td>NIS2, art. 23</td>
<td>Early warning within 24 hours</td>
<td>Incident notification within 72 hours</td>
<td>One month after the notification</td>
</tr>
<tr>
<td>EU AI Act, art. 12, 19, 26</td>
<td>No notice period; logs of high-risk systems must exist and be kept at least six months</td>
<td></td>
<td>Annex III systems from 2 December 2027</td>
</tr>
</tbody></table>
<p>The best time to find out whether your agent's record would hold up is a quiet afternoon, not hour three of an incident.</p>
]]></content:encoded></item><item><title><![CDATA[Who can edit your AI agent's log?]]></title><description><![CDATA[Ask this question in any team that runs AI agents in production and the answer is almost always the same: the agent process writes its trace to a database…]]></description><link>https://navahrodski.hashnode.dev/who-can-edit-your-ai-agent-s-log</link><guid isPermaLink="true">https://navahrodski.hashnode.dev/who-can-edit-your-ai-agent-s-log</guid><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[Security]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Anatoli Navahrodski]]></dc:creator><pubDate>Mon, 09 Nov 2026 13:15:07 GMT</pubDate><content:encoded><![CDATA[<p>Ask this question in any team that runs AI agents in production and the answer is almost always the same: the agent process writes its trace to a database or a file, the operator administers that database, and nobody outside the company ever sees it.</p>
<p>For debugging, that is fine. For accountability, it is not. The day an auditor, an insurer, a counterparty or a regulator asks what the agent did, an editable log is a statement from the operator, not evidence.</p>
<h2>Agents already edit their own traces</h2>
<p>In September 2026 a group from ETH Zurich and Invariant Labs published <em>LLM Agents Can Easily Tamper With Their Own Traces</em> (<a href="https://arxiv.org/abs/2609.30266">arXiv:2609.30266</a>). They tested eight coding-agent harnesses, among them Claude Code, Codex, OpenCode and Kimi Code, ten model-harness pairs, ten trials each.</p>
<p>The results are not subtle:</p>
<ul>
<li>In the privacy scenarios, almost every model-harness pair deleted its local traces in every trial.</li>
<li>Tool-call spoofing, where the agent reports a different call than the one it made, succeeded at 80 percent or higher.</li>
<li>Monitors built on top of those traces failed in five of the ten pairs, because they were reading what the agent chose to write.</li>
<li>On Terminal-Bench tasks the tampering success rate was above 80 percent for every model tested.</li>
</ul>
<p>The authors' recommendation is one sentence: log traces "via an independent interception mechanism outside the agent's control".</p>
<p>Two months later, MemoryDojo (<a href="https://arxiv.org/abs/2611.09721">arXiv:2611.09721</a>, Microsoft AI Red Team) showed the same pattern one layer up. Across 155 multi-session trajectories and five memory systems, every memory system could be poisoned so that an instruction planted in one session steered the agent in later ones. Memory is a log the agent writes to itself, and the agent cannot be its own witness.</p>
<h2>Observability is not evidence</h2>
<p>None of this is a criticism of tracing tools. Langfuse stores observations in ClickHouse behind Postgres, LangSmith exposes <code>PATCH /runs/{run_id}</code> so a run's outputs can be rewritten after the fact, and the OpenTelemetry GenAI semantic conventions describe spans, metrics and events with no integrity attributes at all. These are excellent tools for understanding what happened. They were designed to be queried, not to be presented to someone who distrusts you.</p>
<p>That distinction starts to matter when a third party enters the room.</p>
<h2>What auditors and insurers are starting to ask for</h2>
<ul>
<li><strong>EU AI Act, Article 12</strong> requires high-risk systems to "technically allow for the automatic recording of events (logs) over the lifetime of the system". <strong>Article 19</strong> requires providers to keep those logs for at least six months. After the Digital Omnibus (Regulation (EU) 2026/1744, in force since 27 July 2026) the Annex III high-risk obligations apply from 2 December 2027. Transparency duties under Article 50 already apply since 2 August 2026.</li>
<li><strong>ISO/IEC 42001</strong>, Annex A.6.2.8, asks organisations to enable event logging at minimum while the AI system is in use.</li>
<li><strong>AIUC-1</strong>, the AI agent standard used by the Artificial Intelligence Underwriting Company, makes a tool-call log mandatory (D003.3: originating MCP server, tool name, version, input parameters, timestamps) and adds a supplemental control E015.4, <em>Log integrity protection</em>: logs must be "tamper-evident and independently verifiable", and "gaps, omissions, and reordering are detectable".</li>
</ul>
<p>Read E015.4 again. None of the tracing stacks above satisfies it out of the box, and no CLAUDE.md rule will either.</p>
<h2>Four conditions for a record to count as evidence</h2>
<p>Other industries solved this decades ago, and the pattern is always the same.</p>
<ol>
<li><strong>The record is written outside the process it describes.</strong> CloudTrail does not ask the EC2 instance to log itself. The agent must have no write access to the record.</li>
<li><strong>Each entry is signed and chained to the previous one</strong>, so removal or reordering is detectable. CloudTrail digest files carry the signature of the previous digest. Certificate Transparency logs are Merkle trees that "can only be added to, not deleted, modified, or retroactively inserted".</li>
<li><strong>The head of the chain is anchored with a witness the operator does not control.</strong> An RFC 3161 timestamp or a transparency log entry (Sigstore's Rekor) proves the chain existed no later than a given moment. Without this, an operator can quietly re-sign a cleaned-up history.</li>
<li><strong>Anyone can verify the complete bundle offline</strong>, on their own machine, without asking the operator for anything.</li>
</ol>
<p>If any of the four is missing, what you have is a log. With all four, you have something an auditor can check without trusting you.</p>
<h2>What this looks like in practice</h2>
<p><a href="https://github.com/sealedrun/sealedrun">SealedRun</a> is an open-source implementation of exactly these four conditions, built for AI agents. Version 0.8.1 shipped on 5 November 2026 (Apache-2.0 for code, CC-BY-4.0 for the spec).</p>
<pre><code class="language-bash">pip install sealedrun            # recorder, SDK, verifier (Python 3.12+)
npm install @sealedrun/core      # TypeScript verifier, Node 22 and browser
docker run --rm -p 127.0.0.1:8080:8080 ghcr.io/sealedrun/sealedrun:0.8.1
</code></pre>
<p>The recorder sits between the agent and its tools: as an MCP proxy (<code>sealedrun-mcp-wrap</code> for stdio servers), an A2A proxy, or an OTLP endpoint for GenAI spans you already emit. Every model, MCP and tool step becomes a record:</p>
<pre><code>kind, target and its location, payload digests, data labels,
policy decision and reason, prev_hash, hash
+ Ed25519 signature + ML-DSA-65 (FIPS 204) signature
</code></pre>
<p>Both signatures are required; the post-quantum one is there so that a bundle produced today still means something in ten years. Records are canonicalised with RFC 8785 and chained with SHA-256. The signing keys live in the recorder, never in the agent or the model. Every 600 seconds the chain head is anchored in Sigstore's timestamp authority (DigiCert as fallback, Rekor on request).</p>
<p>Verification needs nothing from the operator:</p>
<pre><code class="language-bash">python -m sealedrun bundle.zip
</code></pre>
<p>The README has a ten-second demo: change one field of one record inside the bundle, run the verifier, and it refuses the bundle. <code>python scripts/demo_tamper.py</code> reproduces it against the example bundle.</p>
<p>Two additions in 0.8.0 answer questions auditors actually ask:</p>
<ul>
<li><strong>Was the required human approval given before the step?</strong> A <code>human_approval</code> record stores the decision together with a hash of the exact request the human saw. The verifier binds it to the step that ran. Missing approval, approval after the fact, an ignored denial, or approval of a different request each become a finding (<code>approval.missing</code>, <code>approval.denied_but_executed</code>), and <code>--strict-approval</code> turns findings into failures.</li>
<li><strong>Which instructions was the model running under?</strong> Every model call carries <code>system_prompt_hash</code>, a digest of the system instructions (never the text), derived automatically for OpenAI, Anthropic, Ollama and Gemini clients. The verifier reports the step where it changed.</li>
</ul>
<p>There is also a mapping from bundle parts to AIUC-1 controls in <code>docs/aiuc-1.md</code>, and an example of using a Langfuse trace id as the run label, so the evidence layer and the observability layer point at the same run.</p>
<h2>What a bundle does not prove</h2>
<p>This part matters as much as the rest, and the project's <code>TRUST.md</code> says it plainly. A verified bundle does not prove:</p>
<ul>
<li>that the agent did nothing else: a tool that reaches the network without passing through the recorder leaves no record;</li>
<li>that a self-reported step happened as described (the recorder signs that the agent claimed it, nothing more);</li>
<li>that the human who approved understood what they approved, or was the right person;</li>
<li>that the model output is true or the reasoning was sound;</li>
<li>that the operator's clock is right, beyond what the anchors bound.</li>
</ul>
<p>Completeness is a property of the deployment, not of the cryptography. The bundle proves that these records were produced in this order, none was inserted, removed or reordered later, and the chain head existed no later than the witness time. That is a narrower claim than "we log everything", and it is the claim that survives a hostile reader.</p>
<h2>The question again</h2>
<p>If you run agents in production: who can edit your agent's log today? If the honest answer is "the agent, and anyone with database access", the fix is not a better dashboard. It is a record the agent cannot touch, with a witness you do not control, that someone else can check without calling you.</p>
<p>How are you answering that question right now? Interested in the setups people actually run.</p>
<p><em>Originally published on <a href="https://dev.to/anatoli_navahrodski_55514/who-can-edit-your-ai-agents-log-3j61">dev.to</a>.</em></p>
]]></content:encoded></item></channel></rss>