Correct positioning per the actual product hierarchy:
• zapdb is the canonical Hanzo storage layer — ZAP-native, multi-
language, generic. Canonical home is `zap-proto/db`. Currently
mirrored at `luxfi/zapdb` (Go) and `~/work/luxcpp/zapdb` (C++);
both are in-flight to migrate to `zap-proto/db`.
• luxfi/database is the Lux-flavored extension of zapdb — adds
blockchain concerns (chain heads, archival, validator sets) on top
of the base zapdb. NOT a generic brain backend.
Changes:
• ADVERTISED: rename "luxdb" → "zapdb" with the canonical zap-proto/db
pointer.
• Module header doc: list zapdb in the canonical-backend bullets and
clarify luxfi/database is the Lux extension, not the brain backend.
• README table: zapdb gets the canonical row pointing at zap-proto/db;
luxdb gets a separate row labeled as the Lux extension.
• README "native distributed-SQL story" section: ZAP repos under
zap-proto org enumerated (one per language: c, cpp, go, py, rust,
…). zapdb canonical home noted as zap-proto/db.
• Routing table: multi-machine + team rows reference `zapdb`
(zap-proto/db) instead of `luxfi/database`.
Rust crate mod.rs header similarly corrected to point at zapdb /
zap-proto/db as the canonical native store.
9.0 KiB
@hanzo/bot-memory
The Hanzo Brain memory layer. Pluggable BrainStore contract. SQLite default — single file, zero infra. Path shared across every Hanzo SDK at
~/.hanzo/brain/brain.db.
Canonical artifact locations
Every Hanzo SDK on a machine reads/writes the same paths so brains compose across the bot, hanzo-mcp, hanzo-dev, and the Python SDK:
| Path | What |
|---|---|
~/.hanzo/brain/brain.db |
The brain — pages / edges / facts / FTS5 |
~/.hanzo/workspace/ |
Markdown source — auto-ingested |
~/.hanzo/config.toml |
Per-machine bot/mcp config |
~/.hanzo/cache/ |
Embedding cache, tool-output cache |
~/.hanzo/logs/ |
Structured logs |
What ships in core
- SQLite backend (canonical default) —
~/.hanzo/brain/brain.db. FTS5 keyword. Optional sqlite-vec for ANN. - Pluggable interface —
registerBackend("name", factory). Anyone can ship Qdrant / Meilisearch / Postgres / LanceDB / D1 / libSQL as a sibling extension.
Scale-out backends (sibling extensions, not in core)
Hanzo ships the heavy machinery, all open source — register them by name. Native stack uses ZAP (zero-copy transport) + hanzo-consensus (metastable agent agreement) + zapdb (storage primitives) end-to-end. No libSQL, no Turso — we ship our own.
| Backend | Repo | License | Use |
|---|---|---|---|
qdrant |
~/work/hanzo/vector |
Apache 2 | Vector ANN at scale (millions of embeddings, multi-replica) |
meilisearch |
~/work/hanzo/search |
MIT | Fast keyword FTS when SQLite FTS5 stops being enough |
replicate |
~/work/hanzo/replicate |
Apache 2 | Background SQLite WAL → S3 backup, point-in-time restore |
vfs |
~/work/hanzo/vfs |
Apache 2 | S3-backed virtual block FS with PQ encryption — unlimited write storage |
zapdb |
zap-proto/db (canonical, in-flight migration from luxfi/zapdb) |
Apache 2 | The canonical Hanzo storage layer. ZAP-native primitives, multi-language. Currently mirrored at luxfi/zapdb (Go, badger-derived) and ~/work/luxcpp/zapdb (C++) — both moving to zap-proto/db. |
luxdb |
~/work/lux/database |
BSD-3 | Lux-flavored extension of zapdb. Adds blockchain-specific concerns (chain heads, archival, validator sets) on top of the base zapdb. Don't use for generic brain stores — pick zapdb directly. |
postgres |
(sibling pkg) | Multi-tenant team brain with pgvector |
The native distributed-SQL story (we ship our own — not libSQL/Turso)
For multi-machine SQLite-shaped semantics with native replication, the canonical Hanzo stack is ZAP + hanzo-consensus + zapdb, not libSQL/Turso:
- Wire: ZAP (zero-copy transport) — same protocol the rest of Hanzo speaks. Carries MCP, A2A, and ACP natively too. Repos live under
zap-proto(one per language:zap-proto/c,zap-proto/cpp,zap-proto/go,zap-proto/py,zap-proto/rust, …). - Consensus:
hanzo-consensusfor metastable agent agreement. For storage-level write ordering we run the same metastable primitive on a node quorum (one quorum per brain shard). - Storage —
zap-proto/dbis the canonical home for zapdb. It's the base store, multi-language, ZAP-native. Currently mirrored atluxfi/zapdb(Go) and~/work/luxcpp/zapdb(C++) pending the migration tozap-proto/db.luxfi/databaseis a separate Lux-flavored extension of zapdb that adds blockchain concerns (chain heads, archival, validator sets) — generic brain users wantzapdbdirectly, notluxfi/database. - Backup:
replicateships SQLite WAL → S3 for the solo + small-team cases. - Streaming:
vfsfor unlimited-size brains backed by S3 blocks with PQ encryption.
We deliberately don't pull in libSQL or Turso; they'd force us off our own ZAP+consensus path. The multi-replica zapdb primitive is in-flight at luxfi/zapdb (Go) and ~/work/luxcpp/zapdb (C++), both migrating to zap-proto/db.
Default routing:
- Solo dev, < 100K pages → SQLite + FTS5 (zero infra)
- Solo dev, > 10K pages with vector → SQLite +
sqlite-vec(requires better-sqlite3 driver — bun:sqlite has no extension loading) - Durable solo (offsite backup) → SQLite +
replicateto S3 - Multi-machine personal →
zapdbover ZAP + hanzo-consensus (our native distributed-SQL story; canonical home iszap-proto/db, in-flight migration fromluxfi/zapdb) - Team / Hanzo Node → Qdrant for vectors + Meilisearch for FTS +
zapdbfor facts/edges (or Postgres if multi-tenant + external) - Org-scale streaming → VFS-backed brain.db, lazy block-level fetch from S3, unlimited size
Schema
CREATE TABLE pages (
slug TEXT PRIMARY KEY,
content TEXT NOT NULL,
frontmatter TEXT, -- JSON
updated_at TEXT NOT NULL -- ISO 8601
);
-- FTS5 virtual table over pages.content
CREATE VIRTUAL TABLE pages_fts USING fts5(content, content='pages', content_rowid='rowid');
CREATE TABLE edges (
source TEXT NOT NULL, -- page slug
target TEXT NOT NULL, -- entity slug
type TEXT NOT NULL, -- mentions/attended/works_at/invested_in/founded/advises
evidence TEXT, -- verbatim phrase that triggered inference
PRIMARY KEY (source, target, type)
);
CREATE TABLE facts (
id TEXT PRIMARY KEY, -- subject::predicate::ts
subject TEXT NOT NULL, -- entity slug
predicate TEXT NOT NULL, -- preference/status/decision/replied/…
object TEXT NOT NULL,
source TEXT, -- originating page slug or session id
ts TEXT NOT NULL, -- ISO 8601
confidence REAL DEFAULT 1.0
);
Anyone implementing BrainStore for another backend should mirror this shape — @hanzo/bot-graph-links and @hanzo/bot-recipes-brain Just Work against any conforming store.
Pluggability
import { registerBackend, open } from "@hanzo/bot-memory";
registerBackend("my-store", async (cfg) => {
const store: BrainStore = makeMyStore(cfg);
return store;
});
const store = await open({ backend: "my-store" });
Environment override: HANZO_BRAIN_BACKEND=my-store.
API surface
const store = await open(); // SQLite, ~/.hanzo/brain/brain.db
await store.upsertPage("people/alice", markdown, { type: "person" });
await store.upsertEdges("people/alice", edges);
await store.upsertFact({
subject: "people/alice",
predicate: "preference",
object: "replies in under 2 hours",
});
const facts = await store.recall("people/alice", { limit: 50 });
const hits = await store.hybridSearch("Acme", 5);
const out = await store.edgesFor("people/alice", "out");
Config
interface MemoryConfig {
backend?: string; // default "sqlite"
dataDir?: string; // default ~/.hanzo/brain
dbPath?: string; // explicit file; overrides dataDir
embeddingModel?: string; // for the optional vector ANN path
embeddingApiKey?: string;
}
Driver
- bun:sqlite (canonical — already built into Bun, no install needed)
- better-sqlite3 (fallback for Node-only deployments)
Either works. The init step tries bun:sqlite first.
Tests
cd ~/work/hanzo/bot && bun test extensions/memory
Covers: upsert + read pages, upsert + read edges, upsert + recall facts, FTS hybrid search, pluggability (register a stub backend), unknown-backend error message.