a maintained fork of CozoDB · v0.8.5

embedded Datalog.
performant graphs.
a substrate for machine memory.

mnestic is a transactional relational–graph–vector database that speaks Datalog, the same engine that called itself “the hippocampus for AI,” now actively maintained and tuned as the recall layer for agents.

MPL-2.0 · Rust · RocksDB / SQLite / in-memory backends

recall.cozo — graph ∩ vector, one query
1# every memory reachable from a seed, then the 12 nearest by meaning
2recall[to] := *recalls{ from: $seed, to }
3recall[to] := recall[via], *recalls{ from: via, to }
4 
5?[memory, dist] :=
6 recall[memory],
7 ~memory:embedding{ memory |
8 query: $cue, k: 12, ef: 80, bind_distance: dist
9 }
10:order dist
11:limit 12

Why a fork

CozoDB went quiet after December 2024. The design is too good to let drift. So we forked it, openly, under MPL-2.0, and pointed it at one job: being the memory an agent can trust. Every divergence is documented, every original copyright preserved.

The engine you inherit

01

Datalog, not SQL

Queries compose piece by piece. Recursion is first-class, and runs faster than the SQL equivalent. A safe subset of aggregations is allowed inside recursion.

02

Relational · graph · vector

One engine, one model. The relational algebra handles graph structure implicit several levels deep, with no shoehorning your data into a labelled-property graph.

03

Embeddable like SQLite

Runs in-process (no server, no setup), yet scales to large data and high concurrency, and can also run client-server when you want it to.

04

Vector search (HNSW)

Disk-resident HNSW indices unify with Datalog: search by meaning inside a recursive query, filter with the rest of your rules, all in one pass.

05

Full-text & near-dup

Built-in full-text search and MinHash-LSH for near-duplicate detection: the keyword and dedup legs of retrieval, native to the engine.

06

Time travel

Relations can carry validity time. Query the graph as it was at any historical point: memory you can rewind, not just overwrite.

What mnestic adds

Faster, safer, and built for recall.

The query language and semantics are unchanged. What changed is the stuff that bites you in production (lock contention, full scans, seven-rule retrieval pipelines) and the things agents actually need.

0.8.5

Flat in-RAM parallel index builds — 15× faster ::hnsw create

The bulk build now constructs the graph in flat, integer-indexed memory (contiguous vector slab + per-node adjacency, the hnswlib/pgvector layout) with parallel insertion under per-node locks, then serialises once into the unchanged on-disk format. ::fts create drops a redundant second tokenisation pass and tokenises in parallel. Same search path, same incremental maintenance, still non-blocking.

40k × 384-dim: 294 s → 19 s synthetic · 89.1 s → 8.1 s real-embedding corpus (RocksDB), recall@10 unchanged

0.8.5

Plain-snapshot reads — read-only scripts skip the transaction

On RocksDB, read-only scripts no longer open a pessimistic transaction: they read through a plain snapshot (the standard MVCC read pattern), so reads structurally cannot wait on writer locks. HNSW search batches neighbour vector fetches through one RocksDB MultiGet per expansion step. Also: ::describe was documented and implemented upstream but never reachable from the grammar — wired in, with a read-only guard.

keyed point reads p50 28.5 → 23.9 µs (−16%) · p99 −19%

0.8.4

Per-leg fusion detail — recall that explains itself

ReciprocalRankFusion(detailed: true) and HybridSearch::detailed return one row per (item, contributing leg): which legs surfaced each result, the within-leg rank the fusion used, and the leg's raw score. The fused score reconstructs exactly as Σ 1/(k + rank) — the mechanism behind “why was this retrieved” surfaces. Also fixes a 0.8.3 defect where the durable avgdl counter made concurrent writers to FTS-indexed relations contend on one key.

every fused score decomposes into its legs · Python: detailed=True

0.8.3

Native 3-way fused recall

Graph proximity is now a typed leg of hybrid_search: add GraphLeg seeds and a bounded-hop edge relation, and vector, keyword, and graph signals fuse in one call, one transaction. No hand-written recursion, no app-side stitching.

all 3 signals in one call, ~4× faster than decomposing it by hand

0.8.3

BM25-correct full-text search

