Skip to content

Repository files navigation

CortexDB

Go Reference CI codecov License: MIT

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.

Why CortexDB?

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.

Install & Quick Start

go get github.com/liliang-cn/cortexdb/v2
db, _ := 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})

Layers — pick the right one

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

Knowledge Graph

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 }`})

Ontology

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 as entity:<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.

Current limitations

  • vectorized is declarative only. The flag is stored and validated, but no write path embeds those properties. upsert_entities writes a lexical FNV hash vector into the node regardless of whether an embedder is configured, so a nearest_neighbors predicate over a text query compares across two different vector spaces. Object-set vector predicates are meaningful today only when you pass an explicit query vector.
  • 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-all entity object type (primary key name) and a related_to link type in the schema.
  • modify_object does 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, MCP & Plugin

tools := db.GraphRAGTools()                             // in-process tool calling
server := db.NewMCPServer(cortexdb.MCPServerOptions{})  // MCP server

Tool 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.

Claude Code and Codex plugin

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@cortexdb

Codex 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 server

To 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.

Shared brain — one CortexDB, many agents and machines

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-grpc

On 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.

OpenClaw and Hermes memory plugins

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
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 restart

Run 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.

Other languages (gRPC sidecar)

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-client

Quality

Retrieval 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 & Status

examples/01_core16_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.

About

A pure-Go, single-file AI memory and knowledge graph library and plugin.

Topics

Resources

Contributing

Stars

250 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages