Skip to content

Memory Backends — Integration Guide

CortexBuild can optionally persist long-term agent memory over the artefacts it already produces (.cortexbuild/analysis/structural.json, .cortexbuild/analysis/GRAPH_REPORT.md, .cortexbuild/recurrence-ledger.yaml). Memory is opt-in and additive: the files in .cortexbuild/ remain the source of truth; memory is a queryable cache seeded from them.

Supporting research: docs/research/CortexBuild/MEMORY_BACKENDS_VALIDATION.md (7-library comparison, drop rationale, positioning).


Decision flow — pick a backend in under 60 seconds

Do you want cross-session memory at all?
│
├─ No  → do nothing. Agents read .cortexbuild/ files directly. (default)
│
└─ Yes → Do you need graph / temporal reasoning over the memory?
         │
         ├─ No  → mem0            (recommended default; in-process, Python)
         │
         └─ Yes → Already run Neo4j?
                  ├─ Yes → graphiti   (temporal knowledge graph)
                  └─ No  → cognee     (Python KG, no Neo4j required)

Already operate a Letta server?   → letta
Want zero new dependencies today? → MCP filesystem server (baseline)

Target: a developer with zero context has working memory in under 5 minutes by following the mem0 section below.


Supported backends

Python-native, in-process, Apache-2.0. Maps 1:1 onto the CortexBuild adapter.

Install

pip install "cortexbuild-core[memory]"

Enable + seed

# Seed memory with this repo's structural + graph artefacts
cortexbuild init run . --with-memory

# Mirror ledger transitions into memory (opt-in, best-effort)
export CORTEXBUILD_MEMORY=1
cortexbuild ledger set-status my-finding implemented

Adapter config (Python)

from cortexbuild_core.memory import get_adapter, seed_from_init

adapter = get_adapter(".")              # user_id is derived from the repo path
seed_from_init(adapter, ".cortexbuild") # add structural.json + GRAPH_REPORT.md
hits = adapter.search("multi-tenant isolation", limit=5)
  • Pick when: you want simple, durable recall with no external services.
  • Avoid when: you specifically need graph/temporal queries (see cognee/graphiti).

cognee — knowledge-graph alternative

Python-native ECL (extract → cognify → load) pipeline with pluggable vector + graph stores. Apache-2.0.

Install

pip install cognee

Adapter config (sketch)

Wrap a cognee-backed client in an object exposing add / search / update / delete / get_all and pass it through:

from cortexbuild_core.memory import MemoryAdapter, repo_hash

adapter = MemoryAdapter(client=my_cognee_client, user_id=repo_hash("."))
  • Pick when: your workflows are knowledge-graph-heavy and you want reasoning over relationships, not just nearest-neighbour recall.
  • Avoid when: you only need lightweight recall — mem0 is simpler.

letta (ex-MemGPT) — if you already run Letta

Self-editing memory + agent runtime. Apache-2.0. Server/runtime model.

Install

pip install letta
  • Pick when: you already operate a Letta server and want CortexBuild to reuse it.
  • Avoid when: you don't want to run a separate server/runtime — CortexBuild already owns orchestration, so letta's runtime overlaps ours.

graphiti — if you already run Neo4j

Best-in-class temporal knowledge graph. Apache-2.0. Requires Neo4j.

Install

pip install graphiti-core
# plus a running Neo4j instance (bolt://...)
  • Pick when: you already have Neo4j and want temporal graph memory that pairs with CortexBuild's dependency graph.
  • Avoid when: you don't want to stand up Neo4j — use cognee for a Python-only KG.

MCP filesystem server — zero-dependency baseline

Works today with no extra Python dependency: an MCP filesystem server exposes the .cortexbuild/ directory to any MCP-capable client as a read surface.

Install

npx -y @modelcontextprotocol/server-filesystem ./.cortexbuild

Wire-up (MCP client config)

{
  "mcpServers": {
    "cortexbuild-memory": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "./.cortexbuild"]
    }
  }
}
  • Pick when: you want something working immediately with zero install of a memory library, or your agent client speaks MCP.
  • Avoid when: you need semantic search / ranking — this is plain file access, not vector recall.

Comparison at a glance

Backend Install Runtime External infra Graph memory Pick when
mem0 pip install "cortexbuild-core[memory]" In-process None Optional Default; simple durable recall
cognee pip install cognee In-process pipeline Pluggable KG-heavy reasoning, no Neo4j
letta pip install letta Server Server + DB Partial You already run Letta
graphiti pip install graphiti-core Library Neo4j ✅ (temporal) You already run Neo4j
MCP filesystem npx … server-filesystem Node MCP server None (Node) Zero-dep baseline / MCP clients

How it fits together

  • Files win. Memory is seeded from .cortexbuild/ artefacts and is fully rebuildable. On any conflict, the committed files are authoritative.
  • One adapter, swappable backends. cortexbuild_core.memory.MemoryAdapter exposes add / search / update / delete / all keyed on user_id = repo_hash. mem0 is the default implementation; the others slot behind the same contract.
  • Opt-in everywhere. No default install pulls in a memory library; CI is unaffected. Enable per-repo with --with-memory and CORTEXBUILD_MEMORY=1.

Shared / hosted memory — egress security boundary (P2-19b/c)

When memory is shared across tools via the hosted backend, every write/erase passes through a single fail-closed gate (RedactingMemoryBackend) before any byte can leave the machine:

  • Redaction on the normal path. Every free-text field that egresses — text, source, and tool — is run through the central P2-23 redactor; a secret-shaped record id is replaced with a deterministic id derived from the redacted content hash. The repo_namespace is the cross-tool sharing key, so it is validated (must be a canonical repo: identity) rather than redacted — and a namespace that contains a secret-shaped token is refused (fail-closed) instead of silently mutated.
  • Provenance origin is required, fail-closed (B1). The caller MUST pass the TRUE provenance origin, derived at record-creation from source classification. If origin is absent / unknown / unclassified, the hosted write is refused (fail-closed to proprietary). The boundary never guesses an origin, and consent never relaxes this rule for proprietary/unknown rule text.
  • Consent is read fresh every write (S1). Consent is loaded from disk on each write; deleting consent.json (revocation) causes the next write to be refused.
  • GDPR erasure is real (B4). A shared learning can be erased by record_id or by its redacted content_sha256, so an operator on a different tool — who has only the content, not the original id — can still erase it.

Threat-model boundary (read this — no over-claiming)

These controls prevent accidental egress of PII / secrets / proprietary rule-text on the normal paths, and confine the hosted SDK import to one module (a grep guard fails the build if mem0 is imported anywhere else, in any import style). Name-mangling and non-export reduce accidental bypass by careless or new call sites — they are not a sandbox. An attacker with arbitrary in-process Python code execution can monkeypatch any function, read os.environ, or open a socket directly; Python cannot prevent that. Defending against arbitrary in-process code is therefore explicitly out of scope and unachievable in Python, and this boundary must not be read as such a defence.