Lunaris — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Lunaris (MCP Server) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Sub-25 ms recall over millions of bi-temporal facts, with provable atomicity and a graph that's opt-in.
A production-grade agent-memory engine in Rust, with first-class Python and TypeScript SDKs and a zero-Rust MCP server for coding agents. Raw observations in; structured, bi-temporal facts out. Backed by Moon (the high-performance Redis-compatible substrate), Postgres, or SQLite (memory:// — zero infrastructure).

Documentation: the full guide lives in the [Lunaris Book](https://pilotspace.github.io/lunaris/) or (mdbook serve docs/booklocally . live). First time here?docs/POSITIONING.mdis the one-page pitch + honest "use a different tool when…" criteria. How it works:docs/ARCHITECTURE.md— the layered design and the Moon advantage map, every claim proof-anchored.
| You are… | Do this | Time |
|---|---|---|
| Giving your AI agent memory (Claude Code, Codex) | Install the MCP server — no Rust toolchain needed | 2 min |
| Building an app in Python / TypeScript / Rust | Install an SDK | 5 min |
| Evaluating against Mem0 / Zep / Cognee | Read POSITIONING.md, then the migration doc for your tool | 10 min |
The MCP server gives any MCP-capable agent eleven memory tools — seven durable-memory tools (memory.ingest, memory.recall, memory.forget, memory.list_scopes, memory.record_decision, memory.record_edit, memory.status) plus four working-memory scratchpad tools (memory.scratchpad_write, memory.scratchpad_read, memory.scratchpad_grep, memory.scratchpad_consolidate). Both install paths download a prebuilt native binary on first run — no Rust toolchain required (linux-x64/arm64, darwin-x64/arm64, win32-x64).
Claude Code — one command, either runner:
claude mcp add --transport stdio lunaris -- npx -y @pilotspace/lunaris-mcp
# or
claude mcp add --transport stdio lunaris -- uvx lunaris-mcpAny MCP client — JSON config:
{
"mcpServers": {
"lunaris": { "command": "npx", "args": ["-y", "@pilotspace/lunaris-mcp"] }
}
}From a repo checkout (adds lifecycle hooks for automatic capture + context injection, Codex included):
scripts/setup-lunaris-agents.py --agent both --runner npx # or: uvx | localA plain npx/uvx/cargo install lunaris-mcp install defaults to a zero-config per-scope SQLite store — recall works out of the box via brute-force cosine, no external process. Point LUNARIS_MCP_STORAGE at Moon (moon://127.0.0.1:6380) or Postgres for HNSW-class latency beyond ~10k vectors per scope. The setup-lunaris-agents.py repo path instead defaults its agent config to Moon (the lifecycle hooks use Moon's queues for background embedding; pass --storage-backend sqlite to opt out). A source build with --features embedded-moon auto-launches an in-process Moon — an opt-in, not the published-binary default. First ingest stages the embedder weights once (lazy GGUF download).
Full guides: docs/integration/claude-code.md · docs/integration/codex.md · docs/integration/hooks.md
Paste this into your CLAUDE.md / AGENTS.md so your agent uses the memory deliberately:
## Memory (Lunaris MCP)
- Persist durable facts, decisions, and user preferences with
`memory.ingest`; record code decisions with `memory.record_decision`
and notable edits with `memory.record_edit`.
- Before answering questions about prior work, query `memory.recall`.
- Use `memory.scratchpad_write`/`scratchpad_read`/`scratchpad_grep` for
transient working notes within a task (drafts, plans, in-progress state);
promote the durable ones with `memory.scratchpad_consolidate` (needs a
Moon/Postgres backend).
- Memory is partitioned by scope — never mix scopes; list with
`memory.list_scopes`. Use `memory.forget` when asked to delete.
- Check backend health with `memory.status` if recall returns nothing.# Python (3.11+)
pip install lunaris
# TypeScript (Node 20+)
npm install @pilotspace/lunaris
# Rust — published as `lunaris-memory`; import as `lunaris`
cargo add lunaris-memory --rename lunarisPython — ingest + recall in one file (from the SDK guide):
import asyncio, lunaris, ulid
async def main():
handle = await lunaris.open("moon://127.0.0.1:6380") # or "memory://"
lsn = await handle.ingest({
"id": str(ulid.ULID()), "source": "quickstart",
"content": "Alice loves chocolate.", "metadata": {}, "t_ref": None,
"bt": {"valid": [{"wall_ms": 0, "counter": 0, "node_id": 0}, None],
"sys": [{"wall_ms": 0, "counter": 0, "node_id": 0}, None]},
})
hits = await lunaris.RetrievalBuilder().bind(handle).top(5).execute()
print(lsn, hits)
asyncio.run(main())TypeScript — same shape (SDK guide):
import { open, RetrievalBuilder } from "@pilotspace/lunaris";
const handle = await open("moon://127.0.0.1:6380"); // or "memory://"
const lsn = await handle.ingest(episode); // same episode shape as Python
const hits = await new RetrievalBuilder().bind(handle).top(5).execute();Rust — the typed surface:
use lunaris::{EpisodeBuilder, Lunaris, Scope};
let lunaris = Lunaris::open("moon://127.0.0.1:6380").await?;
let scoped = lunaris.scoped(Scope::new("acme.agent-1")?);
let lsn = scoped.ingest(EpisodeBuilder::new("user-msg", "Alice loves chocolate.")).await?;The connection URL is the only backend switch: moon:// (latency flagship), postgres:// (portability, RLS-isolated), memory:// / sqlite:///path (zero infrastructure). Embedding and reranking run in-process on CPU (candle-native granite-embedding-311m + bge-reranker-v2-m3) — no embedding API, no network on the hot path; the model weights are staged once on first use. Quantized GGUF and air-gapped options: configuration reference.
Runnable examples: examples/quickstart-py/ · examples/quickstart-ts/ · examples/quickstart-rs/ · examples/multi-agent-rs/
Three properties define what Lunaris IS. Every commit is reviewed against them; any feature that weakens any of the three is rejected.
| Moat | What it means | Where enforced |
|---|---|---|
| Sub-25 ms p50 recall | No LLM on the recall hot path. Measured strict-replay: p50 10.3 ms / p99 20.8 ms (methodology); k=30 hydration tail p50 6.0 ms / p99 6.2 ms after the concurrent-hydration fan-out (A/B). | cargo bench --bench recall_hot_path |
| Single `atomic_write` per ingest | All-or-nothing commit across vector, KV, BM25, graph, audit, 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 + HLC | BiTemporal { valid, sys } on every primitive. "What did the agent know at time T" is a query, not a rebuild. | Required field on Episode, Chunk, Entity, Fact, Relation, Community |
Surface (SDKs / HTTP / MCP / hooks) → engine pipelines (ingest, retrieval DSL, opt-in graph + consolidation + verification) → one storage trait → three backends. The retrieval DSL fuses vector, keyword (BM25), and graph lanes with RRF in a single typed expression — and on Moon, the fusion and the time-travel cut execute inside the substrate.
Full tour with diagrams: docs/ARCHITECTURE.md and the book's Architecture at a Glance.
The conventional agent-memory stack is three databases and a broker: a vector DB, a graph DB, a relational store, and a queue — four failure domains with no transaction spanning them. Moon collapses all four lanes into one process, so each Lunaris feature maps onto something the substrate does natively instead of a layer bolted on top:

TXN.BEGIN / TXN.COMMIT commit every lane at once; no half-written memory.FT.SEARCH + native RRF fuse vector + keyword in one round trip.AS_OF / VALID_AT make "what did the agent know at T?" a query, not a rebuild.GRAPH.QUERY (Cypher): relationships without running Neo4j.FT.INVALIDATE_RANGE erases a whole time range, no scan-and-delete loop.Same job as Mem0, Zep, and Cognee — different guarantees. The single substrate is why several rows below are a ✓ for Lunaris where the fan-out tools manage only a partial or an ✗:

Every cell is sourced from the comparison table in Why Lunaris; the full advantage map — each claim anchored to a code path — lives in docs/ARCHITECTURE.md. One honest caveat: plain key-value point reads aren't natively temporal and index schemas are fixed at creation, so the architecture page lists every limit beside every win.
Every Lunaris operation is partitioned by Scope — a validated newtype enforced at compile time and at the storage boundary (Postgres RLS with WITH CHECK, per-scope Moon keyspaces + indices). Cross-scope reads are a type error. See RFC 0001.
let scope_a = Scope::new("acme.agent-1")?;
let scope_b = Scope::new("acme.agent-2")?;
// Same ULID, different scopes — two distinct rows. No leak.
lunaris.scoped(scope_a).ingest(builder.clone()).await?;
lunaris.scoped(scope_b).ingest(builder).await?;| Milestone | Status |
|---|---|
| v0.2.1 — multi-agent partitioning | Shipped 2026-05-11 |
| v0.4 — candle-native ML default | Shipped 2026-05-14 — in-process embedder + reranker, Ollama path removed |
| v0.4.0 — MCP surface + embedded Moon + RAPTOR | Shipped 2026-06-13 — lunaris-mcp scratchpad tools, embedded Moon, RAPTOR tree retrieval, recall fan-out p50 12→6 ms, hybrid filter push-down |
| v0.5.0 — framework adapters + memory convergence + Apache-2.0 | Shipped 2026-06-16 — lunaris_integrations LangGraph/CrewAI/Letta adapters, write-time dedup + cross-episode supersede, relicensed Apache-2.0 |
| v0.5 — proactive capture + packaging | Shipped 2026-05-26 — lunaris-hook lifecycle capture, MCP polish, npx/uvx distribution |
| v0.6 — adaptive chunking + RAPTOR | In progress on main — hierarchical memory tree, .tree() retrieval operator |
| MCP working memory + embedded Moon | Merged to main 2026-06-09 — four memory.scratchpad_* tools, guarded scratchpad_consolidate, opt-in --features embedded-moon |
See CHANGELOG.md for the full history.
code-side comparisons (ingest, recall, time-travel, forget), a 5-step incremental migration plan, honest "stay on Mem0 if…" criteria.
already has bi-temporal facts; the conversation is latency + substrate simplification.
pipeline-vs-DSL tradeoff: if your custom logic lives at ingest time, Cognee's Task model maps cleaner; at recall time, Lunaris's operator DSL is simpler.
0.3 → 0.4 native-default cutover and the 0.4 → 0.5 release notes.
Licensed under the Apache License, Version 2.0. See LICENSE for the full text.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.