
What the AI crawlers actually fetched from this site in one week
SAGARIS5 min
BlogEngineering
Emitting the record is the easy half. The test is whether a specific question has a defined answer, and which questions yours cannot answer.

In the Klue OAuth token incident, one compromised integration ran nearly a thousand queries in fifteen minutes against a single customer environment. Seventeen or more organisations were confirmed affected and 3.4 GB was posted. The incident is dated 11 June 2026 and was reported on 19 June (https://thehackernews.com/2026/06/salesforce-disables-klue-app.html; we are relaying this from research rather than a first-hand fetch).
Hold on to the two numbers that sit next to each other awkwardly: a thousand queries in fifteen minutes, and eight days between the incident and the report. Every one of those queries hit systems that log. The records existed. What "audit trail" is supposed to mean is that somebody can turn those records into an answer to a question like "what did this token read, in which tenant, and when did it stop", quickly, with confidence that the answer is complete.
Emitting the record and being able to answer that question are two different engineering problems, and almost every product that advertises the second has only built the first.
A log is a stream optimised for writing. It is append-only because appends are cheap, unstructured at the point of reading because structure costs the writer, and retained rather than kept because storage has a bill attached. Nothing in that list is a criticism. Logs are excellent at what they are for, which is telling you what a system was doing while you were not watching.
An audit trail is a different artifact defined by a different property: for a named question, there is a defined answer, and the answer does not depend on who is grepping or how good they are at it.
We wrote the sharpest statement of the difference about our own code, in a file describing a control we had deliberately shipped in a weaker form. Where the signed record of who asserted a fact currently goes is Cloud Logging, "because it is an append-only sink that needs no migration", and the file is explicit that this is phase one and why: "A log sink is retained, not permanent, so a signature parked there proves an action was not ALTERED but does not by itself prove one was not DROPPED."
That sentence is the whole test. Tamper-evidence and completeness are separate properties. A log gives you the first for free and never gives you the second, because a log has no opinion about what should have been in it.
Can you ask it something, and is the answer defined? In our claim store, provenance is not a grep target. A provenance lookup keyed by a claim id, and a point-in-time lookup keyed by a subject and an instant, are declared methods on an interface that every adapter implements and a shared contract suite pins. The difference between that and a log is that the answer's shape is agreed in advance, so a caller can write code against it and a test can fail when it changes. Ninety percent of "full audit trail" claims in this market resolve, on inspection, to a table you can page through in a UI, which is a log with pagination.
Does the record survive being corrected? This is where most designs quietly lose. Correcting a fact and destroying the evidence of the old one are the same operation in a normal database: you update the row. Ours separates them. Deprecating a fact is retain-and-hide: nothing is deleted, the lifecycle status is untouched, and the provenance lookup still returns the full history. Supersession appends to a per-row history chain and closes the validity interval with a coalesce, so the interval end is write-once and "a later recovery does not rewrite the past interval."
The detail we like best is a refusal. A deprecation requires a reason, and the interface states the consequence plainly: "an unexplained veto cannot be recorded." Somebody suppressing a fact has to say why, in the same write, or the write fails. That is a small thing that turns an audit trail from a record of what happened into a record of why, which is the only version anybody actually wants during an incident.
Is absence distinguishable from silence? If a record is missing, can you tell whether the event did not happen, or happened and was not recorded, or was recorded and aged out of retention? Most systems cannot answer this, and it is the question every post-incident review turns on.
An article about auditability that lists only its wins is a marketing page. Here is the list, derived from committed source rather than from memory.
The combined bitemporal query is not addressable. We store two independent time axes: when a fact was true, and when we came to believe it. One method travels the second, another travels the first, and both take exactly one instant. There is no method on the interface that takes two. So "what did we believe in March about what was true in June" is a question the model supports and the seam does not expose. It is a real gap, not a philosophical one, and it is the query a lawyer asks.
On the shipped adapter, time travel does not rewind everything. The transaction-time query reconstructs a claim's status and confidence from the history event at that instant, but it returns the current end-of-validity value rather than a per-version one. So asking what we believed in March about a claim superseded in June returns a March status alongside a June interval end, which is a fact the system did not possess in March. Our prototype adapter snapshots the value per version and gets this right. The prototype is not the one in production. The weaker implementation is the shipped one.
And nothing binds the two. The shared cross-adapter contract suite has eight references to the end-of-validity field, and every one of them sits inside a validity-interval test. Not one asserts anything about the value a transaction-time query returns. Which means the divergence above is unbound, can drift further, and would do so without any test going red. That is the exact defect class we spend most of our engineering discipline hunting in other people's code, sitting in ours, and the reason we know about it is that we went looking specifically.
The signed actor is not on. The trail always names the extractor and the source, because those are NOT NULL columns. It names a cryptographically signed human or agent actor only when a control is armed, and that control is not generally available yet. The file says of itself that when unarmed it "returns null, and the ingest path is byte-for-byte what it was. THE CONTROL DOES NOTHING." Built, not yet generally available, not running. Durable storage for those records is a column on the claim record and has not been added.
Everybody emits. Emitting is a library call. The questions worth asking a vendor, including us, are narrower and harder: name a question your audit trail answers, show me the method that answers it rather than the screen that displays it, tell me what happens to the record when someone corrects the underlying fact, and tell me which questions it cannot answer today.
That last one is the tell. A vendor with a real audit trail has a list, because you only discover the list by trying to query the thing. A vendor with a log has no list, because nobody has asked it anything yet. The worst possible time for a customer to discover which of those they bought is during the eight days between an incident and its report.
Thirty minutes, your own data, no setup.
SAGARIS opens fully in October 2026. Join the waitlist and we will be in touch before launch.