Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Agent Memory Engine

πŸŒ™ Lunaris

Sub-25 ms recall at 100,000 documents per scope β€” measured β€” with provable atomicity and a graph that's opt-in.

Lunaris is a production-grade agent-memory engine written in pure Rust, with first-class Python (pip install lunaris) and TypeScript (npm i @pilotspace/lunaris) SDKs generated from the same source of truth.

You feed it raw observations β€” chat turns, documents, tool outputs β€” as Episodes. It chunks, embeds, and (optionally) extracts entities, relations, and facts using a small local LLM, then stores everything in a bi-temporal MVCC store backed by Moon, a high-performance Redis-compatible substrate (and, as of 0.7.0, the only backend). Agents query it through a composable retrieval DSL that fuses semantic search, graph traversal, and BM25 keyword lookup, with an optional cross-encoder rerank pass on top.

async fn demo() -> Result<(), lunaris::LunarisError> {
async fn demo() -> Result<(), Box<dyn std::error::Error>> {
use lunaris::{EpisodeBuilder, Lunaris, Query, Scope};

let lunaris = Lunaris::open("moon://127.0.0.1:6380").await?;
let scope   = Scope::new("acme.agent-1")?;
let scoped  = lunaris.scoped(scope);

let lsn  = scoped.ingest(EpisodeBuilder::new("user-msg", "Alice loves chocolate.")).await?;
let hits = scoped.recall(Query::text("what does Alice like?")).await?;
Ok(())
}
Ok(())
}

Want a hybrid plan (vector + BM25, fused, reranked)? Compose it with the retrieval DSL:

use lunaris::{Lunaris, Scope};
async fn demo() -> Result<(), lunaris::LunarisError> {
use lunaris::{Lunaris, Scope};
async fn demo() -> Result<(), Box<dyn std::error::Error>> {
let lunaris = Lunaris::open("moon://127.0.0.1:6380").await?;
let scoped = lunaris.scoped(Scope::new("acme.agent-1")?);
use lunaris::{Keyword, Query, Vector};

let hits = scoped
    .dsl()
    .with_root(Vector::new("chunks", 30).and(Keyword::bm25("chunks", 30)).fuse_rrf(60).top(5))
    .execute(Query::text("what does Alice like?"))
    .await?;
Ok(())
}
Ok(())
}

Use it from your agent (MCP)

Don’t want to write SDK code? Lunaris ships an MCP server so coding agents β€” Claude Code, Codex, or any MCP client β€” get persistent, scope-isolated memory over stdio. Install the binary and register it:

# no Rust toolchain needed β€” both download a prebuilt binary on first run
claude mcp add --transport stdio lunaris \
  -e LUNARIS_MCP_STORAGE=moon://127.0.0.1:6381 \
  -- npx -y @pilotspace/lunaris-mcp
# or: claude mcp add --transport stdio lunaris \
  -e LUNARIS_MCP_STORAGE=moon://127.0.0.1:6381 \
  -- uvx lunaris-mcp

Building from source instead? lunaris-mcp is not on crates.io (it depends on a publish = false crate), so use the git form: cargo install --git https://github.com/pilotspace/lunaris lunaris-mcp.

The agent then calls eleven memory.* tools β€” seven durable-memory tools (ingest, recall, forget, list_scopes, record_decision, record_edit, status) plus four working-memory scratchpad tools (scratchpad_write, scratchpad_read, scratchpad_grep, scratchpad_consolidate):

memory.ingest  source="src:notes"  content="The ingest pipeline writes one atomic_write per episode."
memory.recall  query="ingest atomicity"  k=3

Scope is derived per-repo from the git remote. LUNARIS_MCP_STORAGE must name a Moon β€” there is no default store as of 0.7.0, and the server refuses to boot without one. memory.recall then runs hybrid vector + BM25 recall. See MCP Server for the full guide.

The three moats

Three properties define what Lunaris is. Every commit is reviewed against them; any feature that weakens any of the three is rejected.

MoatWhat it meansWhere enforced
Sub-25 ms p50 recallNo LLM on the recall hot path. Measured p50 19.2–22.4 ms / p99 23.4–24.4 ms at 100k documents per scope, graph OFF, rerank OFF. The opt-in cross-encoder rerank is a quality stage, not a latency-class stage β€” it measures p50 1301.3 ms at top_in=60 and voids this contract when enabled.scripts/bench/perf/recall_latency.sh all β€” a manual, local ~10-minute live-Moon gate. Not CI-enforced: perf-gates.yml is opt-in behind a perf-bench label, is not a required check, and is red on main (capacity.md)
Single atomic_write per ingestAll-or-nothing commit across vector, KV, BM25, queue. Fan-out architectures (Mem0, Zep) can’t make this guarantee.tests/ingest_pipeline.rs::single_atomic_write_call + CI grep gate
Bi-temporal MVCC + HLCBiTemporal { valid, sys } on every primitive, on every backend β€” supersede closes intervals instead of destroying rows. As-of reads are search-side and graph-side (FT.SEARCH AS_OF, GRAPH.QUERY VALID_AT); historical KV reads have no version chain on Moon and are refused explicitly β€” see Core concepts.Required field on Episode, Chunk, Entity, Fact, Relation, Community

If everything else fails, that performance + correctness contract must hold β€” it’s what differentiates Lunaris from Mem0, Zep, and Cognee. See Why Lunaris for the honest β€œuse a different tool when…” criteria.

How this book is organized

  • Getting Started β€” install, a 10-minute quickstart, and the core concepts (episodes, scope, bi-temporal MVCC, the atomic write).
  • Guides β€” one chapter per capability: ingest, the retrieval DSL, forget, the opt-in graph pipeline, consolidation & verification, multi-agent scoping.
  • Cookbook β€” the built-in recipe types (chat agents, document corpora, Slack/email archives, code-repo memory, timelines) as copy-pasteable how-tos.
  • Reference β€” the exhaustive configuration reference (every feature flag and LUNARIS_* env var), the generated API docs, and the error taxonomy.
  • Operations β€” running the HTTP server, choosing a backend, durability & recovery.
  • SDKs β€” Python and TypeScript surface notes.
  • Integrations (MCP) β€” the MCP server and its Claude Code / Codex integration guides.
  • Migrating From β€” Mem0 / Zep / Cognee mapping tables.
  • Protocol β€” the MemoryProtocol 0.1 HTTP/SSE wire spec and its conformance suite.

Source of truth. Where a claim in this book disagrees with the Rust source, the source wins. Many pages carry path:line cross-references back into the crates; the generated API reference is built from cargo doc on every release.