agmiAgent Memory Integrity GitHub

Agent Memory Integrity

Does your agent's memory notice when someone changes it?

12 of the 12 stores that make no integrity claim served every applicable edit as genuine. 20 agent memory and checkpoint stores, 37 rows, seeded through their own APIs, edited behind their backs nine ways, and asked to read their memory again. The 12 that claim tamper evidence were measured against that claim, with the detection point named.

See the scorecard Run it in CI Meet the memory agent

Measured 2026-10-08 · Darwin arm64, Python 3.12 · agmi 0.6.3

Cross-context replay, T6 user A A0 A1 A2 user B B0 B1 B2 A2 B reads its memory A2 accepted
T6, cross-context replay. Genuine bytes from user A, copied over user B's newest record. B's store serves them as B's own. No forging, no key.

The problem in three frames

01 agentrememberslimit is 40ship on Fridaymanager: Ravi

An agent remembers

Facts, decisions and checkpoints go into a store: a SQLite file, a Postgres table, a vector index, a cloud service.

02 limit is 40ship on Mondaymanager: Ravistoreaccess

Someone edits the store

No keys, no API: a mounted volume, a shared database credential, a restored backup, a data-plane role. One record changes.

03 ship on Mondayagent"we shipMonday"

The agent believes it

It reads the store the way it always does and acts on the edited memory as its own. agmi measures whether anything in that read, or in a later audit, notices.

How one cell is measured

Seed through the tool's own API, apply one edit behind its back, reopen the store the way its users would, read through the tool's own read path, record what it said. Three controls run before any verdict counts.

How one cell is measured Seedthrough the tool'sown API Editone change to thestore, no keys Restartnothing cachedin process Readthrough the tool'sown read path Verdict rejectedreportedaccepted control: a reload with no edit must still verify, or the cell is n/a the verdict is what the tool does, never what we infer
How one cell is measured. The attacker's edit is the only thing that changes between seed and read; the tool's own behaviour on read is the verdict.

Where to go next

  1. 1What an attacker can doFifteen edits and attacks, each drawn and explained
  2. 2How a cell is measuredThree controls before any verdict counts
  3. 3What each store doesOne page per store, the tool's own words per cell
  4. 4What a level meansFive levels a vendor can claim and link
  5. 5Run it on yoursOne CI step, under a minute, offline

The board

Rows are stores, columns are edits. Click a cell for what the tool said; click a row for its page.

