Design Principles
The decisions that shape how MemexAI stores, retrieves, and isolates memory — and why.
MemexAI makes a small number of opinionated decisions that affect everything downstream. Understanding them helps you structure memory well and predict behavior when things are working correctly.
Files, not embeddings as the source of truth
Memory is Markdown text stored as rows in Postgres — not embedding vectors in a separate store.
This means memory is human-readable, grep-able, patchable by a script, and auditable with a SQL query. You can open any memory file in the admin UI and understand exactly what the agent knows. You can also run eval fixtures against a known memory state and get reproducible results.
BM25 and memory-link traversal cover most retrieval cases in practice. When you need semantic recall, optional pgvector hybrid search is available via MEMEX_SEARCH_MODE=auto — see Docker service setup. In hybrid mode, memory_find uses vector ranking merged with BM25 via RRF, and memory_context inherits this automatically. The Markdown file remains the canonical memory record.
Path-enforced isolation
Multi-tenancy is enforced in code, not prompts.
When an agent calls a memory tool, MemexAI validates the virtual path and injects the userId before touching the database. user/profile.md physically becomes users/{userId}/profile.md. Agents cannot construct a path that reaches another user's data, regardless of what the model generates.
Shared memory is read-only for agents by default through the same mechanism. A write to shared/anything.md is rejected at the path validation layer unless the deployment explicitly enables MEMEX_SHARED_WRITE_MODE=rw.
Deterministic retrieval
memory_find returns the same files for the same query on the same data and search mode, every time.
Retrieval is BM25 full-text search for initial candidates, or BM25 plus pgvector RRF when service hybrid mode is active. memory_find is a pure search primitive — it returns ranked file metadata and snippets and does not follow memory links. Link traversal happens in memory_context, where a model call decides which linked files are relevant to the query.
This makes retrieval testable. You can write an eval that seeds a known memory state and asserts exactly which files are returned and why.
Memory links keep context connected
Memory links let one durable file point at another without copying the same facts everywhere.
Wikilinks are plain text — an agent writing [[user/preferences.md]] anywhere in a file's content creates a forward link that memory_context will follow during retrieval. There is no special syntax beyond the [[path]] convention. When memory_context reads a file that contains [[user/preferences.md]], it uses that as a signal to fetch preferences.md as part of the same retrieval pass.
memory_remember is also prompted to write ## See also sections with [[...]] wikilinks when creating related files, so the link graph grows naturally as agents write memory.
Bidirectional backlinks and hub scoring are planned next: notes that point back to a hub file will be able to surface as inbound context, and highly referenced files will be able to rank higher in search.
Writes are permanently recorded
Every write creates a full content snapshot in mx_revision. Nothing is silently overwritten.
Agents can correct memory — a later write can update or contradict an earlier one. But the previous version is always recoverable. The access log records every read and write, including which tool call caused it.
This supports post-hoc debugging: when an agent gives a wrong answer, you can inspect the exact memory state it read and trace every write that produced that state.
Admin and agent have separate trust levels
Agents and operators are not the same thing. MemexAI enforces this structurally.
- Agents authenticate with an API key and can always write to
user/*. They can readshared/*, and can write it only when the deployment enables shared writable mode. - Operators authenticate with an admin secret and can read or write any file, inspect revisions, manage config, and control dreaming behavior.
The boundary is not advisory — it is enforced at the route and path validation level. Changing it requires changing how the service is deployed or extending the path rules in code.
Vocabulary
| Term | Meaning |
|---|---|
| User | In the API, userId is the isolation key for the current call. It is usually a human user ID, but it can also be a tenant ID, agent ID, workspace ID, or any stable application identity. Everything under user/ is private to that userId. |
| Agent | The LLM acting on behalf of the user. Can read+write user/*, read shared/*, and write shared/* only when shared writable mode is enabled. |
| Operator | The team or developer running the deployment. Controls shared/*, admin config, and model settings. |
| Virtual path | The path agents see: user/profile.md, shared/index.md. |
| Physical path | How paths are stored in Postgres: users/{userId}/profile.md. Agents never see these. |
| Hub file | A memory file that many others link to or reference. Useful as a stable context anchor. |
| Memory link | [[user/file.md]] syntax in file content. Creates forward graph edges used by memory_context traversal. |
| Scope | The namespace prefix that determines write access and visibility: user/ or shared/. |
| Dreaming | Background consolidation. Merges duplicates, resolves contradictions, and keeps memory clean between sessions. See Dreaming. |
| Procedural memory | How-to rules that apply in every session. Stored in shared/procedural.md. Always injected into the prompt block. |
| Semantic memory | Stable, deduplicated facts about a user. Schema in shared/semantic.md; per-user instances in user/profile.md. |
| Episodic memory | Time-ordered events and decisions. Schema in shared/episodic.md; per-user log in user/log.md (append-only). |
| Cognitive triad | The three-type memory architecture (procedural + semantic + episodic) that MemexAI uses as its default structure. See Cognitive Architecture. |