The default ::fts scorer is now Okapi BM25 with term-frequency saturation and document-length normalization, and OR-disjunction sums per-term contributions. avgdl is an O(1) read (process-cached since 0.8.4), not a per-query index scan. (tf and tf_idf stay selectable for byte-identical upstream scoring.)

fused recall jumps 0.75 → 0.954, cold-start tail cut ~10× (40k chunks)

0.8.2

Non-blocking HNSW index builds

::hnsw create no longer holds the base relation's write lock while it constructs the graph. The graph is built off-lock under a snapshot and bulk-published via SstFileWriter; mutations during the build reconcile under a brief final lock.

90k reads served (slowest 0.8 ms) during a build that previously blocked them all

0.8.1

One-call hybrid retrieval

DbInstance::hybrid_search runs HNSW + FTS + optional graph traversal, fuses them with Reciprocal Rank Fusion, and optionally diversifies with MMR, all in a single typed call. Previously ~7 hand-written Datalog rules.

RRF + MMR fusion · one typed call replaces ~7 hand-written rules

0.8.1

HNSW builds ~3× faster

The build no longer round-trips the whole graph through the transaction's write-batch overlay. The resulting index is byte-identical to before, just built in a third of the time.

20k × 128-dim: 135 s → 43.6 s (release, measured)

0.8.0

Equality pushdown

*rel[k, ..], k == <value> now compiles to a keyed stored_prefix_join instead of a full scan. Numeric equalities keep cross-type op_eq semantics, so nothing silently changes meaning.

~28–29× faster single-row primary-key lookups (5k rows, measured)

0.8.0

ULID identifiers

rand_ulid() and ulid_timestamp() give you lexicographically-sortable, time-ordered keys, ideal for append-only memory streams you scan by recency.

lexicographically sortable · time-ordered scans

0.8.0

Correctness, inherited & added

Forked 30 commits ahead of the published 0.7.6, including the stored_prefix_join correctness fix. Plus a parser fix for identifiers that begin with a keyword (nullable_column, trueValue).

upstream fixes for free + a slimmer dependency graph

The headline feature

Hybrid retrieval,
one typed call.

Vector similarity, keyword match, and graph proximity are three different signals about “what should I remember right now.” As of 0.8.3 mnestic fuses all three natively: a typed graph leg joins the vector and keyword legs in one call, combined by Reciprocal Rank Fusion and de-duplicated by Maximal Marginal Relevance.

  • Graph proximity is a typed GraphLeg: bounded-hop, ranked by min distance
  • Query vector, text & seeds passed as params, never string-interpolated
  • One call, one transaction; generated CozoScript inspectable via hybrid_search_script
hybrid recall — RRF + MMR in one call
1use cozo::{DbInstance, GraphLeg, HybridSearch, MmrParams};
2 
3// One typed call: HNSW + FTS + graph proximity, fused natively
4// with Reciprocal Rank Fusion, then MMR-diversified.
5let recalls = db.hybrid_search(&HybridSearch {
6 relation: "memory".into(),
7 vector_index: "embedding".into(),
8 query_vector: cue, // Vec<f32> from your embedder
9 vector_k: 24,
10 ef: 80,
11 fts_index: "summary_fts".into(),
12 query_text: "pricing decision",
13 fts_k: 24,
14 // graph leg: expand 2 hops from a seed over *recalls,
15 // rank by min hop distance — fused in the same call.
16 graph_legs: vec![GraphLeg {
17 edge_relation: "recalls".into(),
18 seeds: vec![seed.into()],
19 max_hops: 2,
20 ..GraphLeg::default()
21 }],
22 rrf_k: 60.0,
23 mmr: Some(MmrParams { lambda: 0.5, k: 12, embedding_col: "embedding".into() }),
24 ..HybridSearch::default()
25})?;

Built to be fast

upstream figures · 2020 Mac mini · RocksDB backend

100K+
QPS

mixed read / write / update transactions

250K+
QPS

read-only queries

<1 ms
2-hop

on a 1.6M-vertex, 31M-edge graph

~50 MB
peak RAM

at OLTP load

