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¶
mem0 — recommended default¶
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.MemoryAdapterexposesadd / search / update / delete / allkeyed onuser_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-memoryandCORTEXBUILD_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, andtool— is run through the central P2-23 redactor; a secret-shaped recordidis replaced with a deterministic id derived from the redacted content hash. Therepo_namespaceis the cross-tool sharing key, so it is validated (must be a canonicalrepo: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. Iforiginis 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_idor by its redactedcontent_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.