Stores that make no integrity claim
StoreT1T2T3T4T5T6T7T8T9
LangGraph SqliteSaverlanggraph-checkpoint-sqlite 3.1.1 · L0
LangGraph PostgresSaverlanggraph-checkpoint-postgres 3.1.2 · L0
LangGraph RedisSaverlanggraph-checkpoint-redis 0.5.2 · L0
OpenAI Agents SDK SQLiteSessionopenai-agents 0.20.0 · L0
LlamaIndex Memory, SQLAlchemy chat storellama-index-core 0.14.24 · L0
CrewAI long-term memory, LanceDB datasetcrewai 1.15.23 · L0
Vertex AI Agent Engine Memory Bank, managed store, edits through the data-plane APIgoogle-cloud-aiplatform 2.4.0 · L0
OpenClaw agent memory, MEMORY.md read through memory_getopenclaw 2026.10.1 · L0
Hermes Agent memory, MEMORY.md read through the system-prompt snapshothermes-agent 0.19.0 · L0
Letta block checkpoint historyletta 0.16.8 · L0
Mem0 local Qdrant storemem0ai 2.0.20 · L0
inspeximus, receipts off (default)inspeximus 3.0.0 · L0
Stores that claim tamper evidence
StoreT1T2T3T4T5T6T7T8T9
OpenFang model, tip-persistence fixreference model of a hash chain · L1
inspeximus, receipts on, attacker holds the store directoryinspeximus 3.0.0 · L0
inspeximus, receipts on, attacker also holds the config homeinspeximus 3.0.0 · L0
langgraph-ledger over SqliteSaver, hash-chained ledger, verify_thread auditlanggraph-ledger 0.3.0 · L0
memory-blackbox memory.md watcher, agent process alive, scan auditmemory-blackbox 0.1.1 · L0
memory-blackbox memory.md watcher, agent restarted before the scan, scan auditmemory-blackbox 0.1.1 · L0
Atelya Attest, keyed hash chain, verify_chain auditatelya-attest 0.1.1 · L0
Atelya Attest, keyed hash chain plus anchored head, verify and consistency auditatelya-attest 0.1.1 · L0
CONTINUUM event log, hash chain, verify_events auditcontinuum-agent 0.1.0 · L0
CONTINUUM event log, hash chain plus Ed25519-signed head, attest-verify auditcontinuum-agent 0.1.0 · L0
AtMem 2.3.8, audit chain alone, verify() auditatmem 2.3.8 · L0
AtMem 2.3.8, audit chain with an external checkpoint outside the attacker-controlled store directory, verify() auditatmem 2.3.8 · L0
acrf-memory-guard, per-entry HMAC over a JSON storeacrf-memory-guard 0.1.0 · L0
Agent Memory reference runtime, SQLite canonical substrate, bucketed row digests, fail-closed openagent-memory-reference 0.2.0 (f2aef57) · L3
Measured through the front door
StorePlanted factCross-userRetrievalHiddenUpdateMetadata
LangGraph SqliteStorelanggraph-checkpoint-sqlite 3.1.1
Letta archival memoryletta 0.16.8
inspeximus, trust root keyed on the labelinspeximus 3.0.0, maintainer-contributed
inspeximus, trust root keyed on an attested keyinspeximus 3.0.0, maintainer-contributed
Reference store, user-scopedno defence, for calibration
Reference store, unscopedno defence, for calibration
Reference store, defendedsigned writes, quarantine, stuffing check
accepted
the tool loaded the altered store and served the edit as genuine
reported
the read path served it; a separate audit call flagged it
rejected
the read path refused or flagged it before the agent could act on it
n/a
the edit does not apply to this store, or the cell is not yet measured

If you ship a memory store

Put the nine edits in CI as one step. memory-blackbox and MythologIQ already run them against every release.

The CI step

If you build agents

Pick the store by its row, not its README. A served edit means the agent acts on it first and nobody finds out.

Every store

If you write standards

The method is an Internet-Draft in the IETF BMWG and the proposed test for OWASP's memory integrity control.

The method

The memory agent

Beyond the fixed scorecard, agmi ships an agent that hunts: point it at one store and it searches six attacks, three channels and the content mutations for the first that gets a false memory served as trusted, proves the landing against a positive control, and hands back a reproduction script. Against every real store measured it walked in on the first attempt; only the defended reference store made it search. It runs behind an authorisation gate that fails closed. How it works.

The hunt loop authorisefails closed pick the next attempt6 attacks3 channelsbase fixture, then mutations write, then readas the victim would served as trusted?positive control checked no: next attempt findingproof, served text, reproduction script library target: allowedlocalhost: allowedother host: token named for it, or consent fileanything else: raises before the hunt starts
The agent searches attacks, channels and mutations for the first that lands, proves each landing against a positive control, and reports only what it proved. The gate runs before any target is touched.

Latest findings

  1. A managed cloud store joins the table: Vertex AI Memory Bank serves seven edits, two have no API
  2. First row to refuse all eight record-level edits on the read path: the Agent Memory reference runtime
  3. T9, a rollback of the whole store that every digest still passes
  4. A vendor fix driven by the suite: memory-blackbox restart gap, reported and fixed in a day

All findings · Feed

Five levels a store can claim

A level is a set of edits rejected on the read path, in the order attackers reach for them. Vendors can claim one and link the row that earns it.