mem0 2.0.11 export · inspeximus 2.41.0
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
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.
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.
Read from the signatures of mem0 2.0.11 (mem0.memory.main.Memory) and
inspeximus 2.41.0.
| mem0 2.0.11 | inspeximus | what 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 file | there is no call that erases a receipt chain, by design. |
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.
# 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.