On-prem, MCP-native, cryptographically-auditable memory for regulated AI (EU AI Act Art.12 Β· India DPDP Β· HIPAA Β§164.312(b)).
Mnemo runs on your own infrastructure and writes a memory log an auditor can verify offline, without trusting the store or any hosted tier β see Positioning for how that compares to Mem0 and Letta.
π Documentation: https://sattyamjjain.github.io/mnemo/ β the full mdBook (quickstart, architecture, MCP tool reference, REST/SDK guides, compliance docs), deployed from docs/ on every push to main.
Mnemo (from Greek mneme β memory) is an embedded database (DuckDB in-process, or your own PostgreSQL) whose primitives β REMEMBER, RECALL, FORGET, SHARE β are exposed as MCP tools any AI agent connects to directly. What sets it apart for regulated deployments: every write and delete is a SHA-256 hash-chained agent_events entry an external verifier can check offline (no store, no hosted tier to trust), and mnemo-compliance layers signed audit-log export + DPDPA consent records on top.
β Verify a mnemo log yourself, without trusting mnemo β a five-minute transcript: write three records, export the chain, run a standalone verifier whose only imports are the Python standard library, then tamper with one record and watch it get caught by name. Includes what a PASS does not mean, and a concurrency defect the exercise itself found.
β Positioning: on-prem, MCP-native, cryptographically-auditable memory for regulated AI β how mnemo compares to Mem0, Letta, and native provider memory on the compliance-audit axis, wired to the shipped bench numbers.
Mnemo runs in-process on your own infrastructure (embedded DuckDB or your
PostgreSQL) with no hosted tier to trust β and its memory-write log is
tamper-evident: every write and delete is a SHA-256 hash-chained
agent_events entry, and an external verifier can detect any post-hoc
mutation offline, without consulting the store. That is the record-keeping
substrate regulated AI deployments need (EU AI Act Art.12 / Art.26(6), India
DPDPA, HIPAA Β§164.312(b) audit controls).
This is proven, not asserted β a deterministic, offline bench writes memories
through the real path, then shows an outside verifier accepts the pristine log,
catches 100% of single-byte mutations (256 trials, Wilson 95% β₯ 98.5%), and
confirms forget appends a signed delete event rather than erasing the trail:
cargo run --release -p mnemo-audit-conformance-bench
# β bench/audit_conformance/results/conformance.md (byte-stable report + recomputable SHA-256 crypto vector)Scope of that 256: it is mutation detection on a log the bench wrote one record at a time, and says nothing on its own about concurrent writes. So the same bench now runs a concurrency arm and reports it in the same units, side by side:
| measurement | one trial is | successes / trials | rate | Wilson 95% |
|---|---|---|---|---|
| Tamper detection, serially written log | one single-byte mutation, caught and attributed to the right record | 256 / 256 | 100.0% | [98.5%, 100.0%] |
| Chain linkage, concurrently written log | one record naming its predecessor, with 16 writers running at once | 255 / 255 | 100.0% | [98.5%, 100.0%] |
16 writers Γ 16 writes = 256 records on 8 tokio worker threads, on an Apple M4
(10 cores, arm64, macOS); the denominator is 255 because exactly one record is
legitimately the chain head. Alongside the rate the arm asserts what a rate cannot
express β exactly 1 head, 0 forks, 0 dangling records, and all 256 reachable by
walking links from that head β with no interval to hide behind. Until v0.5.29 that
arm produced 16 heads and 0 links; the structural regression test lives in
concurrent_chain_linkage.rs.
Reproducible-by-disclosure memory: mnemo publishes its LoCoMo numbers with a
fixed seed + a Wilson-95 you can re-run offline (cargo run --release -p mnemo-locomo-bench --bin reproduction_bench), tabled against vendors' cited,
not-re-run claims β see
bench/locomo/results/reproduction_2026-07-06.md.
Real-embedder retrieval quality (measured, not asserted): the byte-reproducible number above runs under a deterministic hash-bag embedder, which is a lexical floor, not a retrieval result.
The one headline real-embedder number is the generated block below: Xenova/all-MiniLM-L6-v2 384-dim, n=45, recall@1 0.689 [0.543, 0.805], against a lexical control on the same corpus and harness. On the same 45 queries the paired gap over that control is +0.267 [95% 0.133, 0.400], McNemar exact p=4.9e-4 β it separates.
There is deliberately only one headline, and an older measurement is kept below it under its own heading rather than beside it.
Every published number, this one and the poisoning / audit / retrieval numbers
further down, is collected in one place with its exact reproduction command, its
raw results file, and a "what this does not show" column:
docs/benchmarks/index.md (the single benchmark
entry point). The mirror of that page, things a reader might reasonably assume
mnemo measures and it does not, is
docs/security/known-limitations.md. Full per-mode tables and the different-axes caveat vs. Mem0/Letta
are in the benchmark tables below and bench/RESULTS.md.
Real-embedder recall, measured on the supported backend. Gold-document recall@1 = 0.689 [Wilson 95% 0.543, 0.805], recall@5 0.889, recall@10 0.911, MRR 0.770. Against the lexical control on the same 45 queries the paired gap is +0.267 [95% 0.133, 0.400], McNemar exact p=4.9e-4 (12 queries won, 0 lost) β the gap separates at 95%.
| embedder | Xenova/all-MiniLM-L6-v2 (onnx/model.onnx) (384-dim, backend onnx) |
| weights sha256 | 759c3cd2b7fe⦠|
| weights source | https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main/onnx/model.onnx |
| storage backend | duckdb (in-memory) |
| corpus | crates/mnemo-core/benches/data/longmemeval_m.jsonl, n=45 queries, mean of 5 seeds |
| control (lexical) | recall@1 0.422 [0.290, 0.567] |
| paired gap vs control | +0.267 [0.133, 0.400], McNemar b=12/c=0, exact p=4.9e-4 |
shipped default (auto) |
recall@1 0.631 [0.476, 0.749] |
| paired gap vs default | +0.058 [-0.031, 0.160], McNemar b=4/c=2, exact p=0.6875 β does not separate at 95% |
| hardware | arm64/darwin Apple M4 |
| measured | 2026-08-24 at 3b876fc |
The lexical row is the control, not a second headline: it is the same corpus and the same harness with the vector lane switched off, so what the embedder buys is the difference between them. Do not read that difference off the two intervals β they overlap (0.543 sits below 0.567), and overlapping intervals neither establish nor rule out a difference. The paired row is the one that answers it: the same queries scored both ways, so each query is its own control.
Against the strategy you actually get. auto is the default: recall() resolves an unset strategy to auto, so unless a caller names one, this is the path they are on β and it scores recall@1 0.631 here, not 0.689. On the same 45 queries semantic beats it by +0.058 [95% -0.031, 0.160], McNemar exact p=0.6875 (4 queries won, 2 lost). That interval runs from -0.031 to 0.160: it contains 0, so the gap does not separate at 95% and the sign of the difference is not established by this sample. Roughly n=127 paired queries would be needed at this effect size. The +0.267 headline above is against the lexical control, which is not the default and which nobody runs by choice; it is the embedder's contribution, not the improvement a user sees over stock behaviour.
Reproduce:
# fetch the exact weights the sha256 above pins
curl -sSL --fail -o model.onnx https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main/onnx/model.onnx
MNEMO_ONNX_MODEL_PATH=./model.onnx cargo run --release --features onnx \
-p mnemo-locomo-bench --bin locomo_v1_benchWhat this number is not. It is not a LoCoMo leaderboard score and is not comparable to one: the corpus is the bundled LongMemEval_M slice, not full LoCoMo. It is retrieval quality only, with no LLM in the loop and no answer-correctness judge. It says nothing about poisoning resistance or audit integrity, which are measured separately. n=45 is below 100, so the bench marks it preliminary; treat the interval, not the point estimate, as the claim. This label cannot clear by re-running. The corpus is the bundled longmemeval_m.jsonl, which is 45 records in full β not a sample of a larger local file β and the bench sets preliminary whenever n < 100. So it reflects a corpus-size decision, not work in progress. Clearing it honestly needs the larger public LongMemEval slice, which is access-gated (see #44); until that is vendored under a recorded licence, this number stays preliminary at n=45.
This block is generated from bench/results/locomo_v1.json by
scripts/gen_recall_number.py; the doc-guards CI job runs it
with --check, so the README cannot drift from the measured result. It is also
CI-reproducible: real-embedder-benches-nightly
fetches the digest-pinned checkpoint, re-runs the bench and compares against the
committed artifact nightly, opening an issue if either number leaves its band.
The most recent run reproduced recall@1 exactly β 0.6890 committed, 0.6890
regenerated β on an x86-64 Linux runner, against a number first measured on
arm64/darwin.
Earlier measurement, different embedder and corpus (nomic-embed-text 768-dim, n=23)
An earlier real-embedder run, kept for provenance and not a second headline. It used a different embedder on a different slice, so it is not comparable to the number above and the two must never be quoted together as a range.
| embedder | nomic-embed-text (768-dim, local via Ollama, no API key) |
| corpus | bundled LongMemEval_M held-out slice, n=23, mean of 5 seeds |
| recall@1 | 0.739 [Wilson 95% 0.535, 0.875] |
| recall@5 | 0.826 [0.629, 0.930] |
| MRR | 0.805 |
| mode | vector_only, the one stable strong mode |
| first measured | 2026-06-29 @ 640b7b1; raw data in docs/benchmarks/baseline.json |
The interval is the point. At n=23 the Wilson 95% interval is [0.535, 0.875], which is wider than the headline's and fully overlaps it ([0.543, 0.805]). That overlap is not a finding of equivalence β the same arithmetic that cannot prove a difference cannot prove its absence either. What can be said is narrower and worth saying plainly: these are two separate runs on different corpora, so unlike the semantic-vs-lexical comparison above there is no paired statistic available here at all, and at n=23 the interval is far too wide to resolve a gap of this size in either direction. The higher point estimate is therefore not evidence that this embedder is better. Neither run clears the repo's own nβ₯100 bar, and both are marked preliminary for that reason.
Reproduce with no credentials and no special build feature:
ollama pull nomic-embed-text && cargo run --release -p mnemo-locomo-bench --bin semantic_recall_benchThe runner refuses to emit a score under a no-op embedder.
And the memory-poisoning defense is measured on a real embedder, not asserted.
Through a real ONNX MiniLM embedder (not a hash stand-in), mnemo's always-on
lexical / self-referential lane drops canonical MINJA ASR 100% β 0% at
0/300 benign false-quarantine. Honestly, the opt-in embedding z-score lane
does not generalise to a dense embedder: on MiniLM, poison lands ~1.5Ο from
the benign mean (below the 3Ο gate), so marker-stripped and consolidation-style
redirects survive (ASR 100%) β a limitation we publish rather than hide,
correcting the hash-embedder bench's rosier z-score reading. ASR + Wilson-95 +
benign-FPR per attack, refuse-to-score-on-noop:
docs/BENCH_POISONING.md
Non-adaptive Phase-3 exploitation, against a digest-pinned real embedder
(#37): a poisoned record that
is already Phase-2-shortened is exploited at 0.956 [Wilson 95% 0.906, 0.980],
129/135 trials (45 distinct victim queries Γ 3 seeds; conservative interval at
the distinct-query denominator [0.852, 0.988], n=45). With the detector on:
0.956 [0.906, 0.980], 129/135 β defense delta 0.0000, and 0/135
records quarantined at a mean z of 1.08 against the 3.0 gate. The matched
benign twin is retrieved at 0.956 [0.906, 0.980], 129/135, so the
poisoning delta is +0.0000: the rate is topical retrieval, not poisoning.
This is not a MINJA number β Phases 1 and 2 are generative and adaptive and
are not implemented, and it says nothing about an adaptive attacker.
docs/benchmarks/2026-08-25-minja-phase3-nonadaptive.md
Β· bench/results/minja_phase3.json
(cargo run --release --features onnx -p mnemo-poisoning-bench --bin poisoning_real_bench).
This bench is also --features onnx, and like the ONNX MiniLM retrieval number
above it is CI-reproducible: the same
real-embedder-benches-nightly
job regenerates it against the pinned checkpoint and reproduced every rate exactly,
including the canonical-MINJA 1.000 β 0.000 defense delta. The ort integration
it used to be blocked on was repaired in
#125 (closed 2026-08-04), and
ci.yml's onnx-feature job has built, linked and run end-to-end ONNX inference on
every push since. The byte-stable hash-embedder defense-delta companion also runs
in CI and is at bench/poisoning/.
Quarantine is only half the story β the other half is the auditable layer,
mnemo's real wedge for OWASP ASI06 β Memory & Context Poisoning
(whose recommended control is provenance on every write + evaluation against
ground truth). Against three ASI06 attack families β contradictory-fact
overwrite, authority-spoofed origin + provenance forgery, belief-drift
splice/back-date β the SHA-256 hash-chain + read-provenance HMAC layer rejects
100% of poisoning cover-up/forgery attempts (1500/1500, Wilson 95% [99.7%,
100.0%]) at 0% benign false-positive (0/300, [0.0%, 1.3%]); a naive store
with no crypto layer catches 0%. Read honestly: this is tamper-evidence +
attribution β poisoning cannot be hidden β not write-time prevention. Method,
numbers, and the LoCoMo ground-truth-quality caveat:
docs/benchmarks/asi06-poisoning.md
(cargo run --release -p mnemo-asi06-poisoning-bench --bin asi06_poisoning).
That ASI06-recommended control β provenance on every write β is now the
default path: every remember/share records who wrote it, under what
capability, in what session, hash-chained and revocable by principal (see
Write provenance & FORGET BY PROVENANCE).
The compositional ("Salami") case β the harder shape where each of N writes
is individually benign but their union is harmful (arXiv:2608.01637)
β is measured, not defended, at bench/salami_poisoning/:
individually-benign slices sail through the write path (high save rate) and a
single trigger recall reliably reconstructs the harmful composition (high
retrieval-influence rate), while a topic-matched benign control co-retrieves
yet never completes the harm (~0). Both rates carry a Wilson-95 interval;
deterministic + offline. This is the compositional subset of
#37 β a quantified gap, not a
claimed defense (cargo run --release -p mnemo-salami-poisoning-bench).
Regulatory mappings (honest, hedged, not legal advice): EU AI Act Art.12 Β· India DPDP (Rules 2025) Β· ASI06 memory-poisoning resistance. Apache-2.0, no SaaS.
The 0.5.x compliance line is on crates.io β the minimal set to run on-prem, hash-chain-audited memory in your own Rust service:
cargo add mnemo-core mnemo-compliance # engine + audit-log/consent primitives
cargo add mnemo-mcp # (optional) expose it as MCP tools
cargo install mnemo-mcp-server # server binary β `mnemo`What these commands resolve is in the generated table below, which is the authority. No version is restated here: a hand-written copy of a generated number is a copy that drifts, and this paragraph did β for four days after v0.5.28 reached crates.io it still described that version as pending.
The seven satellite crates that trailed a patch during the
v0.5.25window have caught up, and they are in the tag walk as ofv0.5.26, so the split that caused it cannot recur: see Naming.Three guards keep this honest instead of stale: the test
crates/mnemo-cli/tests/readme_crates_version_matches_workspace.rsfails if the stated release, or any bare current-band0.5.2xrelease literal anywhere in this README (thev-prefixed feature history is exempt), drifts from the workspace[workspace.package].version;scripts/check_version_drift.shfails if crates.io falls more than one patch behind the workspace; andscripts/check_publish_closure.shfails if a publishable crate is missing from the release closure, which is what let a crate fall behind silently in the first place.
Workspace [workspace.package].version (unreleased target): v0.5.30. The Rust library line and the Python SDK both track the workspace (the wheel compiles mnemo-core into itself, so its version names the engine inside it). Only the TypeScript SDK versions independently. Published, per registry:
| Registry | Artifact | Published version | Published |
|---|---|---|---|
| crates.io | mnemo-core β engine + hash-chain verify |
v0.5.29 |
2026-09-04 |
| crates.io | mnemo-mcp-server β the mnemo server binary |
v0.5.29 |
2026-09-04 |
| crates.io | mnemo-embeddings-bench β bench crate the server binary depends on |
v0.5.29 |
2026-09-04 |
| PyPI | mnemo-db β Python SDK (tracks the workspace) |
v0.5.30 |
2026-09-06 |
| npm | @mndfreek/mnemo-sdk β TypeScript SDK (independent) |
v0.4.4 |
2026-05-18 |
Table generated from the live registries by scripts/gen_published_versions.py; scripts/registry_parity.sh fails a release if these drift from what the release actually published.
This project does not own the mnemo name on crates.io, and does not publish a
crate called mnemo. Everything it publishes carries the mnemo-* prefix.
Two unrelated crates sit on names you might otherwise reach for:
| crates.io name | Who owns it | What it is |
|---|---|---|
mnemo |
aayushadhikari7/mnemo | "A personal knowledge vault for your terminal" β not this project |
mnemo-cli |
watzon/mnemo | "CLI management tool for the Mnemo LLM memory proxy" β not this project |
mnemo-cli is the easier mistake: this repo's server binary lives in the
directory crates/mnemo-cli, but it publishes as mnemo-mcp-server. The
directory name is not the crate name.
The server binary is:
cargo install mnemo-mcp-server # installs the `mnemo` executableThe mnemo command this installs is ours; the mnemo crate is not.
mnemo-db on crates.io is a defensive
name-reservation pointer that ships no code and redirects here β distinct from
the PyPI mnemo-db package, which is the real Python SDK (see
Python below).
scripts/check_crate_name_refs.sh fails CI if a bare cargo install mnemo or
cargo add mnemo (or the mnemo-cli equivalents) reappears anywhere in the
docs.
Installing the right name is only half of it: cargo install resolves whatever crates.io actually has. Of the 21 published mnemo-* crates, 21 are not yet on the workspace version v0.5.30: mnemo-admin (0.5.29), mnemo-amp (0.5.29), mnemo-attention-state (0.5.29), mnemo-baseline (0.5.29), mnemo-cma (0.5.29), mnemo-codemode (0.5.29), mnemo-compliance (0.5.29), mnemo-core (0.5.29), mnemo-db (0.5.29), mnemo-deal (0.5.29), mnemo-embeddings-bench (0.5.29), mnemo-graph (0.5.29), mnemo-grpc (0.5.29), mnemo-letta (0.5.29), mnemo-mcp (0.5.29), mnemo-mcp-server (0.5.29), mnemo-md-sync (0.5.29), mnemo-mesh (0.5.29), mnemo-pgwire (0.5.29), mnemo-postgres (0.5.29), mnemo-rest (0.5.29). That is either a release in flight or a stranded crate; scripts/check_version_drift.sh distinguishes the two by naming the crates rather than reporting a total.
The 21 are mnemo-admin, mnemo-amp, mnemo-attention-state, mnemo-baseline, mnemo-cma, mnemo-codemode, mnemo-compliance, mnemo-core, mnemo-db, mnemo-deal, mnemo-embeddings-bench, mnemo-graph, mnemo-grpc, mnemo-letta, mnemo-mcp, mnemo-mcp-server, mnemo-md-sync, mnemo-mesh, mnemo-pgwire, mnemo-postgres and mnemo-rest.
One of them, mnemo-db, ships no code: it is a defensive name reservation whose entire contents are a doc comment pointing at mnemo-core and mnemo-mcp. It is counted because it is a real published artifact someone can cargo add, and they should learn that from the count rather than from an empty crate.
Count and list generated from cargo metadata (publishable workspace members, less those with a written never-published exemption) and checked against the live registry by scripts/gen_published_versions.py.
Other mnemo* names on crates.io (mnemo, mnemo-cli, mnemo-engine,
mnemo-rs, mnemo-server) belong to unrelated projects. See
Naming.
This was not true a day earlier, and the history is worth keeping because two guards were changed on account of it.
mnemo-mcp-server had stranded at v0.5.23, skipping v0.5.24 entirely,
because the tag lane that publishes it could not resolve mnemo-admin: the
publish closure was written down three times and the copies drifted apart. There
is now one WALK definition that the gate, the packaging dry-run and the publish
loop all expand. Its version history on crates.io still shows the gap:
v0.5.25, v0.5.23, v0.4.4.
Seven satellite crates (mnemo-letta, mnemo-mesh, mnemo-codemode,
mnemo-deal, mnemo-md-sync, mnemo-cma, mnemo-baseline) then sat a patch
behind, because they were not in the tag walk at all and published only on the
push-to-main lane, which had stopped partway through the release. They have since
caught up, but "it resolved itself" is not a fix: two lanes with different
contents is the condition that lets a crate fall behind with nothing going
red, which is the confusion the drift guard exists to prevent rather than
describe.
So the split is closed rather than waited out. All seven are in WALK as of
v0.5.26 (they depend on mnemo-core alone and nothing depends on them, so
folding them in reorders nothing), and
scripts/check_publish_closure.sh now
asserts the general form in CI: every publishable workspace member must appear in
the closure or carry a written exemption. A new crate cannot be orphaned by being
left out of a list.
scripts/check_version_drift.sh treats one
patch behind an unreleased workspace as a publish in flight: green, but it
names the crates rather than reporting "every published crate matches
workspace", which it used to say while seven of them did not.
The summary table above is written by hand and is a narrative of one moment. The
authoritative, per-registry numbers are in
the generated table under Install from crates.io, which is
generated from the live registries by
scripts/gen_published_versions.py and
re-checked by the doc-guards CI job, so a stale table there fails the build
rather than quietly misinforming a reader.
The wedge β a memory-write log an auditor can verify offline, without trusting
the store β is the mnemo-core
hash-chain verify API:
use mnemo_core::hash::{verify_chain, ChainVerificationResult};
// `records` is the exported, hash-chained write log. The store is never
// consulted β the verifier is a pure function an auditor runs on their machine.
let result: ChainVerificationResult = verify_chain(&records);
assert!(result.valid); // false + `first_broken_at` names any post-hoc mutationSigned NDJSON export and DPDPA consent records live in
mnemo-compliance
(export_audit_log, verify_ndjson_signed). Why this matters, and how mnemo
compares to Mem0 / Letta / native provider memory on the compliance-audit axis:
docs/POSITIONING.md.
cargo build --releaseAdd to your MCP client configuration (e.g. Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"mnemo": {
"command": "./target/release/mnemo",
"args": ["--db-path", "./agent.mnemo.db"],
"env": {
"OPENAI_API_KEY": "sk-..."
}
}
}
}Your AI agent now has persistent memory with 21 MCP tools:
| Tool | Description |
|---|---|
mnemo.remember |
Store a new memory with semantic embeddings |
mnemo.recall |
Search memories by semantic similarity, keywords, or hybrid |
mnemo.forget |
Delete memories (soft delete, hard delete, decay, consolidate, archive) |
mnemo.forget_subject |
GDPR / DPDPA subject erasure: redact (default, preserves the hash chain) or hard-delete every memory tagged subject:<id> |
mnemo.share |
Share a memory with another agent |
mnemo.checkpoint |
Snapshot the current agent memory state |
mnemo.branch |
Create a branch from a checkpoint for experimentation |
mnemo.merge |
Merge a branch back into the main state |
mnemo.replay |
Replay events from a checkpoint |
mnemo.delegate |
Delegate scoped, time-bounded permissions to another agent |
mnemo.verify |
Verify SHA-256 hash chain integrity |
mnemo.trajectory_audit |
GEM-aligned trajectory-correctness audit (arXiv:2605.26252): unregulated growth, missing semantic revision, capacity-driven forgetting, read-only retrieval |
mnemo.consolidate |
v0.5.0 β Group related memories into one revisable topic document (Infini-Memory): collects members as evidence, preserves provenance, records a hash-chained audit event; pass supersede to revise a fact while keeping the old version in history |
mnemo.remember_plan |
v0.4.14 β Cache a successful retrieval/reasoning plan (query signature + steps + chunk ids + outcome score) into the experience-memory tier (DocTrace; only when the mode is enabled) |
mnemo.recall_plan |
v0.4.14 β Replay the best cached plan for a structurally-similar query instead of re-running full retrieval; RBAC-gated, returns a miss when nothing matches |
mnemo.attention_state.put |
v0.4.5 β Store an opaque attention-state blob keyed by (agent_id, prefix_hash) (anchored on arXiv:2605.18226; only registered when the server is built with MnemoServer::with_attention_state(...)) |
mnemo.attention_state.get |
v0.4.5 β Look up an attention-state blob by (agent_id, prefix_hash); returns null on miss |
mnemo.mem_write |
Agent-controlled memory (AutoMEM): persist an entry you explicitly decided to keep into your flat, agent-managed store |
mnemo.mem_read |
Agent-controlled memory (AutoMEM): read back only your own agent-managed entries |
mnemo.mem_revise |
Agent-controlled memory (AutoMEM): supersede a stale agent-managed entry with a corrected one (newest wins) |
mnemo.mem_forget |
Agent-controlled memory (AutoMEM): drop an agent-managed entry (soft by default; hard=true for permanent) |
| Protocol | Crate | Use Case |
|---|---|---|
| MCP (stdio) | mnemo-mcp |
AI agent integration via rmcp 3.0 |
| REST (HTTP) | mnemo-rest |
Web clients, dashboards, OTLP ingest |
| gRPC | mnemo-grpc |
High-performance service-to-service (12 RPCs) |
| pgwire | mnemo-pgwire |
Connect with any PostgreSQL client (psql) |
| AMP (memorywire) | mnemo-amp |
AMP-conformant adapter: 5 ops Γ 4 memory types over a JSON-Schema 2020-12 envelope |
mnemo v0.4.5 ships an attention-state-memory substrate anchored on arXiv:2605.18226 (Context Memorization). Two new MCP tools β mnemo.attention_state.put and mnemo.attention_state.get β store and retrieve opaque attention-state blobs keyed by (agent_id, prefix_hash). The substrate is implemented in crates/mnemo-attention-state with a typed AttentionStateStore trait + an InMemoryAttentionStateStore reference impl.
Honest scope: mnemo ships the store. The producer (inference runtime that extracts prefix states) and the consumer (re-injection on the next generation) are out of scope; the substrate's blob format, model compatibility, and quantization sensitivity are the producer's responsibility. Tools are registered only when MnemoServer::with_attention_state(...) is configured at startup; unconfigured calls return a spec-shaped error result, not a panic. See the research anchor for the operator recipe + the explicit non-overclaim disclaimers.
arXiv:2605.18565 (MINTEval) measures how often memory systems return a superseded value of a fact after the same fact has been revised K times. mnemo v0.4.7 ships an opt-in current-fact resolver that post-processes the standard recall result set: candidates sharing the same value under a caller-chosen fact_key (typical convention: "fact_id") are grouped, and only the most-recent write per group is kept. When include_supersession_chain = true, older fact-versions are returned in the response's new superseded field for audit.
Enable via the MCP recall tool param current_fact_resolver: { fact_key, include_supersession_chain }, the REST ?current_fact_key=⦤t_fact_include_chain=true query params, or the Rust RecallRequest.current_fact_resolver = Some(...) field directly. The default read path is unchanged β the resolver is purely additive and opt-in. The MINTEval-shaped bench at bench/locomo/src/bin/interference.rs compares default vs resolver arms across K β {1, 3, 5, 10} revisions; see the resolver module doc at crates/mnemo-core/src/query/current_fact_resolver.rs for the contract + the explicit "not a contradiction detector / not a write-side guard" disclaimers.
arXiv:2605.19932 (PEEK β Prefix-Encoded Episodic Knowledge) shows that a small, token-budgeted "orientation map" maintained alongside an agent's retrieval surface (key entities, UPPER_SNAKE constants, fenced schema fragments that have been useful) lets agents re-enter long-running contexts with a fraction of the recall payload. mnemo v0.4.8 ships an opt-in orientation cache that post-processes the standard recall result set: a heuristic Distiller extracts transferable knowledge from each hit and a priority Evictor enforces a fixed token budget (default 512). The recall response carries the bounded rendered map alongside top-k so the caller has both what is in scope and what is relevant right now in one payload.
Enable via the MCP recall tool param orientation_cache: { namespace?, token_budget?, include_in_response?, distill? }, the REST ?orientation_cache=true&orientation_namespace=β¦&orientation_token_budget=β¦ query params, the gRPC OrientationCacheRequest message, the pgwire /*+ orientation_cache */ SQL hint comment, or the Rust RecallRequest.orientation_cache = Some(OrientationCacheConfig::new()) field directly. The store is in-process, namespace-scoped ((org_id, agent_id) by default), and lost on restart β persistence is a v0.5.x knob. The default read path is unchanged and orientation rendering only fires when both the caller passes a config AND the engine has an OrientationCacheStore attached via MnemoEngine::with_orientation_cache_store(). See the PEEK-anchored bench at bench/locomo/src/bin/orientation.rs for the repeated-context scenario (K β {3, 6, 10, 15} calls per trial) and the module doc at crates/mnemo-core/src/query/orientation_cache.rs for the contract + the explicit "not a learned summariser / not a context-window extender / not persisted" disclaimers.
The default recall path front-loads: it returns the top-limit records by fused score, and an LLM caller pays context tokens for every chunk whether or not it changes the answer. mnemo v0.4.12 ships an opt-in cost-aware evidence budget that runs over the already-ranked candidate set and returns the smallest prefix that clears a configurable sufficiency bar, capped by an optional max_evidence. It is purely subtractive β it only ever returns a prefix of the ranked list, so it can never reorder or silently lower the retrieval's top-k cosine ordering (enforced by a property test).
Relevance is computed through a pluggable EvidenceScorer trait so callers can swap the signal used to decide sufficiency: CosineScorer (default β cosine of candidate vs query embedding, falling back to the fused retrieval score when embeddings are absent/degenerate) and DeltaScorer β an answer-impact scorer that rates a chunk by whether adding it to the evidence already selected would change a downstream answer. The "would the answer change?" judgement is an injectable closure (DeltaScorer::new(|ctx| β¦)) so the engine core stays model-agnostic; DeltaScorer::stub() ships a deterministic marginal-novelty heuristic for tests and offline use.
Enable via the Rust RecallRequest.evidence_budget = Some(EvidenceBudget { max_evidence, stop_when_sufficient, sufficiency_threshold, scorer }) field. Attach a custom answer-impact scorer with MnemoEngine::with_evidence_scorer(Arc<dyn EvidenceScorer>); when the budget selects ScorerKind::Delta but no scorer is attached, the path falls back to cosine rather than erroring. The recall response carries an evidence_selection diagnostics block (scorer name, examined vs returned counts, cumulative score, early-stop / capped flags). The default read path is unchanged β no evidence_budget means the legacy front-loaded top-limit. See the module doc + property test at crates/mnemo-core/src/query/evidence.rs and the end-to-end integration test at crates/mnemo-core/tests/cost_aware_recall.rs.
Where the v0.4.12 evidence budget caps how many records come back, an agent that must keep evidence resident in a bounded context window has a different problem: raw chunk dumps burn the window fast β a handful of full records and it's full, even though most of each record is filler around one salient fact. EMBER (arXiv:2606.05894) frames this as a writer problem: under a fixed retained-token budget, keep compact, recoverable evidence rather than raw text.
mnemo extends the verified recall surface with an opt-in RecallRequest.retained_token_budget: Option<usize>. When set, the engine packs the recalled hits into at most budget tokens as verbatim evidence capsules β a short verbatim excerpt plus a retrieval key (the record id, so the caller re-fetches the full chunk on demand) β ranked by a v0 recoverability heuristic (recency Γ retrieval-hit-rate) standing in for EMBER's learned writer. It is purely additive: the memories list is unchanged, and the capsule view rides in RecallResponse.retained_evidence. No new enum, no protocol change β just the one parameter on the existing surface.
The eval at crates/mnemo-core/tests/budgeted_evidence_retention.rs measures the knob's value on a LongMemEval-style fixture (60 gold facts, each behind a noisy chunk) at a fixed 8192-token budget β budgeted capsules vs naive truncation:
| arm | covered | recall@budget | retained tokens |
|-------------------|--------:|--------------:|----------------:|
| naive truncation | 45 | 0.750 | n/a |
| budgeted capsules | 60 | 1.000 | 4380 |
Because each capsule costs a fraction of a raw chunk, every gold fact survives the budget (recall 1.000 vs 0.750) using barely half the tokens. See the module doc + tests at crates/mnemo-core/src/query/retained.rs for the cost model + the full "what this is NOT" block (v0 heuristic, not EMBER's learned writer; read-side projection, nothing mutated).
Mnemo's default path is an ingestion + retrieval pipeline: content is written, indexed, and recalled by vector + BM25 + graph + RRF. AutoMEM (arXiv:2606.04315) frames a crossover: the fixed pipeline wins single-shot retrieval (it ingested everything, so the fact is in the index), but on long-horizon, multi-session workloads an agent that controls its own writes over a simple flat store can win, because it revises stale facts in place instead of letting every version pile up and pollute retrieval.
So mnemo adds an opt-in agent-controlled memory mode over the MCP tool surface β four tools the agent itself calls to manage a flat store it curates:
| MCP tool | What the agent does | Composed from |
|---|---|---|
mnemo.mem_write |
persist an entry it judged worth keeping | remember (+ reserved agent-managed tag) |
mnemo.mem_read |
read back its own flat store (tag-scoped) | recall filtered to agent-managed |
mnemo.mem_revise |
supersede a stale entry with a corrected one | soft-forget old + remember new (metadata.revises) |
mnemo.mem_forget |
drop an entry it no longer wants | forget (soft / hard) |
These are thin compositions over the verified remember / recall / forget primitives plus a reserved agent-managed tag β no new engine enum or method. The default mnemo.recall pipeline is untouched and remains the fallback for single-shot queries. The point is write-control: the agent (not an ingestion heuristic) decides what persists.
The crossover eval at crates/mnemo-core/tests/agent_managed_crossover.rs measures both directions on a multi-session fixture (12 tracked facts Γ 3 revisions + 12 incidental details), holding retrieval to BM25 so the measured variable is purely write-control:
| query family | mode | recall@k | precision | F1 |
|---------------------------|----------------|---------:|----------:|------:|
| long-horizon current-fact | fixed-pipeline | 1.000 | 0.333 | 0.500 |
| long-horizon current-fact | agent-managed | 1.000 | 1.000 | 1.000 |
| single-shot incidental | fixed-pipeline | 1.000 | 1.000 | 1.000 |
| single-shot incidental | agent-managed | 0.000 | 0.000 | 0.000 |
The pipeline still wins single-shot retrieval (recall 1.000 vs 0.000 β it ingested the incidental details the agent chose to skip); the agent-managed path wins long-horizon write-control (current-fact F1 1.000 vs 0.500 β it revised in place, so no stale versions dilute precision). The agent-managed mode is for long-horizon write-control, not a replacement for single-shot retrieval. See the tool inputs at crates/mnemo-mcp/src/tools/agent_managed.rs.
The four primitives above (REMEMBER / RECALL / FORGET / SHARE) operate on tier 1: the raw memory store. DocTrace (arXiv:2606.10921) adds a tier 2 β an experience memory that caches a successful retrieval/reasoning plan and replays it when a structurally-similar query recurs, instead of re-running full retrieval. mnemo ships this as a mode, not a new store: two new ops on the existing engine surface, gated behind MnemoEngine::with_experience_memory() (or the MNEMO_EXPERIENCE_MEMORY=1 env on the server) so default behaviour is unchanged.
| Op | MCP tool | What it does |
|---|---|---|
REMEMBER_PLAN |
mnemo.remember_plan |
Persist {query-signature, steps, chunk ids, outcome score} β only when the outcome clears the success threshold (failures are never cached). |
RECALL_PLAN |
mnemo.recall_plan |
On a new query, return the best stored plan whose signature matches above a Jaccard threshold (default 0.7) β the cached chunk-set + step order β else a miss. |
Plans are persisted as ordinary MemoryRecords carrying a reserved tag with the plan payload in metadata, so the tier is backend-agnostic (DuckDB + PostgreSQL, unchanged schema) and RBAC/consent-gated exactly like every other record (private plans are invisible to other agents; shared plans honour the ACL). A query signature is its normalized significant-token set; structural similarity is the Jaccard overlap of two signatures β deterministic and embedder-agnostic for the v0 replay gate. Plan records are excluded from ordinary recall (replayed only via RECALL_PLAN). See crates/mnemo-core/src/query/experience.rs and the contract tests at crates/mnemo-core/tests/experience_memory.rs (store-on-success, replay-on-similar, no-replay-on-dissimilar, RBAC, mode-off).
RetrievalMode::DomainScoped adds a recall mode that pre-filters to a metadata-defined sub-corpus before the dense similarity step, then runs a single vector pass. The predicate rides on an additive, optional RecallRequest.domain_scope kwarg (a DomainScope { org_id, namespace, doc_class, tags }) β no breaking change to existing callers, and the legacy strategy / typed mode paths are untouched. Over MCP, pass a domain_scope object to mnemo.recall (named domain_scope, not scope, because scope already filters visibility):
Why scoping beats more-documents. Dense retrieval ranks by semantic similarity, and similarity is not domain-awareness: a query about one tenant's contracts is highly similar to every other tenant's contracts too. As the corpus grows, those off-domain-but-on-topic records crowd into the top-k and push the genuinely relevant ones out β so adding documents makes precision worse, not better. Restricting the candidate set to the right sub-corpus before the vector search removes the dilutants entirely, so precision is independent of how large the rest of the corpus gets. The dilution eval at crates/mnemo-core/tests/domain_scoped_dilution.rs reproduces the curve on a corpus growing 50 β 1,000 docs (10 fixed in-domain gold docs, the rest same-topic off-domain distractors):
| corpus | flat P@10 | domain-scoped P@10 | gap |
|-------:|----------:|-------------------:|-------:|
| 50 | 0.100 | 1.000 | +0.900 |
| 200 | 0.000 | 1.000 | +1.000 |
| 1000 | 0.000 | 1.000 | +1.000 |
Flat semantic recall collapses to 0.000 P@10 by 1,000 docs; domain-scoped holds at 1.000. The metadata predicate is enforced through the existing storage layer, so it works on both the DuckDB and PostgreSQL backends and respects RBAC/scope exactly like ordinary recall.
strategy = "reconstruct" (typed: RetrievalMode::Reconstruct) adds an opt-in recall strategy that, instead of returning only the top-k snippets, (a) retrieves candidates with the default hybrid RRF, (b) walks the existing memory-graph edges from those hits to gather linked/causal context, and (c) synthesises a deterministic belief-state node returned alongside the raw hits β MRAgent's cue β linked-context β reconstruct pattern. It is surfaced as a strategy parameter on the existing recall API across all four protocols (MCP strategy: "reconstruct", REST ?strategy=reconstruct, gRPC RecallRequest.strategy, pgwire SELECT ... /*+ reconstruct */), so the tool surface is unchanged.
{ "query": "Project Apollo owner", "strategy": "reconstruct" }
// β { "memories": [ ...the usual top-k... ],
// "reconstruction": { "cue": "...", "summary": "Reconstructed belief ...",
// "source_ids": [...], "linked_context_ids": [...], "confidence": 0.x } }The raw memories list is exactly what the default auto path returns β reconstruct is purely additive (the belief node is extra), and the synthesis is rule-based (no LLM), so it is deterministic and reproducible. This is an option to test reconstruction-vs-retrieval on your own data, not a claim that retrieval is wrong. MRAgent reports up to ~23% gains on multi-hop questions; the A/B harness at bench/locomo/src/bin/reconstruct_ab.rs lets you check the mechanism on mnemo itself by measuring gold-coverage@k β whether a graph-linked answer that flat top-k retrieval misses is recovered by the reconstruction's linked context. On its (adversarially multi-hop, by-construction) fixture, reconstruct lifts coverage@5 from 0.083 β 0.208; run it on your own corpus before drawing conclusions. The graph walk uses the same related_to relations the graph strategy traverses, so it works on both the DuckDB and PostgreSQL backends and respects RBAC/scope like ordinary recall.
Anthropic's Auto-Dreamer consolidation runs offline, away from the agent's interactive loop, and produces a smaller active bank of semantic summaries that should serve subsequent recall at least as well as the raw episodic trace it replaced. mnemo's existing run_decay_pass + run_consolidation path (crates/mnemo-core/src/query/lifecycle.rs, plus the reflection module at crates/mnemo-core/src/query/reflection.rs that mirrors the same offline-housekeeping shape) is the engine-side equivalent: the decay pass marks low effective-importance records as Archived / Forgotten, the consolidation pass replaces tag-overlap clusters of episodic memories with structured [Consolidated from N memories] β¦ semantic bundles, and the originals are flipped to Consolidated rather than deleted (so the chain stays auditable). The new Auto-Dreamer-shaped scenario at bench/locomo/src/bin/auto_dreamer_consolidation.rs exercises both passes end-to-end on a synthetic multi-session trajectory and reports the two axes Auto-Dreamer headlines as its claim: active_bank_ratio = post / pre (expects < 1.0) and held-out recall_post >= recall_pre. A JSON summary lands beside the Markdown report so the headline number is citable here.
mnemo.consolidate / MnemoEngine::consolidate adds a caller-driven consolidation primitive: it groups a chosen set of member memories into one revisable topic document β a semantic unit that collects related evidence, preserves metadata, and revises facts over time. This is the interactive, by-id sibling of the offline run_consolidation pass above: instead of clustering by tag overlap on a schedule, the caller names the members and the topic.
{ "memory_ids": ["<id1>", "<id2>", "<id3>"], "topic_name": "Acme account" }The primitive is deterministic (no LLM): absent a caller summary, the document body is a stable join of the member contents ordered by (created_at, id). It preserves provenance β the topic document's metadata records consolidated_from plus each member's source, timestamp, and confidence β and links topic_document --consolidated_from--> member relations so the evidence set is retrievable as a unit. Every consolidation appends a hash-chained MemoryConsolidated audit event.
Fact revision keeps history. Pass supersede: <old_topic_document_id> to revise a fact: the new document becomes version N+1 (prev_version_id β old), the old document is retained (not deleted β deleting a mid-chain record would break verify_integrity) and marked Consolidated with a superseded_by pointer, and a MemoryRevised event is appended alongside. Reuse the same topic_name across revisions so the current-fact resolver (keyed on the topic metadata) collapses to the current view at recall time. The primitive is protocol-agnostic β reachable identically over MCP, REST (POST /v1/consolidate), and gRPC (rpc Consolidate). It is not exposed over pgwire, which stays SQL-only (SELECT/INSERT/DELETE β recall/remember/forget); consolidation is a primitive-RPC operation, not a SQL statement.
Run via cargo run --release --bin auto_dreamer_consolidation -p mnemo-locomo-bench β defaults to 8 sessions Γ 25 facts Γ 5 trials with archive/forget thresholds of 0.40 / 0.10 and min_cluster_size = 3; all tunable via CLI flags. The default read path is unchanged β the bench only consumes existing mnemo_core::query::lifecycle::* APIs and adds no public surface. See the bin module rustdoc for the full "what this bin is NOT" block (not a faithful Auto-Dreamer reproduction; not the criterion crate; NoopEmbedding makes the vector lane degenerate by design; single-agent, single-scope).
arXiv:2605.23618 (GE2 vs local encoders β quality + latency) motivates choosing an embedding backend by measured quality and tail-latency on the operator's workload, not by reputation. mnemo v0.4.9 ships bench/embeddings, a criterion-driven bench + SLA-aware recommender that runs each configured backend (Noop and a bench-local hashing baseline always; OpenAiEmbedding when OPENAI_API_KEY is set; OnnxEmbedding when MNEMO_ONNX_MODEL_PATH is set and mnemo-core was built with the onnx feature) against a 50-document / 10-query labeled fixture and reports nDCG@10, recall@10, p50/p95 single-vector embed latency, and throughput at batch sizes 1/8/32. The recommender then picks the highest-nDCG backend whose p95 β€ the SLO and reports the explicit nDCG gap vs the absolute best-quality backend (so the operator sees "you give up 0.003 nDCG for 7x lower p95 latency" rather than a black-box choice).
Run via mnemo bench embeddings --slo-ms <N> (built into the mnemo binary) or cargo bench -p mnemo-embeddings-bench (criterion HTML reports at target/criterion/embed_single/). The default read path is unchanged β no retrieval defaults, no RRF weights, no EmbeddingProvider impls are touched. The embedded-first wedge stays: default builds run without OPENAI_API_KEY and the recommender picks a local backend. See bench/embeddings/README.md for the full "what this bench is NOT" block (not a faithful arXiv:2605.23618 reproduction; not a managed-cloud default; hashing-baseline is a bench-local lexical sanity floor, not a production backend).
mnemo v0.4.6 ships a vertical-slice WASM-component implementation of the golem:vector@1.0.0 WIT interface β three load-bearing functions (upsert-vector / search-vectors / delete-vectors) β split across two crates: crates/mnemo-golem-wit (the WASM component, compiled to wasm32-wasip2 via cargo component build) and crates/mnemo-golem-host (the Rust host that owns an Arc<MnemoEngine> and supplies the WIT host imports). The two-crate split is forced by mnemo-core's C++ deps (DuckDB + USearch) which cannot compile to WASM β see docs/research/golem-vector-wit-provider.md for the layering rationale, the per-function gap list (27 of 30 deferred to v0.5.x), and the wasmtime-component-loader wiring step explicitly deferred. The vertical-slice integration is functionally complete as a Rust trait surface today (MnemoGolemProvider + MnemoGolemHost) with 5 integration tests + an end-to-end example showing REMEMBER β RECALL β DELETE through a real MnemoEngine.
The current spec revision is 2026-07-28, which removes
protocol-level sessions and the initialize handshake, adds server/discover,
requires Mcp-Method / Mcp-Name headers and ttlMs / cacheScope on list
results, and deprecates Roots, Sampling, Logging and the HTTP+SSE transport.
mnemo negotiates 2025-11-25, not 2026-07-28. mnemo speaks MCP through
the rmcp = "3.0" workspace dep (CI resolves 3.5.0 as of 2026-10-03),
and follows rmcp's implementation as it lands rather than racing the spec.
rmcp 3.5.0 made 2026-07-28 its ProtocolVersion::LATEST, but mnemo
advertises nothing past 2025-11-25 and 2026-07-28 has no handshake, so
2025-11-25 is what a handshake settles on.
Where mnemo already satisfies the new revision it is because of decisions taken
earlier, not because it tracked the spec. Cross-call state has always travelled
through explicit, server-minted handles passed as ordinary tool arguments
(checkpoint_id, lease_token), which SEP-2567 makes the sanctioned
replacement for sessions; mnemo implements none of the deprecated features; and
tool listings are already deterministically ordered.
Row by row, with what is still open and what waits on rmcp: docs/src/integrations/mcp-2026-07-28.md. It is not a marketing page. Four rows are still open, every one of them carrying a sentence saying what a caller should assume instead, and a CI test refuses to let an open row exist without one. It also states plainly that mnemo implements no OAuth authorization: no RFC 9728 Protected Resource Metadata document is served on any transport, which is a conformant position on stdio (the spec says stdio servers SHOULD NOT follow the authorization spec and should take credentials from the environment, which is what mnemo does) and a thing worth saying out loud rather than leaving a reader to infer from silence.
Earlier anchor: the March 2026 MCP Roadmap
The MCP 2026 Roadmap (published 2026-03-09) organised the protocol's
direction around four priority areas: Transport Evolution and Scalability, Agent
Communication, Governance Maturation, and Enterprise Readiness. mnemo's
operator-held HMAC keystore, AES-256-GCM at-rest encryption, dual DuckDB /
PostgreSQL backends and mnemo-compliance crate sit under Enterprise
Readiness as an attestable memory layer.
This is kept as history. The July spec release superseded the roadmap as the
statement of current direction, and the conformance table above is where mnemo's
actual state is recorded. The four-priority-area mapping is preserved in
docs/src/integrations/mcp-server.md.
pip install mnemo-dbWhy
mnemo-dband notmnemo? A 2021 notebook project (last release 2021-07-06, unrelated) holds the unqualifiedmnemoname on PyPI. Our distribution publishes asmnemo-db; the import path staysfrom mnemo import β¦so existing code is unaffected.
Version line & wire compatibility.
pip install mnemo-dbgivesv0.5.30. The Python SDK is not independently versioned:python/is PyO3 bindings that compilemnemo-coreinto the wheel, so the wheel version names the engine inside it, andworkspace_version_fence.rsfails CI ifpyproject.tomlandmnemo/__init__.pydrift from[workspace.package].version.
- In-process,
MnemoClient(the PyO3 extension).mnemo-dbv0.5.30ismnemo-corev0.5.30. There is no version-skew question to answer: the engine is the wheel.pip install mnemo-dbandcargo add mnemo-coredo not currently resolve the same version. PyPI hasv0.5.30; crates.io hasv0.5.29. The wheel publishes on merge tomainwhile the crates publish on a tag, so the Python side leads inside an open release window. Pin deliberately if you embed both.- Over MCP, the
agno/camel/agno-memoryadapters. These embed no engine; they spawn the externalmnemoserver binary you install and bind to its MCP tool surface (the 23 registered tools), not to amnemo-coreversion. They are wire-compatible with any 0.5.xmnemo-mcp-server. Server properties such as the rmcp 3.0 transport and the tool-catalog attestation come from that binary, not from the SDK, so run a current one to get them.
from mnemo import MnemoClient
client = MnemoClient(db_path="agent.mnemo.db")
result = client.remember("The user prefers dark mode", tags=["preference"])
memories = client.recall("user preferences", limit=5)
client.forget([result["id"]])
# Mem0-compatible aliases also available:
# client.add(), client.search(), client.delete()Mnemo provides native integration modules for 15 agent frameworks:
| Framework | Integration Class | Connection |
|---|---|---|
| OpenAI Agents SDK | MnemoAgentMemory |
MCP stdio |
| LangGraph | MnemoLangGraphTools |
MCP stdio |
| CrewAI | ASMDMemory |
Direct PyO3 |
| Google ADK | MnemoADKToolset |
MCP stdio |
| Agno | MnemoAgnoTools |
MCP stdio |
| Pydantic AI | MnemoPydanticToolset |
MCP stdio |
| AutoGen | MnemoAutoGenWorkbench |
MCP stdio |
| Smolagents | MnemoSmolagentsTools |
MCP stdio |
| Strands Agents | MnemoStrandsClient |
MCP stdio |
| Semantic Kernel | MnemoSKPlugin |
MCP stdio |
| Llama Stack | register_mnemo_toolgroup |
REST API |
| DSPy | create_mnemo_tools |
Direct PyO3 |
| CAMEL AI | create_mnemo_camel_tools |
Direct PyO3 |
| Mem0 (compat) | Mem0Compat |
Direct PyO3 |
| LangGraph Checkpointer | ASMDCheckpointer |
Direct PyO3 |
All integrations are auto-imported via from mnemo import <ClassName> β dependencies fail gracefully if not installed.
| Surface | Class | What it does |
|---|---|---|
Anthropic memory tool memory_20250818 |
MnemoMemoryToolServer |
Client-side handler for the 6-op view/create/str_replace/insert/delete/rename surface β every "file" lands as a Mnemo memory with hash-chain + ACL coverage. pip install 'mnemo-db[anthropic-memory-tool]'. |
| Letta Conversations-style shared memory | MnemoLettaShared |
Multiple agents sharing a single audit-replayable memory stream. attach/detach/read/write/list_participants over Mnemo memories tagged conversation:<id> + participant:<agent_id>. |
| Cloudflare R2 workspace | CloudflareR2Workspace |
Drop-in R2 backend for MnemoSnapshotStore β same signed-manifest contract as the AWS S3 path; pip install 'mnemo-db[openai-sandbox-r2]'. |
Letta-protocol-compat REST surface (mnemo-letta crate) |
mnemo_letta::router(engine) |
POST /v1/agents, POST /v1/agents/{id}/messages, GET /v1/agents/{id}/memory β drop in front of any MnemoEngine so a Letta-Code-shaped benchmark or notebook can talk to Mnemo without code changes. New in v0.4.0-rc3 (B5). |
AMP / memorywire wire format (mnemo-amp crate) |
mnemo_amp::MnemoAmpStore + AmpRouter |
AMP-conformant MemoryStore surface: 5 ops (remember/recall/forget/merge/expire) Γ 4 memory types over a JSON-Schema 2020-12 envelope. merge/expire are thin compositions over the real primitives (no fictitious engine method); a fan-out router fuses multi-adapter recall with RRF; an optional HITL diff-and-approve hook gates long-term writes and records approvals in the hash-chained audit log. Conformance: recall@5 + RRF-holds-under-rank-0-injection vs max-fusion. New in v0.4.13. |
Mannsetu DPDPA consent manager (mnemo-compliance crate) |
MannsetuConsentSource + ConsentTokenGuard |
DPB-registered consent-manager binding plus a per-write guard with expiry / scope / revocation checks. Refuses any remember whose consent token is missing, expired, wrong-scope, or revoked. New in v0.4.0-rc3 (B4). |
| DPDPA "data passport" PDF | mnemo.dpdpa_passport.build_passport_pdf |
One-page PDF showing every personal data point Mnemo holds for a subject, suitable for Section 11 / 12 access requests. Hand-rolled PDF β zero third-party deps, byte-for-byte reproducible. New in v0.4.0-rc3 (Q3). |
| Provenance SDK | mnemo.provenance.verify_read_provenance |
Pure-Python verifier for the HMAC-SHA256 receipts that Mnemo returns alongside recall(..., with_provenance=True). Auditors verify offline without compiling Rust. New in v0.4.0-rc3 (Q1). |
| Claude Code installer | python -m mnemo install claude-code [--hardened <manifest>] |
Idempotently registers Mnemo as an MCP server in ~/.claude.json. The --hardened flag switches the registered launcher to the v0.4.0-rc3 hardened mode. New in v0.4.0-rc3 (Q2). |
Anthropic CMA-Memory compat shim (mnemo-cma crate) |
CmaTreeRoot + import_cma_tree + audit_bridge |
Drop-in for the Anthropic Context-Managed Agent Memory beta announced 2026-04-23. Mounts an existing CMA .memory/ tree, mirrors writes through to mnemo's HMAC chain, and exports back byte-identical so users can leave cleanly. New in v0.4.1 (P0-2). |
Agent behavioural-baseline exporter (mnemo-baseline crate) |
AgentBaseline + JsonExporter (OTel + OCSF) |
Per-agent rolling profile (recall/write rates, namespace fanout, tool mix, HMAC continuity) emitted to OpenTelemetry semconv 1.31 + OCSF 1.4 Application-Activity envelopes with z-score+EWMA drift detection. Anti-leak invariant: emitted payloads never carry memory contents. Plugs into the agentic-SOC telemetry gap RSAC 2026 flagged. New in v0.4.1 (P0-3). |
1M-context recall budget planner (mnemo-core::budget) |
ContextBudget::for_model + plan_recall |
First OSS memory store with an explicit per-model ContextBudget β RecallPlan planner. Per-model table covers deepseek-v4-1m, claude-3.7-sonnet-1m, gpt-5.1-400k, gemini-2.5-pro-2m plus their smaller siblings. Typed FallbackStrategy (TruncateOldest / SummarizeOldestK / DropDuplicates / None). Property test: never overflows total context. New in v0.4.1 (P1-4). |
Project-Deal counterparty discovery + reputation (mnemo-deal::discovery + ::reputation) |
AgentAdvertisement + compute_reputation |
/.well-known/mnemo-deal-agent.json advertisement (Ed25519-keyed, capability-tagged) plus an advisory reputation score with 90-day half-life decay and per-dispute 10% penalty. mnemo becomes not just the deal ledger but the directory of the agent-deal substrate. Advisory only β see docs/deal-reputation-threats.md. New in v0.4.1 (P1-5). |
Status: maintenance only, not on the 0.5 train.
npm install @mndfreek/mnemo-sdkgives 0.4.4 (published 2026-05-18) while the Rust line is on 0.5.x. It still works, because it is a thin MCP-over-STDIO client that targets the server's tool surface rather than amnemo-coreversion: the published 0.4.4 package was run against amnemo-mcp-serverbuilt fromv0.5.26, andremember,recallandverifyall succeeded with a valid hash chain. Publishing newer versions (package.jsonis at 0.4.8) is blocked on an expiredNPM_TOKEN, an operator action rather than a code change. What that verification does and does not cover, plus therecalldefault that will bite you first, is insdks/typescript/README.md.
import { MnemoClient } from "@mndfreek/mnemo-sdk";
const client = new MnemoClient({ dbPath: "agent.mnemo.db" });
await client.connect();
const { id } = await client.remember({ content: "User prefers dark mode" });
const { memories } = await client.recall({ query: "user preferences" });
await client.share({ memory_id: id, target_agent_id: "auditor-agent" });
await client.close();import "github.com/sattyamjjain/mnemo/sdks/go"
client, err := mnemo.NewClient(mnemo.ClientOptions{DbPath: "agent.mnemo.db"})
defer client.Close()
result, _ := client.Remember(mnemo.RememberInput{Content: "User prefers dark mode"})
memories, _ := client.Recall(mnemo.RecallInput{Query: "user preferences"})
_, _ = client.Share(mnemo.ShareInput{MemoryID: result.ID, TargetAgentID: "auditor-agent"})Two backends implement the same StorageBackend trait. As of v0.5.7 they are
feature-equivalent for recall β semantic / hybrid / graph / domain-scoped
recall now run real pgvector ANN on PostgreSQL (#99).
The matrix below is explicit about what each backend does.
| Backend | Best For |
|---|---|
| DuckDB (default) | Single-agent, embedded, zero-config β semantic/vector recall via USearch HNSW |
| PostgreSQL + pgvector | Multi-agent CRUD / ACL / audit at scale β plus semantic/vector recall via the pgvector HNSW index |
| Capability | DuckDB + USearch | PostgreSQL + pgvector |
|---|---|---|
Semantic / vector recall (strategy="semantic") |
β | β ΒΉ |
Hybrid RRF recall (strategy="auto") |
β | β ΒΉ |
Graph recall (strategy="graph") |
β | β ΒΉ |
Domain-scoped recall (strategy="domain_scoped") |
β | β ΒΉ |
Active-reconstruction recall (strategy="reconstruct") |
β | β ΒΉ |
Lexical / BM25 recall (strategy="lexical") |
β | β |
Exact / filter recall (strategy="exact") |
β | β |
| Remember / CRUD / soft+hard delete | β | β |
| ACL / sharing / delegation | β | β |
| Checkpoint / branch / merge / replay | β | β |
Hash-chain audit / verify |
β | β |
ΒΉ Postgres vector recall runs a real pgvector ANN query against the
idx_memories_embedding_hnsw HNSW index (cosine <=>, ORDER BY β¦ LIMIT k),
with the same permission-safe oversample-then-filter the USearch backend uses,
so filtered/scoped recall never under-returns. As of v0.5.18 the
VectorIndex::search / filtered_search methods are async (#99 resolved):
the sqlx query is .awaited directly on the caller's ambient Tokio runtime, so
Postgres semantic recall works from inside the server/CLI #[tokio::main]
runtime on any flavor β single- or multi-threaded β with no block_on
bridge and therefore no "runtime within a runtime" panic or deadlock. If the
pgvector extension / <=> operator is genuinely absent at runtime, the vector
lane still fails loud with a typed
Error::BackendUnsupported β never a silent
empty result. Verified end-to-end against a live pgvector Postgres by the
MNEMO_TEST_POSTGRES_URL-gated integration test
crates/mnemo-postgres/tests/pgvector_ann.rs:
nearest-in-rank-order + permission filter under a multi-threaded runtime, and
a current_thread-runtime regression test that the old bridge would have
panicked on.
That integration test skips when MNEMO_TEST_POSTGRES_URL is unset, so on an
ordinary CI run it proved nothing about the fail-loud claim.
crates/mnemo-postgres/tests/semantic_recall_fails_loud.rs
closes that hole: it needs no database, runs on every cargo test --workspace,
and asserts through the engine (not just the index) that semantic, auto,
graph and domain_scoped recall each return BackendUnsupported rather than
Ok(empty) β against a store that provably contains a record, so "no matches"
and "not implemented" cannot be confused. Re-introducing a single
.unwrap_or_default() on any of the four filtered_search call sites in
recall.rs turns it red.
Semantic recall is only as real as the embedder behind it. A backend's vector index cannot manufacture meaning from a query that was never embedded β so the supported semantic path is a real embedder + either backend, and mnemo now fails loud rather than silently returning empty when the embedder cannot produce vectors (v0.5.13).
| Embedder | How it is configured | Semantic / hybrid / auto / graph / domain-scoped recall |
Lexical / exact recall |
|---|---|---|---|
OpenAI (OpenAiEmbedding) |
OPENAI_API_KEY set |
β supported semantic path | β |
ONNX, local (OnnxEmbedding) |
MNEMO_ONNX_MODEL_PATH set + built with --features onnx |
β supported semantic path (fully on-prem) | β |
Deterministic (DeterministicEmbedding) |
in-process, offline | β works β but lexical-hashing, for tests/demos, not production semantics | β |
No-op (NoopEmbedding, the default when no key/model is configured) |
default | β hard-errors EmbedderNotConfigured |
β |
The supported semantic path is DuckDB (or PostgreSQL) + a real embedder β
OpenAiEmbedding (HTTP) or, for a fully on-prem deployment, OnnxEmbedding
(local model, onnx feature). With no real embedder configured, mnemo runs with
the no-op embedder, whose query vectors are all-zero; any semantic /
hybrid (auto) / graph / domain-scoped recall then returns a typed
Error::EmbedderNotConfigured β never a
silent empty result set. Lexical (BM25) and exact/filter recall need no
embedder and always work. This mirrors the Postgres BackendUnsupported
fail-loud behaviour above: mnemo will not pretend to do semantic recall it cannot
actually perform.
Same posture, applied to retention: mnemo's agent_events log is append-only
by construction (no forget / TTL / decay / cold-tier path removes an event β
each appends one), so mnemo-compliance
ships a RetentionProfile that
verifies a minimum retention floor held across every deletion path, and fails
loud (ComplianceError::RetentionFloorUnsupported, naming the backend) if a
backend cannot guarantee it. These are conformance checks for the obligation
named β not a certification or a claim of compliance.
| Profile | Obligation it maps to | Retention floor | Commencement | Primary source |
|---|---|---|---|---|
India DPDP Rules 2025 (dpdp) |
Retain personal data, traffic data and processing logs (Seventh Schedule) | 365 days | 2027-05-13 (data-fiduciary obligations; Gazette G.S.R. 846(E), 2025-11-13) | MeitY DPDP Rules 2025 |
EU AI Act Art.19 / 26(6) (eu-ai-act-art19) |
Deployers keep automatically-generated logs β₯ 6 months | 180 days | 2027-12-02 (stand-alone Annex III) / 2028-08-02 (Annex I embedded) per the Digital Omnibus | Reg (EU) 2024/1689 Β· Council, 2026-06-29 |
HIPAA Β§164.312(b) (hipaa) |
Audit controls; documentation retained six years (Β§164.316(b)(2)) | 2190 days (6 yr) | in force | eCFR 45 CFR Β§164.312 |
Run the reproducible conformance harness (every deletion path Γ the floor, byte-stable artifact):
cargo run --release -p mnemo-retention-conformance-bench # DPDP, 365-day floor
cargo run --release -p mnemo-retention-conformance-bench -- --profile eu-ai-act-art19
mnemo compliance retention --profile dpdp # print the profile + backend gateDates are provisional and depend on the final notified/enacted texts β verify against the primary sources before relying on them. Why this matters, and how mnemo compares on the compliance-audit axis: docs/POSITIONING.md.
- Hybrid retrieval β Reciprocal Rank Fusion combining semantic vectors (USearch/pgvector), BM25 keywords (Tantivy), knowledge graph signals, and recency scoring with configurable weights
- Bitemporal graph layer (
mnemo-graph) β Graphiti-inspired temporal edges withvalid_from/valid_to(fact validity) plusrecorded_at(system clock).graph_expand(seed, depth, as_of)walks the graph at any point in time without losing later corrections. New in v0.4.0-rc1. - AES-256-GCM encryption β at-rest content encryption via
MNEMO_ENCRYPTION_KEY - Hash chain integrity β SHA-256 content hashes with chain linking and
verifytool - Memory poisoning detection β anomaly scoring with prompt injection pattern detection; quarantine for flagged content
- Cognitive forgetting β five strategies: soft delete, hard delete, decay, consolidation, archive
- Feedback-driven consolidation trigger β opt-in
ConsolidationPolicy::MaturityDrivengatesrun_consolidationon a per-cluster maturity score (recency Γ hit-success Γ edge-degree Γ redundancy) instead of firing on a fixed schedule. Inherited byforgetandcheckpointautomatically across MCP / REST / gRPC / pgwire; the defaultFixedSizepolicy preserves the v0.4.x behaviour byte-for-byte. New in v0.4.10. - Branching and replay β checkpoint, branch, merge, and replay agent memory timelines
- Point-in-time queries β recall memories as they existed at any timestamp with
as_of - Causal debugging β trace event causality chains up/down with type filtering
- RBAC + delegation β ACL-based permissions with scoped, depth-limited transitive delegation
- Permission-safe ANN β iterative oversampling with post-filtering for ACL compliance
- ONNX local embeddings β run embeddings locally without API calls via
MNEMO_ONNX_MODEL_PATH - S3 cold storage β archive old memories to S3-compatible storage (feature-gated)
- LRU cache β in-memory caching layer for frequently accessed memories
- Scale-to-zero β auto-shutdown after configurable idle timeout with checkpoint-on-shutdown
- OTLP observability β ingest OpenTelemetry GenAI spans as agent events
- Append-only audit log β immutable event log with database-enforced triggers (PostgreSQL)
- GEM trajectory-correctness audit β
mnemo-compliance::trajectory_auditreplays the hash-chained event log and reports four trajectory-level signals: (a) unregulated-growth (active-bank vs ceiling), (b) missing-semantic-revision (facts superseded but never revised), (c) capacity-driven-forgetting (deletes outside the 5 named strategies), (d) read-only-retrieval (scopes that only RECALL). Surfaced viamnemo.trajectory_audit(MCP),POST /v1/compliance/trajectory_audit(REST), and theTrajectoryAuditgRPC RPC β same(agent_id, thread_id)shape asmnemo.verify, on the orthogonal trajectory axis. - MemFail per-operation fault-isolation suite β
mnemo_core::eval::memfaildecomposes each end-to-end recall into the three operation seams mnemo exposes (remember= store,run_consolidation= summarize,recall= retrieve) and ships three adversarial probe sets plus a canonical stale-context fixture that attributes a stale-recall failure to retrieve when store + summarize check out. Designed forcargo testand as a reusable library for downstream eval harnesses. New in v0.4.11. - Evidence-weighted conflict resolution β resolve multi-agent conflicts using source reliability scoring
- Memory-provenance signing on reads β every
recall(..., with_provenance=True)returns an HMAC-SHA256 receipt binding the cited records to a server-side key; supports key rotation. Verify offline from Python viamnemo.provenance.verify_read_provenance. New in v0.4.0-rc3. - Hardened MCP launcher β
mnemo mcp-server --manifest <path>runs a safe-spawn gauntlet (refuse inherited secrets, refuse--configargv injection, refuse untrusted parents) BEFORE engine state is constructed. Direct response to the OX-MCP "exfiltrate-then-act" disclosure (2026-04-24). All privileged knobs come from a chmod-restricted TOML manifest. New in v0.4.0-rc3. - DPDPA consent-token guard (library β not enforced by default) β
mnemo-compliance::ConsentTokenGuardvalidates a consent token's expiry / scope / revocation, andMannsetuConsentSourcebinds a DPB-registered consent manager. The core engine does not require a consent token βengine.rememberperforms no consent check; this is an opt-in guard a caller wires in front of writes, not a default gate. New in v0.4.0-rc3. (See the enforcement table below.) - MCP tool-catalog attestation (enforced at hardened boot) β when the manifest sets
tool_catalog_pin_path,mnemo mcp-serverfingerprints the tools it is about to advertise (after any[role_filter]) and compares them to the pin before serving stdio: it refuses to start on any added or mutated tool and on a removed-only downgrade unlessallow_removed_drift = true, and records every verdict as anmcp_tool_catalog_driftaudit event. Generate a pin for your exact binary withmnemo mcp-server --manifest <m> --print-catalog-pin. On stdio the catalog is static after boot, so this one check is complete for the transport (it defends against a substituted binary, a hostile dependency that injects/renames a tool, and pin drift after an upgrade); it is not a per-request check and is only as strong as the manifest file's permissions. Direct response to arXiv 2604.20994 (function-hijacking via tool-list poisoning). New in v0.4.0, wired in v0.5.20. (See the enforcement table below.) - Cloudflare Mesh runtime adapter β SPIFFE-style
MeshIdentity+ per-namespaceMemOpACL +MeshAuditEnvelopechained into the existing HMAC ledger. First OSS embedded memory DB to speak Cloudflare Mesh attestation natively. New in v0.4.0. - Code-mode WIT recall β
mnemo:memory@0.4WIT world plus a wasmtime-friendly host runner. Agents callrecallas a sandboxed WASM function instead of a JSON tool envelope, dropping per-turn token cost ~96% on 200-turn LongMemEval_S samples. New in v0.4.0. - Decay-curve score lane β
DecayLane(Ebbinghaus + reinforcement) fuses with vector + BM25 + recency in the default recall path.letta_modeflag bypasses it for parity with Letta's published numbers. New in v0.4.0. - Agent-Deal ledger β
mnemo-dealcrate ships a chained-HMACDealEnvelopelog withverify_chain β DisputeReport. v0.4.1 adds advertisement (/.well-known/mnemo-deal-agent.json) + advisory reputation (90-day half-life, per-dispute 10% penalty). - Markdown+Git working-set adapter β
mnemo-md-syncparses YAML-style frontmatter (mnemo_id,tags,expires_at) and providesMdSyncSpec+SyncFlushPolicy(PreferEngine / PreferDisk / NewerWins). New in v0.4.0. - Anthropic CMA-Memory compat shim β
mnemo-cmacrate mounts, mirrors, and exports the Anthropic CMA-Memory beta filesystem (announced 2026-04-23). Every CMA write is bridged into the mnemo HMAC chain viaCmaSource::CmaBetamarkers. New in v0.4.1. - Agent behavioural-baseline exporter β
mnemo-baselinecrate emits per-agent profiles in OpenTelemetry semconv 1.31 + OCSF 1.4 Application-Activity formats with z-score+EWMA drift detection; anti-leak regex test ensures payloads never carry memory contents. Plugs into the RSAC 2026 SOC telemetry gap. New in v0.4.1. - 1M-context recall budget planner β
mnemo-core::budgetaddsContextBudget::for_model+plan_recallcoveringdeepseek-v4-1m,claude-3.7-sonnet-1m,gpt-5.1-400k,gemini-2.5-pro-2m; typedFallbackStrategy; property test asserts no model overflows. New in v0.4.1. - mnemo doctor + Grafana dashboard β typed
DoctorReport+DoctorFixrecommendations and a committeddashboards/mnemo-grafana.json(schemaVersion 39) covering recall p50/p99, tool-catalog drift, HMAC continuity, code-mode token reduction. New in v0.4.1. - MCP role-aware tool filter (enforced when configured) β the manifest
[role_filter]block (caller_roles,default = "allow_all" | "deny_all", per-toolallow/deny, deny-wins) is parsed and validated at startup and attached to the hardened MCP server: a denied tool is hidden fromtools/listand rejected bytools/callwith-32601. On stdio there is no per-call caller identity, so it acts as a server-wide tool denylist rather than per-caller RBAC; with no[role_filter]block present, every advertised tool stays reachable (unchanged). Aligned with the 2025-11-25 MCP authorization spec. New in v0.4.2. (See the enforcement table below.)
To report a suspected vulnerability privately, see SECURITY.md:
supported versions, private reporting via GitHub security advisories, a
first-response time that can actually be kept, which backends are in scope, and
what is out of scope.
mnemo ships a lot of security machinery; not all of it is on the default request path yet. This table is the honest picture β verified against the code, not the roadmap. "Enforced" means a live code path rejects/acts; "library / parsed-only" means the logic exists and is tested but nothing on the default path calls it.
Two rows are β
only under a condition, and that condition is off by default.
Stating it here rather than leaving it inside the row text, because a reader
scanning the middle column will otherwise credit mnemo with a control their
deployment is not actually running:
- MCP role-filter does nothing until the manifest declares a
[role_filter]block.mnemo mcp-serverstarted without one exposes every advertised tool. - Capability-leased reads do nothing until
--lease-ttl-secondsis set non-zero, which additionally requires--capability-key(the server refuses to start with one and not the other). The default is0, so a stock server acceptsmnemo.forget_subjectwith no lease at all.
Both are wired β the CLI attaches them to the served MCP server, and
hardened_mode_attaches_role_filter drives a denied tool over JSON-RPC to a
-32601 β but wired and on are different claims, and only the first is true out
of the box. The table itself is pinned by
readme_enforcement_claims_are_real.rs:
the conditional β
rows fail the build if the CLI stops attaching the filter or
the lease store, and the β rows fail it if those surfaces are ever wired without
this table being updated in the same change. Until that test it was hand-maintained
and unpinned, which is the only way a row here can go stale.
| Control | Enforced by default? | Where / how |
|---|---|---|
| Network bearer-token auth (REST + gRPC) | β
when MNEMO_AUTH_TOKEN is set β else open + loud warning |
axum middleware / tonic interceptor β 401 / UNAUTHENTICATED |
| Per-record ACL / RBAC (private/shared/public scopes) | β | check_permission on recall (shared scope) + share (admin) |
| Permission-safe ANN (post-filter to accessible ids) | β | recall ANN path |
| Scoped, depth-limited delegation | β | delegate + ACL checks |
| At-rest AES-256-GCM content encryption | β
when MNEMO_ENCRYPTION_KEY set |
engine remember/recall |
Hash-chain integrity + verify |
β | events + memories |
| Read-provenance HMAC receipt | β
opt-in per call (with_provenance=true) |
recall |
| Write-provenance + FORGET BY PROVENANCE | β β every REMEMBER/SHARE records who wrote it, under what capability, in what session; hash-chained (tamper-evident) and queryable by memory/principal/session; revocable in one call by principal or session | engine remember/share write path (DuckDB + PostgreSQL); REST /v1/provenance/*, MCP mnemo.provenance + mnemo.forget_by_provenance; see below |
| Opaque-reasoning-payload write flag | β
warn-and-record β on REMEMBER, content with the SHAPE of a provider opaque reasoning payload (arXiv:2608.09867) is flagged opaque_reasoning_payload on its provenance (hashed in, tamper-evident) and stored, not rejected; sweeps via FORGET BY PROVENANCE. Shape only β not proof of a secret; never decodes |
remember write path β WriteProvenance.flags; docs/security/opaque-reasoning-payloads.md |
| Memory-poisoning anomaly + quarantine | β β provenance-aware recall + a published ASI06 resistance micro-bench (100.0% vs canonical query-only MINJA, Wilson 95% [98.1%, 100.0%], n=200; 0% vs marker-free paraphrase β honest limitation) | remember + recall quarantine filter; docs/security/ASI06.md |
| Forged-reasoning defense (reasoning-provenance trust filter) | β
opt-in β a real-embedder bench drives planted fabricated-chain-of-thought ASR 100% β 0% (nomic-embed-text, Wilson 95% ASR_on [0.0%, 3.1%], n=120) at 0/180 = 0% benign false-quarantine [0.0%, 2.1%] |
RecallRequest.reasoning_trust (retrieval::ReasoningTrustPolicy) enforced in recall's passes_filters; bench/forged_reasoning/ |
| Append-only audit-log trigger | β on PostgreSQL | DB trigger |
MCP role-filter (manifest [role_filter]) |
β
when a [role_filter] block is present and not a no-op β a denied tool is hidden from tools/list and rejected by tools/call with -32601; on stdio there is no per-call caller identity, so this is a server-wide tool denylist, not per-caller RBAC. No block β every advertised tool reachable (unchanged) |
mnemo-mcp::role_filter dispatch, attached by the mnemo-cli hardened server; hardened_mode_attaches_role_filter (CLI) + role_filter_* (library) tests |
| MCP tool-catalog attestation | β
when a [tool_catalog_pin] block is present β the hardened server fingerprints the tools it will advertise (post role-filter) and, before serving stdio, refuses to start on any added/mutated tool (and on removed-only drift unless allow_removed_drift); every verdict is an mcp_tool_catalog_drift audit event. On stdio the catalog is static after boot, so this one boot-time check is complete for the transport β not per-request, and only as strong as the manifest file's permissions. No block β no attestation (unchanged) |
mnemo-cli::attest wired in run_mcp_server; hardened_mode_attests_tool_catalog (CLI stdio) + attest unit tests; generate a pin with --print-catalog-pin |
| Consent-token-per-write | β library only β core engine never calls it | mnemo-compliance::ConsentTokenGuard |
| Lease tokens (capability-leased reads) | β
shipped, opt-in via --lease-ttl-seconds <N> (requires --capability-key) β mnemo.recall mints a lease and mnemo.forget_subject refuses without a live one. All four ADR 0001 properties enforced: freshness (TTL), causality (only a recall mints one), caller-binding (the minting principal alone can spend it), and subject scope (it covers only the subjects the read returned β #160). Unattached, both tools behave exactly as before. This row read "not shipped β removed as dead code" until #159; the earlier removal was real, and so is this |
mnemo-mcp::lease wired in server.rs; lease.rs unit tests + tests/capability_leased_reads.rs |
| Cloudflare Mesh / Agent-Deal / baseline exporter / CMA shim | β standalone adapter crates β not invoked by the running server | mnemo-mesh / mnemo-deal / mnemo-baseline / mnemo-cma |
The REST and gRPC servers are unauthenticated unless you set a shared secret:
export MNEMO_AUTH_TOKEN="$(openssl rand -hex 32)"With it set, every REST request (except GET /v1/health and CORS preflight)
must send Authorization: Bearer <token> or get 401; every gRPC RPC must send
an authorization metadata value or get UNAUTHENTICATED. The token is compared
in constant time. With it unset, both servers run open and log a warning on
every startup β they never silently serve an unauthenticated memory database.
This is a single operator-held secret (the floor for "don't run an open memory
server"), distinct from the per-record ACL/RBAC layer above; it is not user
accounts, scopes, or rotation.
Every remember and share records a write-provenance entry: the writing
principal, the capability it was authorised under (if any), the session/trace id,
the operation, a timestamp, and a SHA-256 content_hash chained to the previous
entry's hash. The chain is tamper-evident (verify_provenance_chain), the
records are queryable (memory ID β provenance; principal/session β everything
it wrote), and a principal or session can be revoked in one call. Revocation
removes the memories but not their provenance β the audit trail survives, because
wiping is not remediation.
The authority field references a real capability (model::capability, a
minimal HMAC-signed {principal, scope, expiry} token β part of #126),
not a recorded string. mnemo stays standalone: no external audit dependency.
REST
# Who wrote this memory?
curl -s localhost:8080/v1/memories/$ID/provenance
# Everything a principal / session wrote
curl -s localhost:8080/v1/provenance/principal/alice
curl -s localhost:8080/v1/provenance/session/sess-42
# Tamper-evidence over the chain
curl -s localhost:8080/v1/provenance/verify
# FORGET BY PROVENANCE β revoke everything alice wrote
curl -s -XPOST localhost:8080/v1/provenance/forget \
-H 'content-type: application/json' \
-d '{"principal":"alice","strategy":"hard_delete"}'MCP β tools mnemo.provenance (read by memory_id | principal | session_id)
and mnemo.forget_by_provenance (revoke by principal | session_id).
SDKs β all three expose read and cleanup (an SDK that cannot clean up is not shipping the feature):
# Python
prov = client.write_provenance(memory_id) # who wrote it
client.writes_by_principal("alice") # everything alice wrote
client.forget_by_principal("alice", "hard_delete") # FORGET BY PROVENANCE// TypeScript
const prov = await client.getMemoryProvenance(id);
await client.writesByPrincipal("alice");
await client.forgetByProvenance({ principal: "alice", strategy: "hard_delete" });// Go
prov, _ := client.MemoryProvenance(id)
_, _ = client.WritesByPrincipal("alice", 0)
_, _ = client.ForgetByProvenance(mnemo.ForgetByProvenanceInput{Principal: ptr("alice")})Anthropic's Dreams Research Preview (surfaced 2026-05-06 at Code w/ Claude SF) is a managed-agent feature that "lets Claude reflect on past sessions to curate an agent's memory and surface new insights." Its companion Routines doc describes the long-horizon agents that consume curated memory. mnemo's REMEMBER / RECALL / FORGET / SHARE primitives, envelope provenance, and AES-256-GCM at-rest encryption are the substrate any such curator reads from and writes through β Dreams owns what to curate, mnemo owns how to durably store with audit trail. The two surfaces are complementary, not substitute.
Honest framing: the Dreams API itself is a Research Preview behind a Request-access form, and mnemo does NOT today ship an Anthropic-API adapter. Today's anchor is substrate-level interop documentation, not integration. A mnemo-dreams adapter crate is plausible if/when the API exits Research Preview, but is explicitly NOT in scope for v0.4.x. See docs/comparisons/anthropic-dreams.md for the curator-action β mnemo-primitive layering table.
Cloudflare announced Agent Memory GA during Agents Week (2026-04-30), followed by Workers AI inference, Email Service beta, and an Agents SDK preview. It is the closest hosted competitor to mnemo.
mnemo is an embedded, cryptographically-audited, replayable memory the regulator can inspect offline. Cloudflare optimises recall throughput on the edge runtime; mnemo optimises a memory whose every write is HMAC-chained, every read is provenance-signed, and whose storage layer survives outside any cloud's audit boundary.
Honest concession: on per-recall p50 against the Workers KV+Vectorize
backend, edge-recall throughput likely favours Cloudflare. mnemo's
axis is provenance, chain replay, point-in-time as_of, evidence-
weighted conflict resolution, DPDPA / GDPR subject erasure with audit
preservation, and the v0.4.2 MCP role-aware tool filter β surfaces
that matter when an auditor or regulator must reconstruct exactly what
an agent saw and decided, three months later, without depending on a
cloud account staying live.
A full bench harness against Cloudflare Agent Memory (a planned
mnemo-bench-cf crate) was scoped but has not been built β it is not a
workspace member and the numbers have not been run.
docs/comparisons/cloudflare-agent-memory.md
documents the differentiation scenario list with empty-bench
placeholders so the comparison's contract is explicit.
Retrieval-strategy framing matters here too: arXiv 2605.15184 (Sen et al., May 2026) measured BM25 keyword retrieval outperforming pure vector retrieval on its experiment-1 corpus inside an agent harness. mnemo's documented default β hybrid RRF over BM25 + vector
- graph + recency β is already hedged against the vector-first
default the paper questioned. v0.4.4 adds a typed
RetrievalMode::HarnessAware { harness, format }variant that lets the response envelope be reshaped per agent harness (Claude Code, Codex, Gemini CLI, Chronos, generic) without changing which records the substrate retrieves. Seedocs/research/grep-vs-vector-2605.15184.mdfor the composition anchor + the explicit non-overclaim disclaimer.
Outcome diffing β reconstructing the artifact's full provenance from
append-only events β is the third trust wall in production agent
systems (alongside aligned-by-training intent and policy-mediated
action). The DELEGATE-52 delegation-corruption result
(arXiv 2604.15597, surfaced on
Hacker News 2026-05-09) puts a 25% baseline on the corruption rate
this layer needs to detect. mnemo's append-only event log + snapshot
substrate is the layer that lets a downstream auditor reconstruct any
artifact's full plan / input / trace / output tetrad and diff against
what the primary agent's plan asked for β
see docs/research/delegate52-2604.15597.md
for the operator recipe and the explicit non-overlap callout.
Governed Persistent Memory (arXiv:2608.12476, Guodong Xu, 2026-08-12) is the clearest academic statement of a problem mnemo has only partly solved, so it is cited here rather than left out.
Its argument: agent memory is wrongly modelled as selectβstoreβretrieve, because retrieval never decides whether a contradictory, superseded, retracted, deleted or stale record may support an outgoing claim. It proposes a bitemporal state-transition model with source-bound admission, derived lifecycle state, and fail-closed structured release, expressed as five executable clauses. On its hash-frozen 3,600-case GPM-ReleaseBench, the strongest of three complete simple policies matches only 1,800/3,600 and makes unmatched releases on 50% of violation cases; in its sealed service evaluation the governed lane is correct on 2,400/2,400 clusters against 600/2,400 for ungoverned local Qwen2.5-7B. Those are bounded contract-conformance results by the paper's own framing, not open-world accuracy β the 7B figure is the ungoverned comparison, not a claim about model accuracy.
Where mnemo lands. Some of the five clauses ship, some ship in weaker partial
forms, and some are not implemented at all β one of them in direct conflict with
a shipped feature. Which clause is in which state is deliberately not restated
here, because a second copy is a copy that drifts. It lives in exactly one place,
is generated from a manifest, and every row is asserted against this tree by
crates/mnemo-compliance/tests/gpm_clause_manifest.rs:
The structural gap underneath all five: mnemo does not implement GPM's
bitemporal derived-lifecycle-state model. There is bitemporal machinery in
mnemo-graph and as_of in core, but no state machine
derives per-record releasability from transitions, and mnemo's release posture is
fail-open β RECALL returns records and mnemo's involvement ends. An unforgeable
ledger of a bad release is still a bad release. That is the gap, and it is not
scheduled; the page above says what adoption would actually cost.
Cloudflare extended this story on 2026-05-04 with Project Think, a runtime story for AI agents built on Workers
- DO Facets β the durable agentic loop itself. Project Think is upstream of mnemo's surface: it owns where the agent runs and how the loop survives a Worker restart. mnemo owns whether the writes that loop emits are cryptographically chained, replayable months later, and inspectable without a Cloudflare account.
These are complementary, not substitute, surfaces. An operator can
run their durable loop on Project Think + DO Facets and chain every
memory write into mnemo's HMAC ledger; the bench crate that compares
Cloudflare Agent Memory vs mnemo as a memory store does not redo
itself for Project Think as a runtime vs mnemo as a memory ledger β
the latter is a layering question, not a benchmark. See
docs/comparisons/cloudflare-project-think.md
for the full layering table and where each side wins.
The examples/ directory contains working integration examples for all major agent frameworks:
| Example | Framework | Language |
|---|---|---|
openai_agents_example.py |
OpenAI Agents SDK | Python |
langgraph_mcp_example.py |
LangGraph + MCP | Python |
crewai_mcp_example.py |
CrewAI + MCP | Python |
google_adk_example.py |
Google ADK | Python |
agno_example.py |
Agno | Python |
pydantic_ai_example.py |
Pydantic AI | Python |
autogen_example.py |
AutoGen | Python |
smolagents_example.py |
HuggingFace Smolagents | Python |
strands_agents_example.py |
AWS Strands Agents | Python |
semantic_kernel_example.py |
Microsoft Semantic Kernel | Python |
llama_stack_example.py |
Meta Llama Stack | Python |
dspy_example.py |
DSPy | Python |
camel_ai_example.py |
CAMEL AI | Python |
browser_use_example.py |
Browser Use | Python |
basic_memory.py |
Direct PyO3 | Python |
mastra_example.ts |
Mastra | TypeScript |
vercel_ai_sdk_example.ts |
Vercel AI SDK | TypeScript |
mnemo [OPTIONS] [COMMAND]
Options:
--db-path <PATH> Database file path [default: mnemo.db] [env: MNEMO_DB_PATH]
--openai-api-key <KEY> OpenAI API key [env: OPENAI_API_KEY]
--embedding-model <MODEL> Embedding model [default: text-embedding-3-small] [env: MNEMO_EMBEDDING_MODEL]
--dimensions <DIM> Embedding dimensions [default: 1536] [env: MNEMO_DIMENSIONS]
--agent-id <ID> Default agent ID [default: default] [env: MNEMO_AGENT_ID]
--org-id <ID> Organization ID [env: MNEMO_ORG_ID]
--onnx-model-path <PATH> ONNX embedding model path (local inference) [env: MNEMO_ONNX_MODEL_PATH]
--rest-port <PORT> Enable REST API on this port [env: MNEMO_REST_PORT]
--postgres-url <URL> Use PostgreSQL backend [env: MNEMO_POSTGRES_URL]
--encryption-key <HEX> AES-256-GCM encryption key (64 hex chars) [env: MNEMO_ENCRYPTION_KEY]
--capability-key <HEX> HMAC key for per-request capabilities (ADR 0002) [env: MNEMO_CAPABILITY_KEY]
--capability-key-id <ID> Key id recorded in issued capabilities [default: default] [env: MNEMO_CAPABILITY_KEY_ID]
--http-port <PORT> Serve MCP over authenticated Streamable HTTP instead of stdio
(needs --features http-transport; requires --capability-key) [env: MNEMO_HTTP_PORT]
--lease-ttl-seconds <SECS> Capability-leased reads (#126): recall mints a lease, forget_subject
requires one (0 = disabled) [default: 0] [env: MNEMO_LEASE_TTL_SECONDS]
--idle-timeout-seconds <SECS> Auto-shutdown after idle period (0 = disabled) [default: 0] [env: MNEMO_IDLE_TIMEOUT]
Commands:
baseline Train the per-agent embedding-space baseline used by the z-score
outlier detector (v0.3.3, Task A).
mcp-server Start the MCP STDIO server in hardened mode using a TOML manifest
(v0.4.0-rc3, Task B2). Refuses inherited secrets / argv injection /
untrusted parents BEFORE engine state is constructed. Privileged
knobs come from the manifest; key material reaches the binary via
a chmod-restricted keystore file. See
`examples/mcp-server/manifest.toml` for an annotated reference.
eval Replay a JSONL dataset of {query, expected} rows against an
in-memory engine and emit a per-row latency / top-k JSONL report
(v0.4.0-rc3, Task B6). Defaults to the bundled LongMemEval_M
sample under `crates/mnemo-core/benches/data/longmemeval_m.jsonl`.
Pass `--with-provenance` + `--provenance-key-hex <hex>` to also
measure the HMAC-receipt overhead.
bench Run a measurement-only benchmark (v0.4.9). Subcommand:
embeddings Measure every configured embedding backend
(nDCG@10, recall@10, p50/p95 latency, throughput)
and recommend the highest-nDCG backend whose p95 β€
the SLO. Flags: --slo-ms <MS> [default: 50],
--dimensions <DIM> [default: 384],
--latency-samples <N> [default: 32].
No retrieval defaults / RRF weights change.
compliance Compliance primitives (mnemo-compliance). Subcommand:
retention Print a processing-log retention-conformance profile
(DPDP Rules 2025 / EU AI Act Art.19 / HIPAA
Β§164.312(b)) and gate it against the active backend's
append-only floor, failing loud if it cannot honour
it. Flags: --profile <dpdp|eu-ai-act-art19|hipaa>
[default: dpdp], --floor-days <N> (override the
legal-minimum floor).
capability Mint per-request capabilities (ADR 0002). Subcommand:
issue Issue a signed capability. Flags: --key <HEX>
(must match the server's --capability-key),
--key-id <ID> [default: default], --principal <ID>,
--scope "<tokens>" (`role:<id>` entries become RBAC
roles; others are opaque scopes), --ttl-seconds <N>,
--format <bearer|json> [default: bearer].
audit Export the memory-write hash chain for offline verification.
Subcommand:
export Write the chain as JSONL, in verification order, with
every field a plain string or null and hashes in hex
β no mnemo types, so the verifier needs no mnemo.
Flags: --agent-id <ID> [default: default],
--since <RFC3339>, --limit <N> [default: 100000],
--out <PATH> (stdout if omitted, so it pipes).
Verify with `tools/verify_mnemo_chain.py`, which
imports nothing but the Python standard library:
see [docs/verify-my-log.md](docs/verify-my-log.md).
By default the caller is the boot-time --agent-id: one process, one caller,
which is exactly right on stdio. Set --capability-key <hex> and a request may
instead carry a signed capability whose principal becomes the caller identity
for that one call β in _meta["dev.mnemo/capability"] on any transport, or
as Authorization: Bearer <base64url> over HTTP.
# One key for the server and for minting.
export MNEMO_CAPABILITY_KEY=$(openssl rand -hex 32)
# Serve MCP over authenticated Streamable HTTP (needs --features http-transport).
mnemo --capability-key "$MNEMO_CAPABILITY_KEY" --http-port 8080
# Mint a caller's token.
mnemo capability issue --key "$MNEMO_CAPABILITY_KEY" \
--principal alice --scope "role:reader" --ttl-seconds 900A capability that cannot be verified β malformed, forged, expired, unknown key, or presented to a server holding no key β is rejected, never quietly downgraded to the boot identity. Over HTTP a request carrying no capability is also rejected: the boot fallback is only sound on a transport where the operator is the sole possible caller, and on a network port it would let any client that can reach it act as the operator.
The HTTP transport binds
127.0.0.1and does not terminate TLS. Put it behind a reverse proxy for anything beyond local use.
--lease-ttl-seconds <N> binds destructive erasure to a read the same caller
just performed, breaking the OX-MCP "exfiltrate-then-act" chain. With it on,
mnemo.recall returns a lease and mnemo.forget_subject requires it:
// mnemo.recall result gains β `subjects` lists what this read actually covered:
"lease": { "token": "01a0β¦", "agent_id": "alice",
"scopes": ["forget_subject"], "subjects": ["s1"], "ttl_seconds": 60 }
// mnemo.forget_subject then requires:
{ "subject_id": "s1", "lease_token": "01a0β¦" }A lease is refused if it is expired, names the wrong scope, was never issued,
belongs to a different caller (the replay case it exists to stop), or
does not cover the subject being erased. Requires --capability-key:
without per-request identity every caller shares the boot agent id, so every
lease would validate for everyone.
Off by default, because it changes the contract of two shipped tools.
All four of ADR 0001's properties
are enforced β freshness, causality, caller-binding, and subject scope. The
subjects set is read off the subject: tags of the records the recall
returned, not inferred from the query, so an injected "forget everything about
bob" cannot ride a legitimate read of alice. A read that surfaced no
subject-tagged record mints a lease that authorises no erasure β the empty
set means nothing, never everything.
What it still does not defend: a single caller driven through both steps. If the same principal is induced to recall subject
s1and then erase it, the lease is issued and spent legitimately. Narrowing raises the cost β the injection must now steer a read of the very subject it wants erased β but the lease breaks cross-principal replay and stale authority, not an agent acting against its own interest throughout.
ββββββββββββ βββββββββββββ ββββββββββββ ββββββββββββ
βMCP Clientβ βREST Clientβ β gRPC β β psql β
β (stdio) β β (HTTP) β β β β (pgwire) β
ββββββ¬ββββββ βββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ
β β β β
βΌ βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MnemoEngine β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β
β β Remember β β Recall β β Forget/ β βCheckpointβ β
β β Pipeline β β Pipeline β βShare/... β β/Branch/ β β
β β β β (RRF) β β β βMerge β β
β ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β
β βββββββββββββββ΄βββββββββββββ΄βββββββββββββ β
β β β
β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββ β
β β StorageBackend (trait) β β
β β ββββββββββββ βββββββββββββββ β β
β β β DuckDB β β PostgreSQL β β β
β β ββββββββββββ βββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββ ββββββββββββ ββββββββββββ ββββββββββ β
β βVectorIndex β βFullText β βEmbeddingsβ βEncrypt β β
β βUSearch/PG β β Tantivy β βOpenAI/ β βAES-256 β β
β β β β β βONNX/Noop β βGCM β β
β ββββββββββββββ ββββββββββββ ββββββββββββ ββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
docker build -t mnemo .
docker run -p 8080:8080 -e OPENAI_API_KEY=sk-... mnemo --rest-port 8080helm install mnemo deploy/helm/mnemo \
--set env.OPENAI_API_KEY=sk-... \
--set env.MNEMO_REST_PORT=8080The Helm chart includes: Deployment, Service, ConfigMap, Secret, PVC, HPA, and Ingress templates.
Status: design anchor, not a shipped template. The
deploy/cloudflare/scaffold is parked for v0.4.3 follow-up. This section documents the contract that scaffold will produce against β seedocs/src/integrations/cloudflare-workers-deploy.mdfor the full design note.
Cloudflare Durable Object Facets (open beta, 2026-04-30) lets a single Worker dynamically load Durable Object classes, each with its own SQLite database. That's the per-tenant embedded-substrate shape mnemo already runs on DuckDB-per-agent β making Workers the natural managed runtime for an mnemo MCP server when you don't want to operate the box yourself.
The intended layout (single Worker, one DO Facet per tenant, mnemo as the MCP-over-HTTP entrypoint):
# wrangler.toml (sketch β not yet shipped under deploy/cloudflare/)
name = "mnemo-mcp-worker"
main = "dist/worker.js"
[[durable_objects.bindings]]
name = "MNEMO_TENANT"
class_name = "MnemoTenantFacet"
# DO Facet β each instance gets its own SQLite-backed storage
# matching mnemo's embedded DuckDB-per-agent contract.What stays Rust-native vs. crosses the JS boundary, the file-format compatibility story (mnemo writes DuckDB; the Workers Facet exposes SQLite β a planned bench crate would quantify the gap), and which mnemo surfaces require the operator-held HMAC keystore vs. which can run inside the Worker β all in docs/src/integrations/cloudflare-workers-deploy.md. The Cloudflare-vs-mnemo bench numbers would ship with that mnemo-bench-cf crate, which has not been built β it is not a workspace member and those numbers have not been run.
# Run all tests (unit + integration + MCP + pgwire + REST + admin + gRPC + doctests)
cargo test --all
# Run tests for a specific crate
cargo test -p mnemo-core
cargo test -p mnemo-mcp
# Run integration tests only
cargo test -p mnemo-core --test integration_test
# Lint and format
cargo clippy --all-targets --all-features
cargo fmt --all
# Run benchmarks
cargo bench -p mnemo-core
# Build with optional features
cargo build -p mnemo-core --features onnx # ONNX local embeddings
cargo build -p mnemo-core --features s3 # S3 cold storage
cargo build -p mnemo-cli --features postgres # PostgreSQL backend
# Build Python SDK (requires maturin, NOT cargo build)
cd python && maturin develop
# TypeScript SDK
cd sdks/typescript && npm install && npm test
# Go SDK
cd sdks/go && go test ./...We run LoCoMo-MC10 and LongMemEval on every release. The canonical
results page is
docs/benchmarks/2026-04-25-mnemo-v0.3.4.md
β it carries reference rows for Hindsight (91.4% LongMemEval / 89.61%
LoCoMo, source) and
Letta-Filesystem (74.0%) plus the four mnemo retrieval strategies
side-by-side. The mnemo rows populate from the first authenticated
nightly run β ungated CI
forks read the empty rows and the workflow's first-run exception
keeps the regression gate honest. Earlier reports under
docs/benchmarks/ carry the v0.3.0 / v0.3.1 floor
numbers from before the v0.3.3 Tantivy-default + LLM-judge fixes.
Real-embedder memory-quality result β bench/RESULTS.md.
The earlier measurement (see the headline block above for the
current one, which uses a different embedder and slice): mnemo's recall path with
a real local semantic embedder (nomic-embed-text, 768-dim, via Ollama, never
NoopEmbedding) over the bundled LongMemEval_M slice, held-out eval:
semantic recall@1 = 0.739 [Wilson 95% 0.535, 0.875] at n=23 (MRR 0.805), at
~89% token reduction vs.
dumping the full history (Engram-style lean-slice framing,
arXiv:2606.09900 β a reference point, not a
parity claim).
First authenticated baseline, 2026-06-29 @ 640b7b1 (recorded in
docs/benchmarks/baseline.json; n=23, mean of 5 seeds):
| mode | recall@1 | recall@3 | recall@5 | MRR | errored |
|---|---|---|---|---|---|
bm25_only (lexical) |
0.522 | 0.609 | 0.739 | 0.586 | ~1/23 |
vector_only (semantic) |
0.739 | 0.826 | 0.826 | 0.805 | 0/23 |
rrf_hybrid (auto, default weights) |
0.478 | 0.757 | 0.809 | 0.638 | ~1/23 |
rrf_hybrid (auto, swept v6_b1_r0_g0_k30) |
0.696 | 0.783 | 0.826 | 0.765 | ~1/23 |
One caveat: single-run (5 in-process seeds, not restart-averaged); the
HNSW index + RRF fusion-weight selection sit near a noise floor (cf. The FID
Lottery) β the swept "best" hybrid config flips between runs, so treat sub-0.05
gaps as ties; vector_only is the one stable strong mode. This is retrieval
quality + token efficiency, not an LLM-judged QA-accuracy or leaderboard
claim (45-record LongMemEval_M, not _S; QA accuracy needs a generative LLM not
run here). Full tables + JSON: bench/RESULTS.md and the
dated bench/locomo/results/ report. Reproduce:
ollama pull nomic-embed-text && cargo run --release -p mnemo-locomo-bench --bin semantic_recall_bench.
Indirect-query (implicit-association) retrieval + the orientation cache β a
different axis from gold recall above: does the memory layer surface a decisive
fact when the query shares no wording with it and only bridges through world
knowledge? On a 30-row source-cited corpus (12 domains, nomic-embed-text 768-dim),
every fact is directly retrievable (direct recall@5 β 1.00) but an indirect
query misses it ~13% of the time at k=5 (the blind spot). mnemo's opt-in,
constant-token orientation cache β warmed by the fact's own prior access β
surfaces the decisive entity in its bounded map ~93% of the time, lifting combined
surfacing to 1.00 @5 and recovering the full β +0.13 gap via the map, not by
re-ranking retrieval. This is retrieval surfacing, not an LLM-answer score:
it is not a reproduction of InMind (arXiv:2607.24368,
the framing) and not comparable to its 84.0% / 14.4% (which score an LLM's
answers with an in-context arm). 30 rows β wide Wilson CIs. Full write-up:
docs/benchmarks/implicit-association.md.
Reproduce: ollama pull nomic-embed-text && cargo run --release -p mnemo-locomo-bench --bin implicit_association.
Microsoft STATE-Bench (MIT, pinned
at 4efcbf2d,
v0.8.1) measures whether an agent completes multi-step enterprise workflows
(travel / customer-support / shopping) better with memory β a different axis
from the retrieval numbers above (task completion, not gold recall). mnemo is the
on-prem / embedded / auditable entry: it plugs into the Agent Learning Track's
read-only retrieve_learnings hook through the public Python SDK, using the same
embedded DuckDB store that carries the hash-chained, tamper-evident audit log β so
this is evidence for the regulated-AI wedge, not a repositioning.
The harness is built and the mnemo half is smoke-tested offline
(bench/state_bench/). A score is pending hosted-model
access: STATE-Bench hard-locks its user simulator + judge to GPT-5.4 and needs
an agent model (published baseline: GPT-5.1-no-memory, ~50β60% pass@1 β
leaderboard). We do not
publish a partial or faked number; when models are available,
bench/state_bench/run_state_bench.sh is
turnkey and fills
bench/state_bench/results/state_bench.md.
Not a "state of the art" claim.
These are not directly comparable β they measure different things on different data, and we publish only the row we actually ran. mnemo's number is a retrieval metric (did the gold memory land in the top-k); Mem0/Letta publish end-to-end QA accuracy (did an LLM, reading the retrieved memories, answer the question correctly). mnemo has not run the end-to-end QA-accuracy pipeline β that needs a generative LLM + judge, which this harness does not include.
| system | what's measured | metric | score | source |
|---|---|---|---|---|
mnemo (this repo, vector_only) |
retrieval β gold recall@1 over a real local embedder | recall@1 (LongMemEval_M slice, n=23) | 0.739 [0.535, 0.875] | baseline.json, measured 2026-06-29 @ 640b7b1 |
| Mem0 | end-to-end QA accuracy (different axis) | LLM-judged answer accuracy (LongMemEval) | 93.4% | reported |
| Letta | end-to-end QA accuracy (different axis) | LLM-judged answer accuracy (LoCoMo) | ~83% | reported |
Read this honestly: the 0.739 is not a win over 93.4% β they are on different axes (recall vs. QA accuracy) and different datasets, so a head-to-head number does not exist yet. What the mnemo row claims, and all it claims, is that the retrieval layer surfaces the right memory ~74% of the time at k=1 with a real embedder, fully reproducible from the command above. Closing the gap to a comparable QA-accuracy row is tracked as the open end-to-end-eval work, not something we are reporting here.
For the long-form, axis-by-axis version of this against the three memory layers a
2026 search returns first, see
docs/comparisons/mem0-zep-letta.md: where
mnemo is behind (QA accuracy, temporal knowledge graph) and where it is ahead
(on-prem, offline-verifiable tamper-evident audit).
A separate deterministic number over mnemo's default hybrid recall
(strategy="auto"), offline embedder, no LLM β 100 queries Γ 5 pooled repeats/subtask,
top-5, seed 0xbea320262026, via beam_bench:
| subtask | reproduced (this fixture) | self-reported (upstream) |
|---|---|---|
multi_hop (answer only via a graph edge) |
0.6% (3/500) [95% 0.2β1.7%] | β (BEAM reports one overall score) |
open_domain (gold among same-schema distractors) |
68.6% (343/500) [95% 64.4β72.5%] | Hindsight BEAM 64.1% @ 10M tokens (source) |
Not a ranking. The reproduced figures are on a small synthetic fixture with a
lexical offline embedder and no LLM judge; upstream 64.1% is the real 10M-token,
LLM-graded BEAM. Self-reported memory scores are a vendor-run upper bound (not
independently reproduced) β so the columns are not comparable. The low multi_hop
figure is honest, not a bug: default auto RRF barely surfaces an answer reachable
only through a graph edge (the graph / reconstruct strategies target multi-hop).
No "first"/"best" claim. Full note + reproduce command: bench/RESULTS.md.
Phase-aware cost attribution + agent-memory characterization scorecard (bench-only). Anchored on arXiv:2606.06448 (Agent Memory: Characterization and System Implications of Stateful Long-Horizon Workloads). The new phase_cost bin splits every benchmark scenario's cost into the paper's three phases β construction (remember-path: embedding calls, prefill tokens, write latency), retrieval (recall-path: ANN + BM25 + graph + RRF latency, query tokens), and generation (downstream, estimated β mnemo does not generate) β and emits a per-phase table (tokens, wall-ms, $-estimate at configurable per-1K rates) per scenario. The --scorecard-2606-06448 flag instead renders mnemo's PASS / PARTIAL / FAIL position against the paper's 10 Β§5 recommendations (quoted verbatim) as a 10-row table; mnemo currently scores 5 PASS Β· 5 PARTIAL Β· 0 FAIL (PASS on the latency / feasibility / compaction recommendations R5/R7/R8/R9/R10; PARTIAL on the operator-side lifecycle-policy ones R1βR4/R6). Run via cargo run --release --bin phase_cost -p mnemo-locomo-bench (add -- --scorecard-2606-06448 for the scorecard). This is bench-only β no access protocol (MCP / REST / gRPC / pgwire) and no retrieval default is touched; token counts are ceil(chars/4) estimates and the generation phase is never an LLM call. Sample per-phase table (default rates, 64 records / 16 queries, NoopEmbedding):
### Scenario `long-context` (64 records, 16 queries)
| Phase | Tokens | Wall-ms | $-estimate |
|---|---:|---:|---:|
| construction | 15360 | 1551.42 | 0.001306 |
| retrieval | 96 | 157.76 | 0.000002 |
| generation | 13792 | n/a | 0.003912 |
| **total** | 29248 | β | 0.005220 |
Construction and generation dominate; retrieval is near-free β exactly the lifecycle-cost picture the paper insists operators measure (Recommendation 2). See bench/locomo/src/phase_cost.rs for the cost model + the full "what this is NOT" block.
Embedding-backend selection bench + SLA-aware recommender (v0.4.9). Anchored on arXiv:2605.23618 (GE2 vs local encoders β quality + latency). New crate bench/embeddings measures every configured backend (Noop + bench-local hashing baseline always; OpenAI when keyed; ONNX when configured + feature-gated) for nDCG@10, recall@10, p50/p95 embed latency, and throughput at batch 1/8/32 on a 50-doc / 10-query labeled fixture; the recommender picks the highest-nDCG backend whose p95 β€ the SLO and reports the nDCG gap vs the best-quality backend. Run with mnemo bench embeddings --slo-ms <N> or cargo bench -p mnemo-embeddings-bench. See bench/embeddings/README.md for the full "what this bench is NOT" block.
First public LoCoMo number (v0.4.1, P0-1) β full report at
docs/benchmarks/locomo-2026-04-28.md.
mnemo joins the public LoCoMo board alongside MemMachine (84.87%,
2026-04-24) and Memori (81.95%, 2026-04-24); the harness ships at
bench/locomo with a dual-judge variance gate
(GPT-5.1 + Claude-3.7 Sonnet) and runs nightly via
.github/workflows/locomo-nightly.yml.
mnemo trades raw overall score for temporal-slice strength + ~96% per-turn token cost β
see the report for the honest pitch.
Auto-Dreamer-shaped offline consolidation bench (v0.4.8). Added 2026-05-25 at bench/locomo/src/bin/auto_dreamer_consolidation.rs. Mirrors Auto-Dreamer's "smaller active bank, equal-or-better recall" axis against mnemo's existing run_decay_pass + run_consolidation path. Emits a Markdown report + a JSON summary (active_bank_ratio, recall_pre, recall_post) so the headline is citable. Defaults: 8 sessions Γ 25 facts Γ 5 trials. Run via cargo run --release --bin auto_dreamer_consolidation -p mnemo-locomo-bench. See bench/locomo/README.md for the full "what this bin is NOT" block.
LongMemEval_M provenance overhead bench (v0.4.0-rc3, B3). A
self-contained 45-record synthesized dataset ships at
crates/mnemo-core/benches/data/longmemeval_m.jsonl
(override with MNEMO_LONGMEMEVAL_PATH=<path> for the published
gated dataset). The
longmemeval_bench
criterion target runs two arms β recall_no_provenance and
recall_with_provenance β so the per-recall HMAC-receipt overhead
is measurable in CI:
cargo bench -p mnemo-core --bench longmemeval_bench- mdBook:
docs/directory β runmdbook serve docsfor local browsing - Compliance: SOC 2 controls mapping and HIPAA safeguards at
docs/src/compliance/ - REST API:
docs/src/rest-api.md - Tool reference:
docs/src/tools/(one page per MCP tool) - Hardened MCP launcher:
docs/src/integrations/mcp-server.mdβ manifest schema, threat model, systemd unit example - Time-travel debugger:
examples/time-travel-debugger/index.htmlβ vanilla-JS UI that diffs recall results between twoas_oftimestamps; serve any way you like (python3 -m http.server) - LoCoMo report:
docs/benchmarks/locomo-2026-04-28.mdβ first public mnemo number alongside MemMachine + Memori with the honest temporal-slice + per-turn-token pitch - Grafana dashboard:
dashboards/mnemo-grafana.jsonβ schemaVersion 39, drop straight into Grafana 11.5; covers recall p50/p99, tool-catalog drift, HMAC continuity, code-mode token reduction, baseline anomalies - Benchmarks:
docs/benchmarks/
Apache-2.0
{ "query": "indemnification cap", "domain_scope": { "org_id": "acme", "namespace": "legal", "doc_class": "contract" } }