Standalone hybrid memory library and HTTP server extracted from the ZeroClaw agent runtime.
Features:
- SQLite backend with FTS5 keyword search (BM25 scoring), vector embeddings (cosine similarity), and weighted hybrid merge
- Embedding cache with LRU eviction (SHA-256 keyed)
- Session isolation, category filtering
- Snapshot export and cold-boot hydration
- Markdown append-only backend
- OpenAI-compatible embedding provider (any
/v1/embeddingsendpoint) - REST HTTP server hookable from any language
- Python client (
MemoryClient,AsyncMemoryClient)
zeromem/
├── crates/
│ ├── zeromem-core/ # Rust library crate
│ └── zeromem-server/ # HTTP REST server binary
├── python/
│ └── zeromem/ # Python client library
└── README.md
cargo build --release -p zeromem-server
./target/release/zeromemThe server starts on 0.0.0.0:8765 by default.
| Variable | Default | Description |
|---|---|---|
ZEROMEM_BACKEND |
sqlite |
Backend: sqlite, markdown, none |
ZEROMEM_DATA_DIR |
./zeromem_data |
Directory for database files |
ZEROMEM_BIND |
0.0.0.0:8765 |
Listen address |
ZEROMEM_EMBEDDING_PROVIDER |
(empty = noop) | openai, openrouter, custom:<URL> |
ZEROMEM_EMBEDDING_API_KEY |
— | API key for embedding provider |
ZEROMEM_EMBEDDING_MODEL |
text-embedding-3-small |
Embedding model name |
ZEROMEM_EMBEDDING_DIMENSIONS |
1536 |
Vector dimensions |
ZEROMEM_VECTOR_WEIGHT |
0.7 |
Hybrid search vector weight |
ZEROMEM_KEYWORD_WEIGHT |
0.3 |
Hybrid search keyword weight |
ZEROMEM_CACHE_SIZE |
10000 |
Embedding cache max entries |
ZEROMEM_EMBEDDING_PROVIDER=openai \
ZEROMEM_EMBEDDING_API_KEY=sk-... \
ZEROMEM_DATA_DIR=/var/zeromem \
./target/release/zeromemAll memory endpoints are under /api/v1/memories. Health is at /health.
{
"key": "user_language",
"content": "The user prefers Rust for systems programming",
"category": "core",
"session_id": "optional-session-id"
}Response: 204 No Content
Categories: core, daily, conversation, or any custom string.
{
"query": "what programming language does the user prefer",
"limit": 5,
"session_id": "optional-session-id"
}Response: array of MemoryEntry objects with score field.
Response: MemoryEntry or 404.
Query params: category (optional), session_id (optional).
Response: array of MemoryEntry.
Response:
{ "removed": true }Response:
{ "count": 42 }Response:
{ "ok": true, "backend": "sqlite" }{
"id": "uuid-string",
"key": "user_language",
"content": "The user prefers Rust",
"category": "core",
"timestamp": "2026-02-21T10:00:00+00:00",
"session_id": null,
"score": 0.92
}cd python
pip install -e .
# or: pip install httpx zeromemfrom zeromem import MemoryClient
mem = MemoryClient("http://localhost:8765")
# Store
mem.store("user_lang", "Prefers Rust", category="core")
# Hybrid search
results = mem.recall("programming language preference", limit=5)
for r in results:
print(r.key, r.score, r.content)
# Get by key
entry = mem.get("user_lang")
print(entry.content if entry else "not found")
# List all core memories
entries = mem.list(category="core")
# Forget
removed = mem.forget("user_lang")
# Count
n = mem.count()
# Health
ok = mem.health_check()import asyncio
from zeromem import AsyncMemoryClient
async def main():
async with AsyncMemoryClient("http://localhost:8765") as mem:
await mem.store("key", "value", category="conversation", session_id="sess-1")
results = await mem.recall("value", limit=3, session_id="sess-1")
for r in results:
print(r.key, r.score)
asyncio.run(main())Passing a session_id scopes reads and writes to that session — useful for multi-user or multi-agent scenarios.
# Agent A stores with its session
mem.store("task_status", "in progress", category="conversation", session_id="agent-a")
# Agent B cannot see Agent A's session-scoped memories
results = mem.recall("task", session_id="agent-b")
assert results == []
# Agent A can see its own
results = mem.recall("task", session_id="agent-a")
assert len(results) == 1Add to your Cargo.toml:
[dependencies]
zeromem-core = { path = "path/to/zeromem/crates/zeromem-core" }use zeromem_core::{create_memory, MemoryConfig, MemoryCategory};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = MemoryConfig {
data_dir: "./my_data".into(),
embedding_provider: "openai".into(),
embedding_api_key: Some("sk-...".into()),
..Default::default()
};
let mem = create_memory(&config)?;
mem.store("key", "content", MemoryCategory::Core, None).await?;
let results = mem.recall("content", 5, None).await?;
println!("{} results", results.len());
Ok(())
}On startup, if brain.db is missing but MEMORY_SNAPSHOT.md exists in the data directory, the server automatically re-hydrates from the snapshot. To trigger a manual export:
# Via the Rust API (call from your code):
zeromem_core::snapshot::export_snapshot(std::path::Path::new("./zeromem_data")).unwrap();# Library tests
cargo test -p zeromem-core
# Server tests
cargo test -p zeromem-server
# All
cargo test