inspeximuswe have inspected

mem0 2.0.11 export · inspeximus 2.41.0

How do you move from mem0 to inspeximus without losing whose memory each one is?

A mem0 export is a list of texts with a user_id on each. The import writes one record per memory with that user as its subject, mem0's timestamp as the event time, and one signed receipt each, so a right-to-erasure request reaches every imported record that carried that user. Run twice, it writes nothing the second time, and it stays that way after the person has been erased. The transcript on this page is a real run; ids, hashes and the order of the erasure preview change per run, nothing else does.


the run, 2026-09-17, inspeximus 2.41.0

What does an import look like, and what happens when you run it twice?

Four memories from two users in the shape mem0 2.0.11's get_all(show_expired=True) returns, hashes included, one of them expired. A dry run, the import, the import again, then the store.

$ cat mem0_export.json
{
 "results": [
  {
   "id": "8f3c2a1e-4b6d-4f1a-9c2e-1a2b3c4d5e6f",
   "memory": "Alice prefers email over phone calls",
   "hash": "3bdbc25483ecfd293568596e79ba9105",
   "created_at": "2026-09-01T10:15:00Z",
   "updated_at": "2026-09-01T10:15:00Z",
   "user_id": "alice"
  },
  {
   "id": "2b7e9d4c-8a1f-4e3b-b5c6-7d8e9f0a1b2c",
   "memory": "Alice's phone number is +100",
   "hash": "07465ab59b99af58d34359d8e0e96d85",
   "created_at": "2026-09-02T08:00:00Z",
   "updated_at": "2026-09-02T08:00:00Z",
   "user_id": "alice",
   "metadata": {
    "key": "phone"
   }
  },
  {
   "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
   "memory": "Bob's phone number is +300",
   "hash": "44d851a18e9a1991e3a6880db89edd4c",
   "created_at": "2026-09-03T12:30:00Z",
   "updated_at": "2026-09-03T12:30:00Z",
   "user_id": "bob",
   "agent_id": "support-bot",
   "metadata": {
    "key": "phone"
   }
  },
  {
   "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
   "memory": "Promo code SUMMER26 is valid",
   "hash": "45e6da7c61dc19063ec2509dbae3d34e",
   "created_at": "2026-06-01T00:00:00Z",
   "updated_at": "2026-06-01T00:00:00Z",
   "user_id": "bob",
   "expiration_date": "2026-09-01"
  }
 ]
}

$ inspeximus writer-key --new --out key.txt
secret written to key.txt (0600)
public key: 0562f51d421d1868df51114a2986c3bd90e227853872fac76e9eb0af4d2306d7

point the server at it:  INSPEXIMUS_WRITER_KEY_FILE=key.txt
pin this writer as trusted with trust_seeds={'key:0562f51d421d1868df51114a2986c3bd90e227853872fac76e9eb0af4d2306d7'}
KEEP THE SECRET OUT OF GIT. It attests AUTHORSHIP, not truth.

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt import-mem0 mem0_export.json --dry-run
would import 4 memory item(s) from 2 user(s); 0 carry no user_id and would have no subject

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt import-mem0 mem0_export.json
imported 3 record(s), skipped 1 (expired in mem0)

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt import-mem0 mem0_export.json
imported 0 record(s), skipped 4 (already imported, expired in mem0)

$ inspeximus --path mem.json list
- Bob's phone number is +300 [key=bob::phone]
- Alice's phone number is +100 [key=alice::phone]
- Alice prefers email over phone calls

Three records written, the expired promo code skipped, and the second run skips all four because the store already holds their mem0 ids. Both phone numbers carried {"key": "phone"} in their metadata; the import namespaced the key by user, so Alice's and Bob's do not retire each other.

Does a correction stay corrected, does an erasure reach everything about one person, and what does a re-import do afterwards?

A new value under the imported key retires the old one; recall returns the new value. The erasure for Alice reaches the retired value too, and the same export imported once more writes nothing back.

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt remember "Alice's phone number is +200" --key alice::phone --source mem0/user/alice
remembered d529fa518e [key=alice::phone]

