BlogProduct

PRODUCT

Unauditable memory is a backdoor with a nicer name

If a system can remember something without recording where it came from, then writing to that memory is a way in. The lock is in the identity of the record.

SAGARISProduct8 min
Unauditable memory is a backdoor with a nicer name

There are two hash functions side by side in the module that computes claim identity, and the difference between them is the whole subject of this article.

The first computes a fact key. It hashes the workspace, the subject, the predicate and the object. Roughly: who or what this is about, what property, what value.

The second computes a claim identifier. It hashes all four of those, and then two more things: the identifier of the source the claim came from, and the version of the extractor that produced it.

So a fact is what is believed. A claim is that belief plus its origin. And because the identifier is derived from the origin, you cannot mint a claim identifier without a source and an extractor version. Not as a validation rule that a code path might skip. As arithmetic. There is no input to that function that produces an identifier while leaving the provenance out.

Why the missing half is a way in

Consider a memory layer that stores conclusions without recording where each one came from. It is not obviously broken. It answers questions. The answers are often good.

Now consider what an attacker has to do to change what that system believes. Not break the authentication. Not reach the database. Just contribute text that the extraction pipeline reads, in the ordinary way, through the ordinary door: an email, a form, a support message, a page the enrichment vendor crawls. The belief lands. It looks exactly like every other belief. And afterwards there is nothing to point at, because the record does not carry the thing you would need to trace it.

That is a durable, remotely writable, unattributable influence over a system's future decisions. It is a backdoor. It just does not look like one, because nothing was breached and no rule was broken, which is precisely why it deserves the harder word.

The defence is not detection. Detection means guessing which sentences were hostile, and that is a losing game against text a human wrote. The defence is that every stored belief carries, in the row, what produced it, so that a bad one can be found, scoped and removed by its origin rather than by its content.

What "carries its provenance" has to mean to be worth anything

On the claims table, every provenance column is NOT NULL: the evidence reference, the extractor identifier, the extractor version, the confidence, the time the fact was true in the world, and the time the system came to believe it. There is no nullable "source, if known" column, because a nullable provenance column becomes an empty one within a quarter.

Three details make the difference between provenance and decoration.

Evidence is a pointer plus a hash, never the text. The reference names a source table, a source identifier, a character span, and a hash of the quoted span. The comment explaining why the artifact hash exists is the load-bearing one: without it there is nothing to point at, because the mailbox row can be edited, re-synced or deleted, and the offsets then dangle. A citation into mutable storage is a citation that quietly stops meaning anything.

Changing the model produces a new claim rather than a silent update. The extractor version is part of the hash. Bump the prompt or the model, re-run extraction, and you get a new claim identifier rather than a mutated old one. Model drift becomes visible as supersession instead of invisible as an edit.

What the system does not know, it says. The model identifier and the prompt hash live on a separate extraction-run ledger and are nullable, and the migration that added them states the reason: runs recorded before that migration have no model or prompt recorded, and the read path reports that as unrecorded rather than naming a model nobody verified. Filling a provenance gap with a plausible value is worse than leaving it visibly empty, because the plausible value is indistinguishable from a checked one.

The prompt itself is stored only as a hash, on purpose, because the prompt embeds the customer's body text, which is already stored once as a content-addressed artifact and should not be duplicated. Auditability and data minimisation pull against each other, and this is what choosing both looks like.

Three layers, because one refusal is one refactor from gone

A claim missing its evidence reference is rejected by a shape guard before it reaches an adapter. It is rejected again by the Postgres adapter, whose comment records that an earlier version substituted a default for a missing confidence method, that the default read as honest, and that it was fail-open: it minted rows that no calibration grouping could ever attribute and that no test could fail on. And it is rejected a third time by the database constraints.

The middle one is the story. A default that looks like humility is often a hole. Writing "unspecified" instead of refusing produces a row that is technically present, permanently unattributable, and completely invisible to any query looking for problems.

Two more constraints narrow what can be written at all. Emission is closed: a predicate with no entry in the ontology registry cannot be stored, so the vocabulary of what the system may believe is a reviewed list rather than whatever an extractor emits. And nine categories are refused outright with written reasons, including health, legal matters, personal finances, religion or politics, and any detail about a third party who is not the contact, on the stated grounds that they never volunteered anything.

The mechanism that survives a re-sync

Here is the part that turns provenance from an audit feature into a defence.

Suppose a fact in the memory is wrong and gets marked deprecated. Then the source system syncs again and sends the same wrong value. If suppression were applied to the claim identifier, the re-sync would defeat it, because a claim identifier hashes the source identifier, so a new sync mints an identifier nobody has ever seen and nothing matches.

So suppression is applied to the fact key, the hash that deliberately omits source and extractor. Rank the fact, and the wrong value stays suppressed no matter how many previously unseen sources assert it. A shared contract case pins exactly that, across fifty re-assertions from unseen sources.

That is the two-hash design paying for itself. One identity for what is believed, one for who said it, and a suppression that binds to the first so it cannot be washed out through the second.

The half this article is obliged to say

Provenance recorded is not provenance read, and there is measured evidence for the gap. In a browser game published by an agent-permissions vendor, the single most-missed command was approved 64.7 percent of the time even though the log displayed directly above the approval prompt contained the malicious payload, across 409,000 approve or deny decisions at a mean accuracy of 66.3 percent (https://scalex.dev/blog/ai-agent-permissions-stats/, 5 August 2026). The author's own caveat travels with the number: it was a short game with roughly a third of commands being threats, players were under artificial time pressure, and the post closes by recommending a product. Take the direction, not the decimal. Showing the evidence is necessary and is not sufficient, and any company selling provenance has to design for the case where nobody reads it.

Three limits in this system specifically, stated because an article about auditability that hid them would be self-refuting.

The function that fetches the stored source bytes and re-verifies the quote hash against the exact span is not a shipped behaviour yet. So the accurate sentence is that every claim carries a verifiable quote hash, and the inaccurate one is that a user can click through to a re-verified source span today.

Actor attestation, the piece that would sign which actor asserted a batch, is not generally available yet. With its flag unset the function returns null and the ingest path is byte for byte what it was, and the module says in capitals that the control does nothing in that state. The same module notes that its record goes to a log sink, which proves an action was not altered but does not by itself prove one was not dropped.

And the extraction that would fill this memory from email, transcripts and enrichment is not generally available yet.

For context rather than credit: EU AI Act Article 50 transparency obligations applied from 2 August 2026, while the automatic record-keeping duty in Article 12 sits in the high-risk chapter that Regulation (EU) 2026/1744 deferred to 2 December 2027 for standalone Annex III systems. Nothing here is a claim of conformity, compliance or certification with that regime or any other.

What holds, at the commit this was read from, is narrower than the headline and easier to check: the identity of a stored belief is derived from its origin, so a belief without an origin has no identifier, and three independent layers refuse to write one anyway.

The question to ask any vendor whose product remembers things: if one of the things it believes turns out to have been planted, what query finds every other belief that came from the same place?

SAGARIS

Written by the SAGARIS team.

See the engine run on your pipeline.

Thirty minutes, your own data, no setup.

Book a demo

Get the next one in your inbox.

SAGARIS opens fully in October 2026. Join the waitlist and we will be in touch before launch.

We use these details to contact you about SAGARIS. See our privacy policy.

Book a demo