A pure-Go, single-file AI memory and knowledge graph library and plugin. Use CortexDB as an embedded memory/KG layer in your own Go agent projects, or install it as a shared memory brain for Claude Code and Codex. SQLite is the kernel — one file holds vectors, lexical/RAG search, scoped agent memory, an RDF/SPARQL/RDFS/SHACL knowledge graph, and MCP tools. Works with no embedder (lexical mode) or any OpenAI-compatible embeddings endpoint.
Use CortexDB when you want an agent memory layer that is embedded, inspectable, and graph-aware without standing up more infrastructure.
| If you were considering... | CortexDB gives you... | Trade-off |
|---|---|---|
chromem-go or a small embedded vector store |
Vectors plus lexical search, durable knowledge, scoped memory, RDF/SPARQL, and MCP tools in one SQLite file | More surface area if all you need is a tiny vector collection |
sqlite-vec or raw SQLite extensions |
A Go facade for RAG, memory, hybrid retrieval, graph facts, and agent tools | Less low-level SQL control than wiring extensions yourself |
| Chroma, Qdrant, LanceDB, or a hosted vector DB | No service to run, no separate storage plane, and lexical mode with no API key | Not trying to be a distributed vector database |
| Fuseki, GraphDB, Stardog, or a standalone graph DB | Enough RDF/SPARQL/RDFS/SHACL for local-first agent workflows, next to the text and memory store | Not a full enterprise RDF server |
| Custom memory tables for Claude Code/Codex | A packaged plugin, MCP server, auto-recall path, and reusable memory/KG tools | Bring your own product-specific memory policy |
Planning a launch or community post? See docs/LAUNCH_KIT.md for ready-to-edit Show HN, Reddit, and demo scripts.
go get github.com/liliang-cn/cortexdb/v2db, _ := cortexdb.Open(cortexdb.DefaultConfig("KnowledgeMemory.db"))
defer db.Close()
q := db.Quick()
_, _ = q.Add(ctx, []float32{0.1, 0.2, 0.9}, "SQLite is a single-file database.")
hits, _ := q.Search(ctx, []float32{0.1, 0.2, 0.8}, 1)
// No-embedder RAG (lexical):
_, _ = db.SaveKnowledge(ctx, cortexdb.KnowledgeSaveRequest{
KnowledgeID: "apollo", Content: "Alice owns Apollo. Apollo ships Friday."})
resp, _ := db.SearchKnowledge(ctx, cortexdb.KnowledgeSearchRequest{
Query: "Who owns Apollo?", RetrievalMode: cortexdb.RetrievalModeLexical, TopK: 3})pkg/cortexdb Main facade: vectors, text/RAG search, knowledge, memory, KG, tools, MCP. ← start here
pkg/memoryflow Agent memory workflow: transcript ingest, recall, wake-up layers, promotion.
pkg/graphflow Corpus → extract → build → analyze → report → export (HTML).
pkg/importflow Import CSV / SQL dumps / live Postgres-MySQL into RAG + KG (DDL → graph).
pkg/connector Privacy gate over importflow: PII masking, signed plan, reversible vault, CDC sync.
pkg/graph Low-level RDF/SPARQL/RDFS/SHACL + property graph.
pkg/core SQLite storage, embeddings, FTS5, vector indexes (HNSW/IVF/Flat).
Embedded RDF on the same file: triples/quads, namespaces, N-Triples/Turtle/TriG I/O, a practical SPARQL subset (SELECT/ASK/CONSTRUCT/DESCRIBE, updates, OPTIONAL/UNION/MINUS/VALUES/BIND/FILTER, aggregates, subqueries, property paths ^p p|q p+ p*), RDFS-lite materialized inference, and SHACL-lite validation.
db.UpsertKnowledgeGraph(ctx, cortexdb.KnowledgeGraphUpsertRequest{Triples: triples})
res, _ := db.QueryKnowledgeGraph(ctx, cortexdb.KnowledgeGraphQueryRequest{
Query: `SELECT ?name WHERE { <https://example.com/alice> <https://schema.org/name> ?name }`})CortexDB models a Palantir-style ontology on the same file: typed object types with a mandatory primary key, link types with per-side cardinality, interfaces for polymorphic retrieval, a composable object set algebra, and governed writes through action types. Runnable end to end in examples/16_ontology.
_, err := db.SaveOntologySchema(ctx, cortexdb.OntologySaveRequest{
Schema: cortexdb.OntologySchema{
SchemaID: "aviation",
InterfaceTypes: []cortexdb.OntologyInterfaceType{{APIName: "Facility"}},
ObjectTypes: []cortexdb.OntologyObjectType{{
APIName: "Airport",
PrimaryKey: "iataCode", // mandatory: it is what gives an object identity
TitleProperty: "facilityName",
Implements: []string{"Facility"},
Properties: []cortexdb.OntologyProperty{
{APIName: "iataCode", DataType: cortexdb.OntologyDataType{Kind: cortexdb.OntologyDataString}, Required: true},
},
}},
},
Activate: true,
})One schema at a time is active. What activation does depends on the schema's enforcement:
"strict"(the default) validates every write: unknown object types, unknown properties, missing required values and values that do not parse are rejected. Nodes written under it are identified asentity:<objectType>:<primaryKey>; with no active schema the older name-derived IDs still apply."vocabulary"keeps the schema as a shared vocabulary without gating writes: declared type spellings are canonicalized and interfaces expand for retrieval, but an entity that cannot state its primary key — the normal case for LLM extraction from prose — falls back to the name-derived ID instead of being refused, and undeclared types and link types pass through. Use this for extraction pipelines; strict enforcement would force them to choose between activating the schema and keeping their entities.
strict_actions and enforcement: "vocabulary" are mutually exclusive — one closes the generic write path, the other promises never to.
Object types carry api_name, display_name, plural_display_name, description, status, visibility, primary_key (required), title_property, implements and typed properties. Data types: string, integer, long, double, decimal, boolean, date, timestamp, geopoint, geoshape, vector, array, struct, marking. A property may be marked searchable (routed into FTS5) or vectorized. shared_properties lets one definition be declared once and reused by name across object types and interfaces.
Link types are bidirectional, with two sides that each carry their own api_name and a cardinality of ONE or MANY. A one-to-many link is one ONE side and one MANY side; only the ONE side may name a foreign_key_property.
Interfaces give polymorphism: an object set or find_nodes query against Facility returns every implementing object type. Interfaces may extend other interfaces, an object type may implement several, and inheritance cycles are rejected at save time. An interface may not share a name with an object type — names resolve case-insensitively in one namespace, so Gateway the interface and Gateway the object type would be one ambiguous lookup; SaveOntologySchema rejects the collision at save time.
Object sets compose retrieval — vector search, full-text search and graph traversal as peers in one expression rather than three APIs:
resolved, err := db.ResolveObjectSetObjects(ctx, cortexdb.ObjectSetResolveRequest{
ObjectSet: cortexdb.ObjectSet{
Kind: cortexdb.ObjectSetIntersect,
Operands: []cortexdb.ObjectSet{largeFacilities, airportsNearLondon},
},
})Kinds: base, interface_base, static, reference (a saved set on the schema), filter, search_around, union, intersect, subtract. Filter predicates: eq, lt, lte, gt, gte, in, is_null, contains, starts_with, contains_all_terms, contains_any_term, nearest_neighbors, and the boolean operators and, or, not. At most three chained search_around hops, matching Foundry's limit.
Action types are governed, auditable writes: typed parameters, edit rules (create_object, modify_object, create_or_modify_object, delete_object, create_link, delete_link), and submission criteria. Set validate_only to check parameters and criteria without writing, or return_edits to get the graph edits back — the two are mutually exclusive. Validation never consults the graph, so it cannot report a primary-key collision. Every applied action is recorded in an audit trail. Setting strict_actions: true on the schema closes the generic upsert tools, making actions the only write path.
Typed tools turn the schema into an agent-callable surface — one tool per action type, optionally one list tool per object type, with real JSON Schema types instead of a free-text blob:
tools, err := db.GenerateOntologyTools(ctx, cortexdb.OntologyToolGenOptions{IncludeObjectTypes: true})The result is capped (32 by default) and is deliberately not registered with NewMCPServer. OSDK 1.x grew generated code with the ontology; here the same growth would land on the agent's context window on every request, so exposing these is the caller's explicit decision.
Schema diff answers what applying a new version would invalidate, before it is applied:
diff, err := db.DiffOntologySchema(ctx, cortexdb.OntologyDiffRequest{SchemaID: "aviation", Candidate: candidate})Breaking: a removed object or link type, a removed property, a changed property data type, a property that became required, a new required property, a changed primary key, a retargeted link side, and a cardinality tightened from MANY to ONE. Non-breaking additions and relaxations are reported too, flagged as safe. Both sides are expanded through their shared properties first, so retyping a shared property is visible.
Tools: ontology_save, ontology_get, ontology_list, ontology_delete, ontology_diff, ontology_action_list, ontology_action_apply, object_set_resolve.
vectorizedis declarative only. The flag is stored and validated, but no write path embeds those properties.upsert_entitieswrites a lexical FNV hash vector into the node regardless of whether an embedder is configured, so anearest_neighborspredicate over a text query compares across two different vector spaces. Object-set vector predicates are meaningful today only when you pass an explicit queryvector.- An active ontology constrains
SaveKnowledge. It always runs its built-in heuristic extractor, whose entities are untyped, and write-path validation rejects them. If you want both, declare a catch-allentityobject type (primary keyname) and arelated_tolink type in the schema. modify_objectdoes not rewrite the node's display title. Changing the title property through a modify rule updates the property but leaves the stored title, so name-based endpoint resolution still finds the pre-rename name.- Deliberately not modelled: Foundry's function runtime, branches and proposals, dynamic row-level security, and backing datasources. Those need a platform CortexDB is not trying to be.
tools := db.GraphRAGTools() // in-process tool calling
server := db.NewMCPServer(cortexdb.MCPServerOptions{}) // MCP serverTool groups: GraphRAG (ingest_document, search_text, build_context), knowledge/memory (knowledge_save, memory_search, …), KG (knowledge_graph_query, _shacl_validate), KnowledgeMemory (knowledge_memory_recall, _reflect). memoryflow/graphflow/importflow/connector expose their own toolboxes too.
Give Claude Code (and Codex) durable memory + a knowledge graph as a plugin. It bundles the cortexdb skill plus a live MCP server, runs in no-embedder lexical mode by default (no API key, no Go toolchain — the server binary is fetched from the matching release), and stores everything in one global SQLite file shared by every project.
Install — Claude Code — run each as a slash command:
/plugin marketplace add liliang-cn/cortexdb
/plugin install cortexdb@cortexdb
/reload-plugins
Install — Codex — run in your shell:
codex plugin marketplace add liliang-cn/cortexdb
codex plugin add cortexdb@cortexdbCodex uses the same default global brain at ~/.cortexdb/cortexdb.db.
Use — just talk to Claude; it calls the MCP tools for you ("remember that I prefer …", "what do you know about X?"). Or use the slash commands: /remember <text>, /recall <query>, /cortexdb-graph (interactive knowledge-graph view), or /cortexdb for the skill. Key tools: memory_save / memory_search, knowledge_save / knowledge_search, knowledge_graph_query, and the unified knowledge_memory_recall. When enabled, a SessionStart directive + UserPromptSubmit auto-recall hook make Claude recall and save proactively (it asks once, per machine).
Where data lives — ~/.cortexdb/cortexdb.db by default, so memory follows you across projects (multiple sessions share it safely via SQLite WAL). Override per project:
export CORTEXDB_PATH=.cortexdb/cortexdb.db # inherited by the launched serverTo force the same global brain explicitly, set:
export CORTEXDB_PATH="$HOME/.cortexdb/cortexdb.db"To upgrade: /plugin update cortexdb then /reload-plugins — the server binary auto-refreshes (version-pinned cache). See plugins/cortexdb/README.md for all env vars.
By default every agent opens its own SQLite file. Point them at one central
cortexdb-grpc instead and Claude Code, Codex, OpenClaw and agents in other VMs
read and write the same memory and knowledge graph.
On the host that owns the database:
CORTEXDB_PATH=$HOME/.cortexdb/cortexdb.db \
CORTEXDB_GRPC_ADDR=10.0.0.5:47821 \
CORTEXDB_GRPC_TOKEN=<token> cortexdb-grpcOn every client:
export CORTEXDB_REMOTE="10.0.0.5:47821"
export CORTEXDB_GRPC_TOKEN="<the same token>"That is the whole change. The MCP server then opens no local database: it
discovers the tool surface from the server at startup and proxies every call, so
all tools — current and future — work identically. The UserPromptSubmit
auto-recall hook follows the same remote, so injected memories come from the
same brain the tools write to, as do --memory-html and --export-memory.
Transport is plaintext by design — run it over loopback, a trusted LAN, or
Tailscale. The token is the access control: anyone holding it has full
read/write access. Embedder and LLM settings live on the server, not the
clients. --graph-html reads the shared brain too; the remaining one-shot modes
(--export-memory, --learn-path) still act on a local database.
The graph view is also an MCP tool, render_graph_html. It is the one tool that
is not proxied to the shared brain: the graph is read remotely, but the HTML
is rendered and written where the MCP server runs, because the caller needs the
file on its own filesystem to open or attach it — a server-side render would
land it on the brain's host, out of reach of whatever asked. Set
CORTEXDB_VIEW_DIR to choose where renders go.
CortexDB also ships native memory-layer adapters for agents that expose a memory
lifecycle API. Both use the existing gRPC sidecar and unified
knowledge_memory_recall; they do not add a parallel storage path.
- OpenClaw:
liliang-cn/openclaw-cortexdb-memoryregisters the exclusivememorycapability plus recall/store/delete tools. - Hermes Agent:
liliang-cn/hermes-cortexdb-memoryregisters aMemoryProviderwith automatic pre-turn recall and completed-turn capture.
# OpenClaw
openclaw plugins install npm:cortexdb-openclaw-memory@2.57.1
openclaw config set plugins.slots.memory cortexdb-memory
openclaw gateway restart
# Hermes Agent
hermes plugins install liliang-cn/hermes-cortexdb-memory --enable
hermes config set memory.provider cortexdb
hermes gateway restartRun cortexdb-grpc, then install the adapter. The existing
skills/ remain useful for explicit tool instructions and helper
functions, but a skill alone does not replace the host agent's native memory
backend.
cortexdb-grpc serves the full facade over gRPC, with typed clients for Rust/Python/Node:
go install github.com/liliang-cn/cortexdb/v2/cmd/cortexdb-grpc@latest
CORTEXDB_PATH=my.db CORTEXDB_GRPC_TOKEN=s3cret cortexdb-grpc # 127.0.0.1:47821
cargo add cortexdb-client # pip install cortexdb-client # npm install cortexdb-clientRetrieval quality is measured, not assumed: pkg/eval runs a labeled query set through the real retrieval path and reports recall@k / precision@k / MRR / nDCG, with regression floors in CI (go test ./pkg/eval -run TestLexicalRetrievalQuality -v). Parser/search surfaces (FTS5, SPARQL, SQL-dump import) have Go fuzz tests (go test ./... -run Fuzz); their saved corpora are permanent regression seeds.
examples/01_core … 16_ontology are small and architecture-oriented (go run ./examples/01_core); 01-07/09/15/16 run standalone, others need an LLM/embeddings/live DB — see examples/README.md.
An embedded local-first AI memory/KG library — not a drop-in replacement for Fuseki/GraphDB/Stardog. One file, Go APIs, tool/MCP surfaces, and enough RDF/SPARQL/RDFS/SHACL to build real memory workflows.