Backup ≈ 1M rows/s · restore ≈ 400K rows/s · PageRank on 1.6M vertices ≈ 30 s. mnestic keeps these and adds the fork wins above.

Hybrid recall, measured

One engine for all three signals.

The task is fusing vector, keyword, and graph proximity into one ranking. Raw latency isn't what separates the field at this scale. Three structural things are, and mnestic is the only embedded engine here that gets all three right.

01

It has a graph signal at all

Graph proximity is correlated but distinct from vector and keyword: drop it and you lose recall the other two can't recover. It's the single largest effect in the run, with the graph-less engines (LanceDB) landing far below. This is why graph-augmented retrieval exists.

02

One store, one call, no glue

mnestic serves all three signals from one embedded store and fuses them in a single transactional call. SQLite, DuckDB and Kuzu keep them in one process but fuse in app code (three queries + a hand-rolled RRF); LanceDB fuses natively but needs a second system for graph.

03

Read-your-writes on every signal

An agent writes a memory and must recall it immediately. mnestic's indexes update in the same transaction, giving 100% fused read-your-writes. DuckDB's full-text index is a build-time snapshot: a new memory is unsearchable by keyword (0%) until a rebuild. A static-corpus drag race hides this entirely.

On quality, mnestic hits recall@10 of 0.954, level with DuckDB's 0.957 and far above the graph-less LanceDB (0.501). It's the only engine here that fuses all three signals in one transaction:

Enginerecall@10SignalsFusionFused read-your-writes
mnestic0.954vec · FTS · graphnative · one call100%
duckdb 1.5.30.957vec · FTS · graphapp-side glue0% full-text †
sqlite 0.1.91.000*vec · FTS · graphapp-side glue100%
lancedb 0.330.501vec · FTSnative · no graphn/a — no graph

The native call is the fast path

mnestic's one-call 3-way fusion runs at ~42 ms p50, faster than DuckDB's decomposed path and about 4× faster than hand-decomposing it yourself. It's the only engine here that fuses three signals in a single call; LanceDB's native call covers just two.

Latency, in context

mnestic isn't the lowest absolute latency at this scale, but the numbers hold up off the test wheel. Re-measured on the RocksDB backend it actually runs, with real sentence-transformer embeddings, the decomposed path's tail falls (p99 181 ms vs 258 on the wheel) and the native 3-way call stays around 40 ms. What holds is quality and capability: matching the best indexed engine while fusing a signal the others can't.

† DuckDB's full-text index is a build-time snapshot — its fused read-your-writes is 99% overall but 0% for the keyword leg until a rebuild. Source: the mnestic-benchmarks hybrid suite, summarized in the 0.8.5 changelog. Small scale (40k chunks, 10k entities, 50k edges, dim 384) · 1,000 queries, k=10, 2-hop graph · 2026-05-31 · macOS arm64. Numbers are hardware-specific. Recall@10 is the synthetic text-derived-embedding run on the SQLite-backed wheel, where the vector signal is meaningful by construction; latency is additionally validated on the RocksDB backend with real sentence-transformer embeddings. *SQLite's recall reflects an exact brute-force KNN scan (no ANN index), not a like-for-like indexed search. Kuzu did not complete (extension host offline since its Oct-2025 archival).

Add it to your project

Cargo.toml — the importable name stays cozo
1# default = in-memory + SQLite backends
2cargo add mnestic
3 
4# or, with the RocksDB backend:
5# mnestic = { version = "0.8", features = ["storage-rocksdb"] }
main.rs
1use cozo::DbInstance;
2 
3let db = DbInstance::new("mem", "", "")?;
4db.run_default("?[x] := x in [1, 2, 3]")?;
Python — with LangChain & LlamaIndex adapters
1pip install mnestic # the engine (abi3 wheels)
2pip install langchain-mnestic # LangChain vector store
3pip install llama-index-vector-stores-mnestic

Naming, on purpose

The published crate is mnestic, but the importable library name stays cozo. Every use cozo::… in your existing code, and in downstream crates, keeps working unchanged. A drop-in, not a rewrite.

Credit where it’s due

mnestic is not the official CozoDB and is not affiliated with or endorsed by its authors. All credit for the original design belongs to Ziyang Hu and the Cozo Project Authors.