DEV Community

Anatoli Navahrodski
Anatoli Navahrodski

Posted on

Who can edit your AI agent's log?

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.

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.

Agents already edit their own traces

In September 2026 a group from ETH Zurich and Invariant Labs published LLM Agents Can Easily Tamper With Their Own Traces (arXiv:2609.30266). They tested eight coding-agent harnesses, among them Claude Code, Codex, OpenCode and Kimi Code, ten model-harness pairs, ten trials each.

The results are not subtle:

  • In the privacy scenarios, almost every model-harness pair deleted its local traces in every trial.
  • Tool-call spoofing, where the agent reports a different call than the one it made, succeeded at 80 percent or higher.
  • Monitors built on top of those traces failed in five of the ten pairs, because they were reading what the agent chose to write.
  • On Terminal-Bench tasks the tampering success rate was above 80 percent for every model tested.

The authors' recommendation is one sentence: log traces "via an independent interception mechanism outside the agent's control".

Two months later, MemoryDojo (arXiv:2611.09721, 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.

Observability is not evidence

None of this is a criticism of tracing tools. Langfuse stores observations in ClickHouse behind Postgres, LangSmith exposes PATCH /runs/{run_id} 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.

That distinction starts to matter when a third party enters the room.

What auditors and insurers are starting to ask for

  • EU AI Act, Article 12 requires high-risk systems to "technically allow for the automatic recording of events (logs) over the lifetime of the system". Article 19 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.
  • ISO/IEC 42001, Annex A.6.2.8, asks organisations to enable event logging at minimum while the AI system is in use.
  • AIUC-1, 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, Log integrity protection: logs must be "tamper-evident and independently verifiable", and "gaps, omissions, and reordering are detectable".

Read E015.4 again. None of the tracing stacks above satisfies it out of the box, and no CLAUDE.md rule will either.

Four conditions for a record to count as evidence

Other industries solved this decades ago, and the pattern is always the same.

  1. The record is written outside the process it describes. CloudTrail does not ask the EC2 instance to log itself. The agent must have no write access to the record.
  2. Each entry is signed and chained to the previous one, 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".
  3. The head of the chain is anchored with a witness the operator does not control. 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.
  4. Anyone can verify the complete bundle offline, on their own machine, without asking the operator for anything.

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.

What this looks like in practice

SealedRun 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).

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
Enter fullscreen mode Exit fullscreen mode

The recorder sits between the agent and its tools: as an MCP proxy (sealedrun-mcp-wrap 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:

kind, target and its location, payload digests, data labels,
policy decision and reason, prev_hash, hash
+ Ed25519 signature + ML-DSA-65 (FIPS 204) signature
Enter fullscreen mode Exit fullscreen mode

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).

Verification needs nothing from the operator:

python -m sealedrun bundle.zip
Enter fullscreen mode Exit fullscreen mode

The README has a ten-second demo: change one field of one record inside the bundle, run the verifier, and it refuses the bundle. python scripts/demo_tamper.py reproduces it against the example bundle.

Two additions in 0.8.0 answer questions auditors actually ask:

  • Was the required human approval given before the step? A human_approval 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 (approval.missing, approval.denied_but_executed), and --strict-approval turns findings into failures.
  • Which instructions was the model running under? Every model call carries system_prompt_hash, 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.

There is also a mapping from bundle parts to AIUC-1 controls in docs/aiuc-1.md, 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.

What a bundle does not prove

This part matters as much as the rest, and the project's TRUST.md says it plainly. A verified bundle does not prove:

  • that the agent did nothing else: a tool that reaches the network without passing through the recorder leaves no record;
  • that a self-reported step happened as described (the recorder signs that the agent claimed it, nothing more);
  • that the human who approved understood what they approved, or was the right person;
  • that the model output is true or the reasoning was sound;
  • that the operator's clock is right, beyond what the anchors bound.

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.

The question again

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.

How are you answering that question right now? Interested in the setups people actually run.

Top comments (2)

Collapse
 
techdevfun profile image
TECHDEV •

The 600-second anchor interval is the part I'd want to see treated as a tunable trust parameter rather than a fixed number. The witness binds the head only at anchor time, so everything since the last anchor is still re-signable by whoever runs the recorder — an operator can rewrite the last ten minutes and the next anchor will happily bless the cleaned-up head. For a long session, that soft tail is always the most recent, most interesting ten minutes. Is the interval configurable, and would you anchor on events as well as on the clock — immediately before and after a human approval or a destructive tool call, so the highest-stakes steps never sit in an unanchored window?

Same question on placement, for the stdio wrapper: the recorder only sees what passes through it, and a coding agent with shell access on the same host can invoke the server binary directly and skip the proxy entirely. Is there a deployment pattern you recommend — separate UID, network namespace, the server only reachable through the proxy — or is keeping the agent away from the raw server the deployer's job? The four conditions read airtight, but condition 1 quietly depends on the answer.

Collapse
 
reidmarlow profile image
Reid Marlow •

The note in TRUST.md about tools reaching the network outside the recorder is the main gap in MCP stdio wrapping. Whenever a coding agent also has a shell or Python execution tool, a subprocess can run curl or open a raw socket directly without crossing the MCP proxy at all.

Wrapping sealedrun around MCP stdio catches structured server calls cleanly, though proving completeness on an agent with shell access still requires putting the recorder at the container egress proxy or sandbox boundary so subprocesses cannot bypass it.