Skip to content

Standalone hybrid memory library and HTTP server extracted from the ZeroClaw agent runtime.

Notifications You must be signed in to change notification settings

Stonefish-Labs/zeromem

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zeromem

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/embeddings endpoint)
  • REST HTTP server hookable from any language
  • Python client (MemoryClient, AsyncMemoryClient)

Structure

zeromem/
├── crates/
│   ├── zeromem-core/     # Rust library crate
│   └── zeromem-server/   # HTTP REST server binary
├── python/
│   └── zeromem/          # Python client library
└── README.md

Quick start — server

Build

cargo build --release -p zeromem-server
./target/release/zeromem

The server starts on 0.0.0.0:8765 by default.

Configure via environment variables

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

Example with OpenAI embeddings

ZEROMEM_EMBEDDING_PROVIDER=openai \
ZEROMEM_EMBEDDING_API_KEY=sk-... \
ZEROMEM_DATA_DIR=/var/zeromem \
./target/release/zeromem

REST API

All memory endpoints are under /api/v1/memories. Health is at /health.

POST /api/v1/memories — store

{
  "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.


POST /api/v1/memories/recall — hybrid search

{
  "query": "what programming language does the user prefer",
  "limit": 5,
  "session_id": "optional-session-id"
}

Response: array of MemoryEntry objects with score field.


GET /api/v1/memories/:key — get by key

Response: MemoryEntry or 404.


GET /api/v1/memories — list

Query params: category (optional), session_id (optional).

Response: array of MemoryEntry.


DELETE /api/v1/memories/:key — forget

Response:

{ "removed": true }

GET /api/v1/memories/count — count

Response:

{ "count": 42 }

GET /health — health check

Response:

{ "ok": true, "backend": "sqlite" }

MemoryEntry schema

{
  "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
}

Python client

Install

cd python
pip install -e .
# or: pip install httpx zeromem

Sync usage

from 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()

Async usage

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())

Session isolation

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) == 1

Use as Rust library

Add 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(())
}

Snapshot / disaster recovery

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();

Running tests

# Library tests
cargo test -p zeromem-core

# Server tests
cargo test -p zeromem-server

# All
cargo test

About

Standalone hybrid memory library and HTTP server extracted from the ZeroClaw agent runtime.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published