$ inspeximus --path mem.json recall 'Alice phone' -k 2
- Alice's phone number is +200
- Alice prefers email over phone calls

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt forget-subject mem0/user/alice --request-id DSAR-3 --dry-run
would erase 3 record(s): 3 naming the subject, 0 reached through lineage
  [direct   ] 72164baa15  Alice prefers email over phone calls
  [direct   ] b0dcaeae42  Alice's phone number is +100
  [direct   ] d529fa518e  Alice's phone number is +200

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt forget-subject mem0/user/alice --request-id DSAR-3
erased 3 record(s), 3 tombstone(s)

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt import-mem0 mem0_export.json
imported 0 record(s), skipped 4 (already imported, expired in mem0, imported earlier and since erased)

$ inspeximus --path mem.json list
- Bob's phone number is +300 [key=bob::phone]

The comparison page measures the same property against mem0 itself: after a correction, restated as the old value, inspeximus returns the corrected value in 100% of 30 trials and mem0 2.0.11 in 53.3%. The erasure page shows the certificate a completed erasure produces.

Which mem0 call becomes which inspeximus call?

Read from the signatures of mem0 2.0.11 (mem0.memory.main.Memory) and inspeximus 2.41.0.

mem0 2.0.11inspeximuswhat changes
add(messages, user_id=...)remember(text, source=..., key=...) mem0 runs an LLM to extract facts from the messages; inspeximus stores the text you pass. Extract first if you want extraction. The source is the subject an erasure keys on; the key is what a later correction retires.
search(query, filters=...)recall(query, k=...) superseded values are hidden by default; why_recalled() explains a hit per channel.
get_all(filters=...)items, subject export the subject export returns everything about one person with provenance, for an Art. 15(3) request.
get(memory_id)next(r for r in items if r["id"] == id)records are plain dicts; there is no lookup call, and the MCP server's get does this for you.
update(memory_id, data=...)subject_rights.rectify(store, key, text, actor, reason) mem0 rewrites the vector record and logs the change in its history table; inspeximus supersedes under the key, keeps the old value as a record, and the ledger entry names the actor and the reason.
history(memory_id)history(key), as_of(key, when) mem0's history is per memory id; inspeximus's is per key, and as_of answers what was true at a moment.
delete(memory_id)forget(ids=[id])leaves a signed, content-free tombstone.
delete_all(user_id=...)forget_subject(subject) reaches derived records through declared lineage; a dry run names the blast radius; erasure_certificate() attests it.
reset()delete the store filethere is no call that erases a receipt chain, by design.

What does the import not do?

The text arrives as mem0 extracted it; nothing is re-extracted. History stays behind: mem0 keeps one in its own SQLite table (history(memory_id)), and get_all() exports the last value only. No vector index is built, so lexical recall works on import and semantic recall waits for an embedder you configure. A future expiration_date is kept in meta and never enforced; the record is served until you forget it. mem0's hash is kept for tracing and not checked, and mem0's metadata filters in search() have no counterpart here, the metadata sits under meta.

Two things concern the subject. A memory exported without a user_id gets none, and a per-user export loop never sees what mem0 stored under an agent_id or run_id alone, so export those scopes as well; the import counts records with no subject and says so, because a record with no subject is one a DSAR cannot reach. And metadata.key is this importer's convention rather than a mem0 field: an app that stored a key in metadata for some other reason gets a supersession key it did not ask for.

How do I run this on my own export?

# in your mem0 project, on the Memory your app builds, once per user (and per agent_id or run_id)
python -c "from mem0 import Memory; import json; m = Memory.from_config(YOUR_CONFIG); print(json.dumps(m.get_all(filters={'user_id': 'alice'}, top_k=100000, show_expired=True)))" > alice.json

pip install "inspeximus[crypto]"
inspeximus writer-key --new --out key.txt
inspeximus --path mem.json --receipts --receipt-key-file key.txt import-mem0 alice.json --dry-run
inspeximus --path mem.json --receipts --receipt-key-file key.txt import-mem0 alice.json

The export recipe is the one mem0's maintainers give in discussion #5967; mem0's open PR #6330 proposes an export_session() bundle in a different shape, which this reader does not take yet. From Python: from inspeximus.migrate import import_mem0, load_export. The MCP server serves the imported store to Claude Code, Cursor and any other client; see Claude Code, one line.