Hanzo Brain: Local-First Knowledge Graph for Agents

A single SQLite file your agent reads from every runtime. Zero LLM round-trips for graph ops, FTS5 plus vector plus typed-edge extraction at write time.

Your agent is smart but forgetful. We fixed that. Today we ship Hanzo Brain — a single binary, a single SQLite file at ~/.hanzo/brain/brain.db, and the same algorithm surface in TypeScript, Python, Rust, Go, and C++. Drop a markdown file into ~/.hanzo/workspace/. The brain ingests it, extracts typed edges, and indexes the content for hybrid retrieval. No LLM is called. The graph compounds.

This is the memory layer for every Hanzo runtime — the bot, the MCP server, the Hanzo Node, and anything you embed in your own host.

What the brain is

The brain is three things in one process:

  1. Pages — content addressed by slug, written from anywhere, watched on disk.
  2. Edges — typed relationships (mentions, attended, works_at, invested_in, founded, advises) extracted on every write by regex + role inference. Zero LLM.
  3. Facts(subject, predicate, object, ts, confidence) tuples queryable in real time across sessions.

Pages get an FTS5 index. Edges get a primary key over (source, target, type). Facts get an id and a timestamp. The schema is short enough to keep in your head:

CREATE TABLE pages (slug TEXT PRIMARY KEY, content TEXT, frontmatter TEXT, updated_at TEXT);
CREATE VIRTUAL TABLE pages_fts USING fts5(content, content='pages', content_rowid='rowid');
CREATE TABLE edges (source TEXT, target TEXT, type TEXT, evidence TEXT, PRIMARY KEY (source, target, type));
CREATE TABLE facts (id TEXT PRIMARY KEY, subject TEXT, predicate TEXT, object TEXT, source TEXT, ts TEXT, confidence REAL);

That's the whole contract. Every backend honors it. Every runtime writes a byte-identical file.

Why zero-LLM extraction matters

LLM-based extractors burn tokens, latency, and money on every write. They produce drift between runs. They make the graph non-deterministic.

The brain extracts edges with regex and role inference at write time. It is fast, it is free, and it produces the same edges every time given the same input. When you ingest "Alice founded Acme. She invested in Foobar," you get:

people/alice -[founded]->     companies/acme
people/alice -[invested_in]-> companies/foobar

Every time. On every runtime. No round-trip.

If you want LLM enrichment on top — entity disambiguation, claim verification, summarization — you call your model on top of the deterministic graph. The graph is the substrate. The LLM is the optional finisher.

One file. Every runtime.

The same brain.db opens in TypeScript, Python, Rust, Go, and C++. No format translation, no migration. If you write from hanzobot/go on a server and read from @hanzo/bot-memory in a browser worker, you read the file the server wrote.

FeatureTSPythonRustGoC++
BrainStore interfaceyesyesyesyesyes
SQLite (default)yesyesyesyesyes
FTS5 hybrid searchyesyestraityesyes
Graph-links extractoryesyesyesyesyes
Recipe loaderyesyesyesyesyes
Pluggable backendsyesyesyesyesyes

Cross-runtime test totals at launch: 121 TS + 53 Python + 58 Go + 38 Rust (mcp) + 38 Rust (node) + 98 C++ = 406 tests, all green. Each runtime ships the same fusion, the same rerank (MMR), the same RRF, the same slug normalization, the same edge extractor.

Queryable from MCP

The brain is exposed through MCP as three tools:

  • brain.recall <entity> — every fact ever asserted about an entity, with timestamps.
  • brain.search <query> — hybrid FTS + vector, fused with RRF, optional MMR rerank.
  • brain.ingest <slug> <content> — write a page, auto-extract edges, update FTS.

Every agent that talks to a Hanzo Node, the Hanzo MCP server, or a bot built on hanzobot/* gets these tools for free. No sidecar, no extra service, no separate database to operate.

For the Go single-binary path:

go install github.com/hanzobot/go/cmd/hanzo-bot@latest

hanzo-bot brain init                  # opens ~/.hanzo/brain/brain.db
hanzo-bot brain ingest people/alice.md
hanzo-bot brain recall people/alice
hanzo-bot brain search "founded"

For Python:

from hanzo_memory.graph_links import extract_edges
edges = extract_edges(slug="people/alice", content="Alice founded Acme.")

For Rust, embedded inside hanzo-mcp:

use hanzo_mcp::brain::{extract_edges, slugify};
let edges = extract_edges("people/alice", "Alice is CEO of Acme.", Some("person"));

Pluggable storage, same contract

SQLite is the default. It is enough for the first hundred thousand pages on a laptop. When you outgrow it, register a different backend without changing application code:

BackendWhen
sqlitesolo, < 100K pages
qdrantvector ANN at scale
meilisearchkeyword FTS at scale
zapdbcanonical multi-language native store
replicateSQLite WAL streamed to S3 for backup
vfsS3 streaming block FS, unlimited size
postgresmulti-tenant team deployments

For multi-machine SQLite-shaped distributed semantics we use ZAP transport + Quasar consensus + zapdb. The brain stays the same. The bytes-in-bytes-out contract holds.

Compounding memory

The point of the brain isn't a database. It is the assumption shift. Your agent doesn't start every session blank. Markdown you wrote a year ago is already a graph. Conversations from yesterday have produced edges and facts that today's session can query without paying tokens for it. Two agents on two machines can share brain.db over replicate and converge on the same view.

That is what we mean by compounding memory. The graph grows. The model gets cheaper. The user sees an assistant that remembers.

Get the brain

  • TypeScript canon (ships with the bot): npm install -g @hanzo/bot && hanzo-bot serve
  • Python: pip install hanzo-memory
  • Go single binary: go install github.com/hanzobot/go/cmd/hanzo-bot@latest
  • Rust: workspace member of hanzoai/node and hanzoai/mcp
  • C++: header-only at hanzobot/cpp/include/hanzo/brain/

Source: github.com/hanzoai/brain. License: MIT.


One file. Five runtimes. Zero LLM calls for graph operations. Local-first, by design.

Read more