Introduction
Mnemo is an MCP-native memory database for AI agents. It provides persistent, structured memory with semantic search, access control, hash-chain verification, and multi-agent collaboration primitives.
Key Features
- 21 MCP Tools: core memory ops (remember, recall, forget, forget_subject, share, consolidate), git-like state (checkpoint, branch, merge, replay), delegation & verification (delegate, verify, trajectory_audit), attention state, agent-controlled
mem_*, and plan memory — see the tools reference - Hybrid Retrieval: Vector similarity (USearch/pgvector) + BM25 full-text (Tantivy) + recency + graph signals fused via Reciprocal Rank Fusion
- Access Control: Owner-based permissions, ACL sharing, transitive delegation with time bounds
- Integrity Verification: SHA-256 hash chains over memory records with tamper detection
- Git-like State Management: Checkpoint, branch, merge, and replay agent memory states
- Cognitive Forgetting: Ebbinghaus decay curves, consolidation, archival strategies
- Memory Poisoning Detection: Anomaly scoring with automatic quarantine
- Multiple Backends: DuckDB (embedded) or PostgreSQL (distributed)
- REST API: Full HTTP API alongside MCP stdio transport
- SDKs: Python (with LangGraph, CrewAI, OpenAI Agents integrations), TypeScript, Go
Use Cases
- Agent Memory: Give LLM agents persistent memory across conversations
- Multi-Agent Collaboration: Share memories between agents with fine-grained permissions
- Audit Trails: Immutable event logs with hash-chain integrity verification
- Knowledge Management: Store, retrieve, and organize agent-generated knowledge
Architecture
Mnemo is built in Rust for performance and safety. The workspace contains:
| Crate | Purpose |
|---|---|
mnemo-core | Storage, indexing, query engine, models |
mnemo-mcp | MCP server (rmcp 3.0) |
mnemo-cli | Binary with CLI args |
mnemo-postgres | PostgreSQL storage backend |
mnemo-rest | Axum REST API |
python/ | PyO3 Python bindings |
Quick Start
Installation
From source
cargo install --path crates/mnemo-cli
With Docker
docker pull ghcr.io/mnemo-ai/mnemo:latest
docker run -v mnemo-data:/data ghcr.io/mnemo-ai/mnemo:latest
Running
Basic (embedded DuckDB, noop embeddings)
mnemo --db-path my-agent.db
With OpenAI embeddings
export OPENAI_API_KEY=sk-...
mnemo --db-path my-agent.db
With PostgreSQL backend
mnemo --postgres-url "postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost/mnemo"
With REST API
mnemo --db-path my-agent.db --rest-port 8080
Claude Desktop Integration
Add to your claude_desktop_config.json:
{
"mcpServers": {
"mnemo": {
"command": "mnemo",
"args": ["--db-path", "/path/to/memory.db"],
"env": {
"OPENAI_API_KEY": "sk-..."
}
}
}
}
Python SDK
pip install mnemo-db # `mnemo` itself is held by a 2021 notebook project
from mnemo import MnemoClient
client = MnemoClient(db_path="agent.db")
result = client.remember("The user prefers dark mode")
memories = client.recall("user preferences")
First Operations
Once running, the agent (or you via MCP client) can:
- Store a memory:
mnemo.rememberwith content and optional metadata - Retrieve memories:
mnemo.recallwith a natural language query - Share with other agents:
mnemo.shareto grant access - Verify integrity:
mnemo.verifyto check hash chain consistency
Architecture
System Overview
┌──────────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐
│MCP Client│ │REST Client│ │ gRPC │ │ psql │
│ (stdio) │ │ (HTTP) │ │ (tonic) │ │ (pgwire) │
└────┬─────┘ └─────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
▼ ▼ ▼ ▼
┌────────────────────────────────────────────────────────┐
│ MnemoEngine │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Remember │ │ Recall │ │ Forget/ │ │Checkpoint│ │
│ │ Pipeline │ │ Pipeline │ │Share/... │ │/Branch/ │ │
│ │ │ │ (RRF) │ │ │ │Merge │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └─────────────┴────────────┴────────────┘ │
│ │ │
│ ┌──────────────────────▼──────────────────────────┐ │
│ │ StorageBackend (trait) │ │
│ │ ┌──────────┐ ┌─────────────┐ │ │
│ │ │ DuckDB │ │ PostgreSQL │ │ │
│ │ │ │ │ + pgvector │ │ │
│ │ └──────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │VectorIndex │ │FullText │ │Embeddings│ │Encrypt │ │
│ │USearch/PG │ │ Tantivy │ │OpenAI/ │ │AES-256 │ │
│ │ (HNSW) │ │ (BM25) │ │ONNX/Noop │ │ GCM │ │
│ └────────────┘ └──────────┘ └──────────┘ └────────┘ │
│ │
│ ┌────────────┐ ┌──────────┐ ┌──────────────────────┐ │
│ │ Cache │ │ColdStore │ │ Poisoning Detection │ │
│ │ (in-mem) │ │ (S3) │ │ + Prompt Injection │ │
│ └────────────┘ └──────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Crate Structure
| Crate | Purpose |
|---|---|
mnemo-core | Storage, data model, query engine, indexing, encryption |
mnemo-mcp | MCP server via rmcp 3.0 (STDIO transport) |
mnemo-cli | CLI binary with clap argument parsing |
mnemo-postgres | PostgreSQL storage backend via sqlx + pgvector |
mnemo-rest | REST API via Axum 0.8 |
mnemo-admin | Admin dashboard endpoints (agent stats) |
mnemo-pgwire | PostgreSQL wire protocol server |
mnemo-grpc | gRPC API via tonic 0.12 |
python | Python bindings via PyO3 |
Data Model
MemoryRecord
The core data structure. Key fields:
| Field | Type | Description |
|---|---|---|
id | UUID v7 | Time-ordered unique identifier |
agent_id | String | Owning agent |
content | String | Memory content (encrypted at rest if enabled) |
memory_type | Enum | Episodic, Semantic, Procedural, Strategic |
scope | Enum | Private, Shared, Global |
importance | f32 | 0.0-1.0 importance score |
tags | Vec | Searchable tags |
embedding | Vec | Vector embedding |
content_hash | Vec<u8> | SHA-256 hash |
prev_hash | Option | Previous record hash (chain) |
quarantined | bool | Flagged by poisoning detection |
decay_rate | Option<f32> | Custom decay rate |
decay_function | Option | Custom decay function |
Retrieval Pipeline
Recall uses Reciprocal Rank Fusion (RRF) to combine:
- Vector similarity (cosine via USearch or pgvector HNSW)
- BM25 full-text (Tantivy)
- Recency scoring (exponential decay with configurable half-life)
- Graph expansion (1-2 hop relation traversal)
Weights are configurable via hybrid_weights parameter. Permission-safe ANN pre-filtering ensures only authorized memories appear in results.
Access Control
Three-tier permission model:
- Owner: Agent who created the memory has full access
- ACL: Explicit grants via
sharewith permission levels (Read, Write, Delete, Share, Delegate) - Delegation: Transitive, scoped, time-bounded permission delegation with depth limits
Hash Chain Integrity
Every memory record is linked via SHA-256 hashes:
Record₁ → content_hash = SHA256(content + agent_id + timestamp)
Record₂ → prev_hash = SHA256(content_hash₂ + content_hash₁)
Record₃ → prev_hash = SHA256(content_hash₃ + content_hash₂)
The verify tool checks the entire chain for tampering using constant-time comparisons.
Security Layers
- Encryption: AES-256-GCM at-rest content encryption (pluggable via
ContentEncryption) - Validation: agent_id charset/length validation at engine level
- Poisoning: anomaly scoring + prompt injection pattern detection → quarantine
- CORS: configurable origin allowlist, defaults to localhost
- Error sanitization: internal errors logged only, generic messages returned
MCP Tools Reference
Mnemo registers 21 MCP tools via the rmcp framework. Each is available over
the STDIO transport when running the mnemo binary.
Every tool takes a single JSON object argument (the fields below) and returns a
JSON-encoded text result. On failure a tool returns an isError result whose
text is the error message rather than throwing. Required arguments are bold;
all others are optional with sensible defaults.
Role filtering. When the server is built with
MnemoServer::with_role_filter, a caller only sees the tools its role is allowed to call intools/list, and a deniedtools/callreturns a structured-32601(method-not-found) error instead of a silent empty result. Without a filter every tool below is visible and callable.
The ten core tools also have dedicated pages (linked in the tables). The remaining eleven are documented inline here.
Core memory operations
| Tool | Purpose | Key arguments | Returns |
|---|---|---|---|
| mnemo.remember | Store a new memory (semantic + keyword searchable). | content; memory_type, scope, importance, tags, metadata, ttl_seconds, related_to, thread_id, source_type, source_id, org_id, decay_rate, created_by | { id, content_hash, status } |
| mnemo.recall | Search/retrieve memories by strategy (semantic, lexical, hybrid, graph, reconstruct, exact, auto). | query; limit, memory_type(s), scope, min_importance, tags, strategy, temporal_range, org_id, recency_half_life_hours, hybrid_weights, rrf_k, as_of, explain, current_fact_resolver, orientation_cache, domain_scope | { memories, total } (plus optional orientation, belief_state, explain fields) |
| mnemo.forget | Soft-delete, hard-delete, decay, consolidate, or archive memories by ID or criteria. | memory_ids; strategy, criteria (max_age_hours, min_importance_below, memory_type, tags) | { forgotten, errors, status } |
| mnemo.forget_subject | GDPR / DPDPA subject erasure: redact (default, preserves hash chain) or hard-delete every memory tagged subject:<id>. | subject_id; strategy, agent_id | { subject_id, strategy, matched, forgotten, cascaded_events, errors } |
| mnemo.provenance | Read write-provenance: who wrote each memory, under what capability, in what session, when. Tamper-evident audit history that survives forgetting. | one of memory_id, principal, session_id; limit | one record (by memory_id) or an array (by principal/session_id) of { id, memory_id, principal, capability_id, session_id, op, authored_at, content_hash, prev_hash } |
| mnemo.forget_by_provenance | FORGET BY PROVENANCE: revoke every memory a principal (or session/trace) authored in one call. Targeted remediation, not a wipe — the audit trail survives. | one of principal, session_id; strategy (soft_delete/hard_delete/redact) | { forgotten, errors, status } |
| mnemo.share | Grant one or more agents access to one or more memories (batch supported). | memory_id, target_agent_id; memory_ids, target_agent_ids, permission, expires_in_hours | { acl_ids, memory_ids, shared_with, errors, status } |
| mnemo.consolidate | Consolidate related memories into one revisable topic document (Infini-Memory), preserving provenance + a hash-chained audit event. | memory_ids, topic_name; agent_id, summary, supersede, thread_id, metadata | { topic_document_id, topic_name, source_count, version, superseded_id, member_ids, content_hash, consolidation_event_id, revision_event_id, status } |
Checkpoint, branch, merge & replay (git-like state)
| Tool | Purpose | Key arguments | Returns |
|---|---|---|---|
| mnemo.checkpoint | Snapshot the current agent state (state, active memories, event cursor). | thread_id, state_snapshot; branch_name, label, metadata | { checkpoint_id, parent_id, branch_name, status } |
| mnemo.branch | Fork state into a new branch from an existing checkpoint. | thread_id, new_branch_name; source_checkpoint_id, source_branch | { checkpoint_id, branch_name, source_checkpoint_id, status } |
| mnemo.merge | Merge a branch into another (full, cherry-pick, or squash). | thread_id, source_branch; target_branch, strategy, cherry_pick_ids | { checkpoint_id, target_branch, merged_memory_count, status } |
| mnemo.replay | Reconstruct agent context at a checkpoint (state, memories, events up to that point). | thread_id; checkpoint_id, branch_name, as_of | { id, content, memory_type, created_at, status } |
Delegation & verification
| Tool | Purpose | Key arguments | Returns |
|---|---|---|---|
| mnemo.delegate | Grant scoped, time-bounded (optionally re-delegable) access to your memories. | delegate_id, permission; memory_ids, tags, max_depth, expires_in_hours | { delegation_id, delegator, delegate, permission, status } |
| mnemo.verify | Verify per-record hash-chain integrity; detect tampered/corrupted records. | agent_id, thread_id | { valid, total_records, verified_records, first_broken_at, error_message, status } |
| mnemo.trajectory_audit | GEM-aligned trajectory-correctness audit (arXiv:2605.26252): unregulated growth, missing semantic revision, capacity-driven forgetting, read-only retrieval. | agent_id, thread_id, active_bank_ceiling, fact_key, named_forget_strategies | { report, all_ok } |
Attention state (arXiv:2605.18226)
Requires the server to be built with MnemoServer::with_attention_state;
otherwise both tools return an error result.
| Tool | Purpose | Key arguments | Returns |
|---|---|---|---|
| mnemo.attention_state.put | Store a precomputed, opaque attention-state blob under (agent_id, prefix_hash). | agent_id, prefix_hash, state_blob_hex; model, ttl_seconds | { id, agent_id, prefix_hash, model, ttl_seconds, created_at } |
| mnemo.attention_state.get | Fetch the most-recent attention-state record for (agent_id, prefix_hash). | agent_id, prefix_hash | record { id, agent_id, prefix_hash, model, state_blob_hex, ttl_seconds, created_at } or null on miss |
Agent-controlled memory — mem_* family (AutoMEM)
A flat, agent-managed store: nothing is written unless the agent explicitly
calls mem_write. Entries are tagged agent-managed and are only visible to
mem_read (not the general recall pipeline).
| Tool | Purpose | Key arguments | Returns |
|---|---|---|---|
| mnemo.mem_write | Persist an entry the agent decided is worth keeping. | content; tags, importance, memory_type, metadata, agent_id, org_id | { id, content_hash, store, status } |
| mnemo.mem_read | Read back only the agent’s own agent-managed entries. | query; limit, tags, agent_id, org_id | { memories, total, store } |
| mnemo.mem_revise | Supersede a stale agent-managed entry with a corrected one (newest wins). | id, content; tags, importance, agent_id, org_id | { id, revises, content_hash, store, status } |
| mnemo.mem_forget | Drop an agent-managed entry (soft by default; hard=true for permanent). | id; hard, agent_id | { forgotten, errors, status } |
Plan / experience memory (DocTrace)
Caches successful retrieval/reasoning plans for replay. Requires the server’s experience-memory mode to be enabled.
| Tool | Purpose | Key arguments | Returns |
|---|---|---|---|
| mnemo.remember_plan | Cache a successful plan (query, ordered steps, chunk ids, outcome score in [0,1]). Below-threshold plans are not stored. | query, steps, chunk_ids, outcome_score; scope, agent_id, org_id | { id, signature, stored, status } |
| mnemo.recall_plan | Replay the best cached plan whose query signature matches above a threshold (default 0.7). RBAC-gated. | query; similarity_threshold, agent_id, org_id | { plan, candidates_considered, hit } |
A note on audit-log export
mnemo.export_audit_log is referenced by the manifest schema but is not one
of the 23 registered tools above. The audit-log export capability itself already
exists today as a library API:
mnemo_compliance::export_audit_log(events, format, signer)
(with verify_ndjson_signed), which produces a signed NDJSON / EU-AI-Office CSV
bundle from the hash-chained event log.
The earlier capability-lease design (per-read lease tokens gating
forget_subject / audit-log export) was removed as dead code — it was never
wired, and on the stdio transport a single-operator lease is ceremony rather than
isolation. The design is captured in
#126 for a future
multi-caller (authenticated) transport where it has real value.
mnemo.remember
Store a new memory record with optional metadata, tags, and relationships.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
content | string | yes | The memory content to store |
agent_id | string | no | Agent identifier (uses server default) |
memory_type | string | no | episodic, semantic, procedural, strategic |
scope | string | no | private, shared, global |
importance | number | no | 0.0-1.0 importance score (default 0.5) |
tags | string[] | no | Searchable tags |
metadata | object | no | Arbitrary JSON metadata |
source_type | string | no | conversation, tool_output, reflection, etc. |
source_id | string | no | Reference to source (e.g., message ID) |
related_to | string[] | no | UUIDs of related memories (creates graph edges) |
org_id | string | no | Organization scope |
thread_id | string | no | Conversation thread ID |
ttl_seconds | number | no | Time-to-live in seconds |
decay_rate | number | no | Custom decay rate for importance |
created_by | string | no | Creator identifier |
Response
| Field | Type | Description |
|---|---|---|
id | string | UUID v7 of the created memory |
content_hash | string | SHA-256 hash of the content |
Example
{
"content": "User prefers dark mode and larger fonts",
"memory_type": "episodic",
"importance": 0.8,
"tags": ["preferences", "ui"]
}
mnemo.recall
Retrieve memories using semantic search, full-text search, exact filters, graph traversal, or hybrid retrieval.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
query | string | yes | Search query |
agent_id | string | no | Filter by agent (uses server default) |
limit | number | no | Max results (default 10) |
memory_type | string | no | Filter by single type |
memory_types | string[] | no | Filter by multiple types |
scope | string | no | Filter by scope |
min_importance | number | no | Minimum importance threshold |
tags | string[] | no | Filter by tags (any match) |
org_id | string | no | Filter by organization |
strategy | string | no | vector, bm25, exact, graph, hybrid (default: hybrid) |
temporal_range | object | no | { after: string, before: string } ISO timestamps |
Strategies
- vector: Cosine similarity via USearch/pgvector
- bm25: Full-text search via Tantivy
- exact: Filter-only (no embeddings needed)
- graph: Vector seeds + 2-hop graph expansion with RRF
- hybrid (default): Vector + BM25 + recency + graph fused via RRF
Response
| Field | Type | Description |
|---|---|---|
memories | array | Matching memories with scores |
total | number | Total count of results |
Each memory includes: id, agent_id, content, memory_type, scope, importance, tags, score, created_at, updated_at.
Example
{
"query": "user preferences",
"strategy": "hybrid",
"limit": 5,
"min_importance": 0.3
}
mnemo.forget
Remove or decay memories using various strategies.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
memory_ids | string[] | yes | UUIDs of memories to forget |
agent_id | string | no | Agent identifier |
strategy | string | no | Forget strategy (see below) |
criteria | object | no | Filter criteria for bulk forget |
Strategies
| Strategy | Description |
|---|---|
soft_delete | Mark as deleted (default, recoverable) |
hard_delete | Permanently remove from storage |
decay | Reduce importance using Ebbinghaus decay curve |
consolidate | Merge into a semantic summary |
archive | Move to cold storage |
Criteria (for bulk forget)
| Field | Type | Description |
|---|---|---|
max_age_hours | number | Only forget memories older than this |
min_importance_below | number | Only forget memories below this importance |
tags | string[] | Only forget memories with these tags |
Response
| Field | Type | Description |
|---|---|---|
forgotten | string[] | UUIDs of successfully forgotten memories |
errors | array | { id, error } for any failures |
mnemo.share
Grant another agent access to a memory.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
memory_id | string | yes | UUID of memory to share |
target_agent_id | string | yes | Agent to share with |
target_agent_ids | string[] | no | Share with multiple agents at once |
agent_id | string | no | Sharing agent (uses server default) |
permission | string | no | read, write, delete, share, delegate (default: read) |
expires_in_hours | number | no | ACL expiration time |
Response
| Field | Type | Description |
|---|---|---|
acl_id | string | UUID of the created ACL entry |
shared_with | string[] | Agents the memory was shared with |
status | string | shared |
mnemo.checkpoint
Create a named snapshot of the current agent memory state.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | no | Agent identifier |
label | string | no | Human-readable label for the checkpoint |
Response
| Field | Type | Description |
|---|---|---|
checkpoint_id | string | UUID of the checkpoint |
label | string | The label (if provided) |
created_at | string | ISO timestamp |
mnemo.branch
Create a named branch from a checkpoint for isolated memory experimentation.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
checkpoint_id | string | yes | Base checkpoint UUID |
branch_name | string | yes | Name for the branch |
Response
| Field | Type | Description |
|---|---|---|
branch_name | string | The created branch name |
base_checkpoint | string | The checkpoint it branched from |
status | string | branched |
mnemo.merge
Merge a branch back into the main agent memory state.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
branch_name | string | yes | Branch to merge |
agent_id | string | no | Agent identifier |
Response
| Field | Type | Description |
|---|---|---|
merged | number | Count of merged records |
conflicts | number | Count of conflicts detected |
status | string | merged |
mnemo.replay
Replay events that occurred after a given checkpoint.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
checkpoint_id | string | yes | Checkpoint to replay from |
agent_id | string | no | Agent identifier |
Response
| Field | Type | Description |
|---|---|---|
events | array | List of AgentEvent objects |
count | number | Number of events replayed |
mnemo.verify
Verify the SHA-256 hash chain integrity of memory records.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | no | Agent to verify (uses server default) |
thread_id | string | no | Verify only a specific thread |
Response
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the chain is intact |
total_records | number | Total records checked |
verified_records | number | Records that passed verification |
first_broken_at | string | UUID of first broken record (if any) |
error_message | string | Description of the integrity violation |
status | string | verified or integrity_violation |
mnemo.delegate
Delegate permissions to another agent with optional scoping and time bounds.
Input Schema
| Field | Type | Required | Description |
|---|---|---|---|
delegate_id | string | yes | Agent to delegate to |
permission | string | yes | read, write, delete, share, delegate |
memory_ids | string[] | no | Scope to specific memories |
tags | string[] | no | Scope to memories with these tags |
max_depth | number | no | Maximum transitive delegation depth (default 0) |
expires_in_hours | number | no | Delegation expiration time |
Scoping
If memory_ids is provided, the delegation applies only to those specific memories. If tags is provided, it applies to memories matching those tags. If neither is provided, the delegation applies to all memories.
Transitive Delegation
When max_depth > 0, the delegate can further delegate to other agents, up to the specified depth.
Response
| Field | Type | Description |
|---|---|---|
delegation_id | string | UUID of the delegation |
status | string | delegated |
REST API
The REST API provides HTTP access to Mnemo, enabling non-MCP clients to interact with the memory database. Enable it with --rest-port:
mnemo --db-path my.db --rest-port 8080
All endpoints are under /v1/.
Configuration
- CORS: controlled by
MNEMO_CORS_ORIGINSenvironment variable. Defaults tolocalhost:3000andlocalhost:8080. Set to*for permissive mode. - Body limit: 2 MB maximum request body.
Endpoints
Health Check
GET /v1/health
Returns {"status": "ok"}.
Remember
POST /v1/memories
Content-Type: application/json
{
"content": "User prefers dark mode",
"importance": 0.8,
"tags": ["preferences"]
}
Returns {"id": "...", "content_hash": "..."}.
Recall
GET /v1/memories?query=preferences&limit=5&strategy=hybrid&min_importance=0.3
Query parameters:
| Parameter | Type | Description |
|---|---|---|
query | string | Natural language search query (required) |
agent_id | string | Filter by agent |
limit | integer | Max results (default: 10, max: 100) |
memory_type | string | Filter: episodic, semantic, procedural, strategic |
memory_types | string | Comma-separated list of types |
scope | string | Filter: private, shared, global |
min_importance | float | Minimum importance threshold |
tags | string | Comma-separated tag filter |
org_id | string | Filter by organization |
strategy | string | hybrid, semantic, fulltext, exact, graph |
as_of | string | Point-in-time query (RFC 3339 timestamp) |
hybrid_weights | string | Comma-separated RRF weights |
rrf_k | float | RRF constant (default: 60) |
Get Memory by ID
GET /v1/memories/{id}
Forget
DELETE /v1/memories/{id}?strategy=soft_delete
Query parameters: strategy (soft_delete, hard_delete, decay, consolidate, archive), agent_id.
Share
POST /v1/memories/{id}/share
Content-Type: application/json
{
"target_agent_id": "agent-2",
"permission": "read",
"expires_in_hours": 24
}
Checkpoint
POST /v1/checkpoints
Content-Type: application/json
{"label": "before-experiment"}
Branch
POST /v1/branches
Content-Type: application/json
{"checkpoint_id": "...", "branch_name": "experiment-1"}
Merge
POST /v1/merge
Content-Type: application/json
{"branch_name": "experiment-1"}
Replay
POST /v1/replay
Content-Type: application/json
{"checkpoint_id": "..."}
Verify
POST /v1/verify
Content-Type: application/json
{"agent_id": "my-agent"}
Delegate
POST /v1/delegate
Content-Type: application/json
{
"agent_id": "my-agent",
"delegate_id": "agent-2",
"permission": "read",
"memory_ids": ["uuid-1", "uuid-2"],
"expires_in_hours": 48
}
The agent_id field identifies the caller. The server verifies the caller has Delegate permission on each memory in memory_ids before creating the delegation.
OTLP Ingest
POST /v1/ingest/otlp
Content-Type: application/json
{
"resourceSpans": [...]
}
Accepts simplified OTLP JSON spans and converts them to agent events. Extracts GenAI semantic convention fields (gen_ai.request.model, gen_ai.usage.input_tokens, etc.).
Returns {"accepted": <count>}.
Error Handling
Errors return appropriate HTTP status codes with generic messages:
| Status | Meaning |
|---|---|
| 400 | Validation error (bad input) |
| 403 | Permission denied |
| 404 | Memory not found |
| 500 | Internal error |
Error body: {"error": "description"}. Internal errors are logged server-side; the response contains only a generic message to prevent information leakage.
Python SDK
The Python SDK provides native access to Mnemo via PyO3 bindings, plus integrations for LangGraph, CrewAI, and OpenAI Agents SDK.
Installation
pip install mnemo-db
The PyPI distribution name is
mnemo-db(notmnemo) because the unqualified name is held by an unrelated 2021 notebook project. The import path is unchanged — your code keeps sayingfrom mnemo import MnemoClient.
With framework integrations:
pip install mnemo-db[langgraph] # LangGraph checkpoint support
pip install mnemo-db[crewai] # CrewAI memory integration
pip install mnemo-db[openai-agents] # OpenAI Agents SDK integration
Basic Usage
from mnemo import MnemoClient
client = MnemoClient(db_path="agent.db", agent_id="my-agent")
# Store a memory
result = client.remember("The user likes dark mode", importance=0.8)
# Recall memories
memories = client.recall("user preferences", limit=5)
for m in memories:
print(f"{m['content']} (score: {m['score']:.2f})")
# Forget a memory
client.forget([result["id"]])
OpenAI Agents SDK
import asyncio
from agents import Agent, Runner
from mnemo.openai_agents import MnemoAgentMemory
async def main():
async with MnemoAgentMemory(db_path="agent.db") as memory:
agent = Agent(
name="MemoryAgent",
instructions="Use memory tools to remember and recall information.",
mcp_servers=memory.mcp_servers,
)
result = await Runner.run(agent, "Remember that I prefer Python over JavaScript")
print(result.final_output)
asyncio.run(main())
LangGraph Checkpointer
from mnemo import MnemoClient
from mnemo.checkpointer import ASMDCheckpointer
client = MnemoClient(db_path="agent.db")
checkpointer = ASMDCheckpointer(client)
# Use with LangGraph
from langgraph.graph import StateGraph
graph = StateGraph(...).compile(checkpointer=checkpointer)
CrewAI Memory
from mnemo.crewai_memory import ASMDMemory
memory = ASMDMemory(db_path="crew.db")
# Use with CrewAI agents
Claude Agent SDK (0.2.0+)
Connects Mnemo to the claude-agent-sdk Python package used by Claude Opus
4.7’s Auto Memory workflow. Exposes the MCP tool surface and optionally
materializes memories into Markdown files that Auto Memory reads and edits
directly; a watchdog observer persists those edits back into Mnemo.
import asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from mnemo.claude_agent_sdk import MnemoClaudeMemory
async def main():
async with MnemoClaudeMemory(
db_path="agent.mnemo.db",
agent_id="my-project",
memory_dir=Path(".claude/memory"),
) as memory:
memory.materialize(query="recent work", limit=25)
memory.watch()
options = ClaudeAgentOptions(
mcp_servers={"mnemo": memory.mcp_server_config},
allowed_tools=["mcp__mnemo__recall", "mcp__mnemo__remember"],
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Summarize yesterday's work.")
asyncio.run(main())
Install with pip install mnemo[claude].
OpenAI Agents SDK — Session store (0.2.0+)
Implements the SessionABC protocol introduced in the 2026-04-15 release,
so conversation history is stored in Mnemo. Each turn becomes a
session-tagged episodic memory, so a new process can resume the conversation
by opening a store with the same session_id.
import asyncio
from agents import Agent, Runner
from mnemo.openai_sessions import MnemoSessionStore
async def main():
session = MnemoSessionStore(
db_path="agent.mnemo.db",
agent_id="user-42",
session_id="support-2026-04-20",
)
agent = Agent(name="Support")
result = await Runner.run(agent, "I can't log in", session=session)
print(result.final_output)
asyncio.run(main())
GDPR / DPDPA-safe erasure (0.2.0+)
Subject-scoped erasure through the engine, MCP, REST, or gRPC. Memories are
matched by the tag convention subject:<subject_id>. The default redact
strategy preserves the memory’s hash chain (so audit verification still
succeeds) and replaces content with [REDACTED].
# REST — redact
curl -X POST -H 'content-type: application/json' \
-d '{"subject_id":"user-42","strategy":"redact"}' \
http://localhost:8080/v1/forget_subject
To hard-delete instead, use {"strategy": "hard_delete"}.
Ranking provenance (0.2.0+)
Pass explain=True to recall to receive a score_breakdown for each
result showing the per-signal contributions (vector, BM25, graph, recency)
and the final RRF rank.
result = client.recall("alpha", explain=True, strategy="hybrid")
for memory in result["memories"]:
bd = memory.get("score_breakdown")
if bd:
print(memory["content"], bd)
TTL sweeper + point-in-time replay (0.2.0+)
The engine can run a background TTL sweeper that hard-deletes expired
memories and emits MemoryExpired audit events. Enable it via
--ttl-sweep-interval / MNEMO_TTL_SWEEP_INTERVAL.
replay accepts an as_of timestamp that synthesizes a virtual checkpoint
of the agent state at that instant:
state = client.replay(
thread_id="support-2026-04-20",
as_of="2026-04-18T00:00:00Z",
)
TypeScript SDK
The TypeScript SDK communicates with Mnemo via MCP STDIO, spawning the mnemo binary as a child process.
Installation
npm install @mndfreek/mnemo-sdk
Usage
import { MnemoClient } from '@mndfreek/mnemo-sdk';
const client = new MnemoClient({
dbPath: 'agent.db',
agentId: 'my-agent',
});
await client.connect();
// Store a memory
const result = await client.remember({
content: 'User prefers dark mode',
importance: 0.8,
tags: ['preferences'],
});
// Recall memories
const memories = await client.recall({
query: 'user preferences',
limit: 5,
});
// Share with another agent
await client.share({
memory_id: result.id,
target_agent_id: 'agent-2',
permission: 'read',
});
// Verify integrity
const verification = await client.verify({
agent_id: 'my-agent',
});
console.log(`Chain valid: ${verification.valid}`);
await client.close();
API Reference
Constructor Options
| Option | Type | Default | Description |
|---|---|---|---|
command | string | "mnemo" | Path to mnemo binary |
dbPath | string | "mnemo.db" | Database file path |
agentId | string | "default" | Default agent ID |
orgId | string | - | Organization ID |
openaiApiKey | string | - | OpenAI API key for embeddings |
dimensions | number | 1536 | Embedding dimensions |
Methods
All methods return promises and correspond to MCP tools:
remember(input)/recall(input)/forget(input)share(input)/checkpoint(input)/branch(input)merge(input)/replay(input)/verify(input)/delegate(input)
Go SDK
The Go SDK communicates with Mnemo via MCP STDIO, spawning the mnemo binary as a child process.
Installation
go get github.com/mnemo-ai/mnemo-go
Usage
package main
import (
"fmt"
"log"
mnemo "github.com/mnemo-ai/mnemo-go"
)
func main() {
client, err := mnemo.NewClient(mnemo.ClientOptions{
DbPath: "agent.db",
AgentID: "my-agent",
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Store a memory
importance := float32(0.8)
result, err := client.Remember(mnemo.RememberInput{
Content: "User prefers dark mode",
Importance: &importance,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Stored: %s\n", result.ID)
// Recall memories
limit := 5
memories, err := client.Recall(mnemo.RecallInput{
Query: "user preferences",
Limit: &limit,
})
if err != nil {
log.Fatal(err)
}
for _, m := range memories.Memories {
fmt.Printf(" %s (score: %.2f)\n", m.Content, m.Score)
}
}
API Reference
ClientOptions
| Field | Type | Default | Description |
|---|---|---|---|
Command | string | "mnemo" | Path to mnemo binary |
DbPath | string | "mnemo.db" | Database file path |
AgentID | string | "default" | Default agent ID |
OrgID | string | - | Organization ID |
OpenAIKey | string | - | OpenAI API key |
Dimensions | int | 1536 | Embedding dimensions |
Methods
Remember(RememberInput)/Recall(RecallInput)/Forget(ForgetInput)Share(ShareInput)/Checkpoint(CheckpointInput)/Branch(BranchInput)Merge(MergeInput)/Replay(ReplayInput)/Verify(VerifyInput)/Delegate(DelegateInput)
Memory tiers
Mnemo follows the Letta / MemGPT pattern of giving each memory record a tier — a coarse class that tells the engine how to treat it for expiry, decay, and consolidation. Four tiers are defined:
Working— session-scoped. Auto-expires afterengine.ttl_working_seconds(default 3600 s) when the caller doesn’t supply an explicitexpires_at. Treat this as the “scratchpad” tier for within-conversation working memory.Procedural— system prompts, tool definitions, decision-logic snippets. Importance is clamped on write toengine.procedural_importance_floor(default 0.8) so these never decay below recall visibility.Semantic— facts, user preferences, long-lived knowledge. Current default behaviour; no special handling beyond the normal decay / consolidation pipeline.Episodic— interaction logs. Carriesthread_id/session_idas the scoping identifier; the prime target for the reflection pass’s semantic dedup and stale archival phases.
Shape in the data model (honest note)
MemoryTier is a type alias for the existing MemoryType enum, not
a separate schema field. v0.2.0’s initial Task-8 spec called for a new
tier: MemoryTier field on MemoryRecord; we shipped the type-alias
shape instead because MemoryType already had the same four variants
— a redundant second column would have been churn with no runtime
benefit. The practical effect is identical: callers can pass tier= (a
MemoryTier value) to remember and the engine applies the per-tier
behaviour based on memory_type.
#![allow(unused)]
fn main() {
use mnemo_core::model::memory::{MemoryTier, MemoryType};
// These are the same type; MemoryTier is literally `pub type MemoryTier = MemoryType;`
let t: MemoryType = MemoryTier::Working;
}
Any downstream code that was relying on a separate tier field for v0.1.1
forward compatibility will compile against memory_type.
Engine knobs
MnemoEngine exposes two builder methods for tuning tier behaviour:
#![allow(unused)]
fn main() {
let engine = MnemoEngine::new(...)
.with_ttl_working_seconds(1800) // 30-minute Working TTL
.with_procedural_importance_floor(0.9); // raise Procedural floor
}
The constants DEFAULT_TTL_WORKING_SECONDS and
DEFAULT_PROCEDURAL_IMPORTANCE_FLOOR are exported from
mnemo_core::query so callers can reference the shipping defaults.
Recall semantics
All four tiers participate in the same recall pipeline
(auto/vector_only/hybrid_rrf/graph_boosted/lexical). Use
tags=["tier:procedural"] or the existing memory_type filter to
constrain to a specific tier; the engine does not currently apply a
recall-time boost for Working or cap Procedural to read-only.
Out of scope
Letta’s moving-between-tiers heuristics (Working → Semantic after N
accesses; Episodic → Semantic via reflection) are not implemented — the
v0.3.1 reflection pass only touches Episodic consolidation. A
tier-promotion pipeline is queued for v0.4.0.
Temporal edges (mnemo-graph)
The mnemo-graph crate (introduced in v0.4.0-rc1) adds a
bitemporal graph layer over the existing storage backends. It’s
loosely inspired by Graphiti (Zep)
and the Graphiti paper: every
relation carries two clocks instead of one, so historical queries
can ask “what did we believe at time T?” without losing later
corrections.
The two clocks
Every edge in the graph stores:
valid_from valid_to (None = still true)
^ ^
| fact validity |
+-----------------------+
|
+-- recorded_at (when we wrote the row)
valid_from/valid_todescribe fact validity — when the relation is true in the world.recorded_atdescribes system time — when we wrote the row. Useful for audit replay: “show me what the graph looked like at recorded_at = 2026-04-15.”
Without the second clock, there’s no way to distinguish “we always knew Priya works at Acme since 2025” from “we found out yesterday Priya works at Acme since 2025.” Both situations are common; both need different answers from a debugging session.
The TemporalEdge model
#![allow(unused)]
fn main() {
pub struct TemporalEdge {
pub id: Uuid,
pub src: Uuid,
pub dst: Uuid,
pub relation: String,
pub valid_from: DateTime<Utc>,
pub valid_to: Option<DateTime<Utc>>, // None = still true
pub confidence: f32, // [0.0, 1.0]
pub recorded_at: DateTime<Utc>,
}
}
relation is a free-form string today ("works_at",
"located_in", "reports_to"). We considered an enum but
discarded it — codifying a relation set without real corpus data
risks pinning the wrong vocabulary. The full LLM extractor that
lands in v0.4.0 final will document the conventions it emits.
Storage
Two tables — DuckDB + Postgres equivalents:
CREATE TABLE graph_nodes (
id VARCHAR PRIMARY KEY,
label VARCHAR,
metadata JSON,
created_at VARCHAR NOT NULL
);
CREATE TABLE graph_edges (
id VARCHAR PRIMARY KEY,
src VARCHAR NOT NULL,
dst VARCHAR NOT NULL,
relation VARCHAR NOT NULL,
valid_from VARCHAR NOT NULL,
valid_to VARCHAR,
confidence FLOAT NOT NULL DEFAULT 1.0,
recorded_at VARCHAR NOT NULL
);
CREATE INDEX idx_graph_edges_src_validfrom ON graph_edges(src, valid_from);
CREATE INDEX idx_graph_edges_dst ON graph_edges(dst);
graph_expand — the bitemporal walk
#![allow(unused)]
fn main() {
use chrono::{TimeZone, Utc};
use mnemo_graph::{DuckGraphStore, GraphStore, TemporalEdge, graph_expand};
let store = DuckGraphStore::open_in_memory()?;
// Priya works at Acme starting 2026-01-01.
let priya = Uuid::now_v7();
let acme = Uuid::now_v7();
let acme_edge = TemporalEdge::new(
priya, acme, "works_at",
Utc.with_ymd_and_hms(2026, 1, 1, 0, 0, 0).unwrap(),
None, 0.9,
);
store.insert_edge(&acme_edge).await?;
// Priya leaves Acme on 2026-04-01 and joins Globex.
let globex = Uuid::now_v7();
store.close_edge(acme_edge.id, Utc.with_ymd_and_hms(2026, 4, 1, 0, 0, 0).unwrap()).await?;
store.insert_edge(&TemporalEdge::new(
priya, globex, "works_at",
Utc.with_ymd_and_hms(2026, 4, 1, 0, 0, 0).unwrap(),
None, 0.95,
)).await?;
// Walk the graph at two different points in time.
let in_feb = Utc.with_ymd_and_hms(2026, 2, 15, 0, 0, 0).unwrap();
let in_june = Utc.with_ymd_and_hms(2026, 6, 1, 0, 0, 0).unwrap();
let reachable_feb = graph_expand(&store, priya, 2, in_feb).await?;
// ^^^ contains acme, NOT globex (relation hadn't started yet)
let reachable_june = graph_expand(&store, priya, 2, in_june).await?;
// ^^^ contains globex, NOT acme (relation closed at 2026-04-01)
}
This is the supersession property — without it, the graph layer would be redundant with a regular non-temporal graph.
Conflict resolution
When the LLM extractor (v0.4.0 final) emits a contradicting fact with higher confidence than an existing edge, the convention is:
- The new edge inserts with its own
valid_fromandrecorded_at. - The pre-existing edge with the lower confidence has its
valid_toset to the new edge’svalid_from— capping its validity window.
The result: a sceptical operator can reconstruct the historical view
that contained the old answer (via recorded_at) AND the corrected
view (via valid_from).
What ships in v0.4.0-rc1
| Status | |
|---|---|
TemporalEdge model | ✓ |
GraphStore async trait | ✓ |
DuckDB-backed DuckGraphStore | ✓ |
graph_expand BFS with as_of filter | ✓ |
| Postgres-backed store | v0.4.0 final |
TemporalEdge::extract LLM-driven | out of scope (#156) |
hybrid_rrf 4th-signal integration | v0.4.0 final |
MCP / REST / gRPC graph_expand tools | v0.4.0 final |
There is no LLM extractor, and there is no longer a stub pretending to be
one. mnemo-graph is a bitemporal storage + query layer: callers construct
TemporalEdges, and this crate stores, closes and walks them.
A TemporalEdge::extract stub lived here until 2026-08-15, always returning
Vec::new(). It was removed rather than left in place, because a function that
always returns empty is worse than an absent one: a caller cannot tell “found
no relations” from “not implemented”, so wiring it in produces silent no-ops
indefinitely. It also outlived its own promise by five releases — the docstring
said “lands in v0.4.0 final” while the workspace reached 0.5.23 — and its
graph-extract feature flag gated nothing, since the module was compiled
unconditionally. See #156.
If LLM-driven extraction is wanted later it should arrive as a designed feature with its own issue, not as a placeholder.
Sources
- Graphiti repo (Zep)
- Graphiti paper (arXiv:2501.13956)
mnemo-graphsource- Bitemporal walk integration tests
Claude Agent SDK integration
Mnemo integrates with Anthropic’s claude-agent-sdk two ways at once:
-
MCP tool surface — every Mnemo MCP tool (
remember/recall/forget/share/checkpoint/branch/merge/replay/delegate/verify/forget_subject/reflect) is exposed to the agent through the standardClaudeAgentOptions.mcp_serversparameter. -
Memory-file bridge — recalled memories are materialised as Markdown files on disk with YAML frontmatter. Claude Opus 4.7’s Auto Memory reads and edits those files directly; a
watchdogobserver picks up the edits and persists them back into Mnemo so the two views stay in sync.
Install with pip install mnemo[claude].
Minimal example
import asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from mnemo.claude_agent_sdk import MnemoClaudeMemory
async def main():
async with MnemoClaudeMemory(
db_path="agent.mnemo.db",
agent_id="my-project",
memory_dir=Path(".claude/memory"),
) as memory:
# Seed the memory directory from Mnemo so Auto Memory has context.
memory.materialize(query="recent work", limit=25)
# Start watching for Auto Dream / Auto Memory edits.
memory.watch()
options = ClaudeAgentOptions(
mcp_servers={"mnemo": memory.mcp_server_config},
allowed_tools=[
"mcp__mnemo__recall",
"mcp__mnemo__remember",
],
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Summarize what I worked on yesterday.")
asyncio.run(main())
What the bridge does to each file
-
Write from Mnemo —
materialize(...)writes{memory_dir}/{uuid}.mdwith frontmatter--- id: 0195a7e8-... importance: 0.7 tags: ["decision", "roadmap"] expires_at: 2026-05-20T00:00:00Z --- The team agreed to ship v0.3.1 on 2026-04-22. -
Edit by Opus 4.7 Auto Memory / Auto Dream — the watchdog observer detects an edit, parses the (possibly rewritten) frontmatter, calls
engine.remember(...)with the new content and importance, and the reflection pass (mnemo.reflect) then picks upmetadata.dreamed_atmarkers so they don’t get double-consolidated.
Auto Dream coordination
v0.3.1 adds ReflectionMode::Coordinated which honours the same cadence
Auto Dream does: skip when fewer than 5 new records have accumulated or
fewer than 24 h have elapsed since the last successful pass. Run via:
# The engine is exposed indirectly through the MCP tool surface; the
# Python bridge also owns a MnemoClient instance you can drive directly:
client = memory._ensure_client()
# Coordinated is the default; pass force=True to override.
client.reflect(mode="coordinated")
Parse the Auto Dream “organization report” trailer (the markdown block
with Consolidated: N / Removed: M / Reindexed: K) automatically —
mnemo.reflect emits a dream_report_ingested audit event per memory
containing one and marks the record so subsequent passes are no-ops.
Caveats (v0.3.1)
- Python
MnemoClientcurrently does not attach a full-text index, solexicalandhybrid_rrfrecall strategies return no results when driven from Python. Tracked; seedocs/benchmarks/2026-04-21-mnemo-v0.3.0.md. - If
OPENAI_API_KEYis unset,MnemoClientfalls back toNoopEmbedding. Semantic recall is then random. Set the key, or wait for the v0.3.x ONNX-embedding repair.
Anthropic memory-tool (memory_20250818)
Mnemo ships a client-side handler that satisfies Anthropic’s
memory-tool spec
for the memory_20250818 surface. It maps the six tool commands
(view, create, str_replace, insert, delete, rename) onto
Mnemo’s storage so the model gets a persistent “memory directory”
that’s audited, hash-chained, and ACL-aware by default.
Install
pip install 'mnemo-db[anthropic-memory-tool]'
The extra pulls anthropic>=0.40 for SDK-driven integration. The
server itself does not import Anthropic at runtime, so handlers can
also be wired directly into raw API requests.
Quick start
from anthropic import Anthropic
from mnemo import MnemoClient, MnemoMemoryToolServer
# 1. Wire a Mnemo backend.
client = MnemoClient(db_path="memory.mnemo.db", agent_id="agent-1")
# 2. Construct the handler.
server = MnemoMemoryToolServer(client=client)
# 3. Register the tool with Anthropic.
anthropic = Anthropic()
extra_headers = {}
if server.beta_header():
extra_headers["anthropic-beta"] = server.beta_header()
response = anthropic.messages.create(
model="claude-opus-4-7",
max_tokens=2048,
tools=[server.tool_schema()],
messages=[{"role": "user", "content": "Help me with my project."}],
extra_headers=extra_headers or None,
)
# 4. When the model emits a `tool_use`, dispatch through the server.
for block in response.content:
if block.type == "tool_use" and block.name == "memory":
result = server.handle({
"type": "tool_use",
"id": block.id,
"name": block.name,
"input": block.input,
})
# Feed `result` back into the next `messages.create` call as a
# `{"role": "user", "content": [{... tool_result ...}]}` block.
Storage shape
Every “file” is one Mnemo MemoryRecord with two tags:
memorytool— flags it as belonging to this surface.path:/memories/...— the canonical absolute path.
Directories are implicit. They exist when at least one file lives
under that prefix. view of a directory enumerates first-level
children; delete of a directory recursively forgets every record
under that prefix; rename re-writes every descendant under the
new prefix.
This means:
- Every file write lands in Mnemo’s hash chain.
forgetpropagates correctly — there is no separate file system to keep in sync.- ACL enforcement is whatever the underlying
MnemoClientis configured for. The handler doesn’t bypass scope checks.
Beta header
The basic surface needs no anthropic-beta header. When using the
Managed Agents container,
construct with managed_agents_beta=True:
server = MnemoMemoryToolServer(client=client, managed_agents_beta=True)
extra_headers = {"anthropic-beta": server.beta_header()}
# server.beta_header() == "managed-agents-2026-04-01"
Path-traversal safeguards
The spec calls path validation “the most important security control”
for client-side handlers. MnemoMemoryToolServer enforces:
- Every
path(andold_path/new_path) must start with the configured root (default/memories). - Paths are normalised with
os.path.normpath; the result must still be under the root. - Inputs containing
..segments or URL-encoded%2e%2e/%2fsequences are rejected before normalisation.
Override the root with MnemoMemoryToolServer(client=..., root="/other")
if you need a different namespace.
Return-string contract
All return values are spec-pinned. Tests in
python/tests/test_anthropic_memory_tool.py assert the exact strings
listed in the memory-tool spec:
viewof a directory:Here're the files and directories up to 2 levels deep in {path} ...viewof a file:Here's the content of {path} with line numbers:\n{6-char-right-padded line no}\\t{content}createsuccess:File created successfully at: {path}createduplicate:Error: File {path} already existsstr_replacesuccess:The memory file has been edited.\n{snippet with line numbers}str_replaceno match:No replacement was performed, old_str `{old}` did not appear verbatim in {path}.str_replacemulti:No replacement was performed. Multiple occurrences of old_str `{old}` in lines: {a, b, ...}. Please ensure it is uniqueinsertsuccess:The file {path} has been edited.deletesuccess:Successfully deleted {path}renamesuccess:Successfully renamed {old} to {new}
Errors are returned with is_error: true on the tool_result
block so the model can react.
Sources
- Anthropic — Memory tool docs
- Anthropic — Claude Opus 4.7 release post
- Anthropic — Effective context engineering for AI agents
OpenAI Agents SDK — GA resume contract
The 2026-04-16 GA release of openai-agents generalised the preview
Session protocol into three cooperating interfaces:
SessionStore— per-turn conversation items.SnapshotStore— durableRunState+SandboxSessionStateblobs.ResumeProvider— locator layer for picking aSnapshotRefto resume from.
Mnemo ships adapters for both halves.
MnemoSessionStore (chat history)
python/mnemo/openai_sessions.py. Stores each conversation turn as a
session-tagged episodic memory; survives process restarts.
from mnemo.openai_sessions import MnemoSessionStore
session = MnemoSessionStore(
db_path="agent.mnemo.db",
agent_id="user-42",
session_id="support-2026-04-20",
)
# Pass `session=session` to the Agents SDK `Runner`.
MnemoSnapshotStore (run state)
python/mnemo/openai_sessions_ga.py. Persists RunState +
SandboxSessionState and lets the GA SDK resume crashed runs.
from mnemo.openai_sessions_ga import MnemoSnapshotStore
store = MnemoSnapshotStore(
session_id="support-2026-04-20",
db_path="agent.mnemo.db",
agent_id="user-42",
workspace_backend="local",
workspace_root="/var/mnemo/snapshots",
)
ref = await store.save_snapshot(run_state, sandbox_state)
# ... process crashes ...
ref, run, sandbox = await store.resume(from_ref="latest")
SnapshotRef.as_uri() returns a stable snapshot://<session>/<ts> URI
suitable for the forthcoming MCP resource exposure layer.
Payload storage policy
- Inline — payloads at or below
inline_threshold_bytes(default 64 KiB) live in the Mnemo memory body as base64, with a SHA-256 digest. - Offload — larger payloads go to a pluggable
WorkspaceStorage. Mnemo only keeps the locator + SHA-256; theload_snapshotpath verifies the digest on every read.
Workspace backends
local— shipped. Writes underworkspace_root.s3/r2/gcs/azure— stubs in v0.3.1. TheWorkspaceStorageclass raisesNotImplementedErrorwith aNotImplementedError("…install mnemo[openai-sandbox-<backend>] …")message. The v0.3.1 roadmap ships a realaioboto3-backed S3 backend; R2/GCS/Azure follow once theSnapshotSpecshape stabilises in the GA SDK.
Install with pip install mnemo[openai-agents].
Example (crash / resume)
See python/examples/openai_agents_snapshot_example.py for a 3-step
agent that writes two snapshots, crashes before the final reply, and
resumes from the latest snapshot on the second process start. The
equivalent openai_agents_resume_s3_example.py ships with the v0.3.1
S3 backend.
Workspace backends (parity matrix)
The OpenAI Agents SDK GA snapshot store persists an agent’s workspace tree to object storage. Mnemo ships four backends. All four write the identical object layout and share the identical signing contract, so a snapshot is portable between providers by copying objects — nothing in the manifest is provider-specific.
<bucket-or-container>/<key_prefix>/manifest.json
<bucket-or-container>/<key_prefix>/manifest.sig
<bucket-or-container>/<key_prefix>/files/<rel_path>
The four backends
| Backend | Class | Extra | Client | Implementation |
|---|---|---|---|---|
| AWS S3 | S3Workspace | mnemo-db[openai-sandbox-s3] | boto3 | base class |
| Cloudflare R2 | CloudflareR2Workspace | mnemo-db[openai-sandbox-r2] | boto3 | subclasses S3Workspace |
| Google Cloud Storage | GCSWorkspace | mnemo-db[openai-sandbox-gcs] | google-cloud-storage | standalone |
| Azure Blob | AzureBlobWorkspace | mnemo-db[openai-sandbox-azure] | azure-storage-blob | standalone |
Why R2 subclasses and the other two do not
R2 speaks the S3 wire protocol, so it inherits the entire storage contract and encodes only an endpoint and a region. That makes it a one-paragraph maintenance burden.
GCS and Azure Blob are not S3-wire-compatible:
- GCS exposes an interoperability XML API that is a partial S3
emulation. It does not cover the paginated
list_objects_v2or batchdelete_objectscallsS3Workspacerelies on, so subclassing would inherit methods that fail at runtime against a real bucket. - Azure Blob shares no wire surface at all — different auth (shared key / SAS / Entra ID), different REST API, different pagination.
Both therefore use their provider’s native client, as a single
standalone class each. There is deliberately no abstract base layer:
the genuinely shared logic (manifest construction, Ed25519 signing,
per-blob digest verification) already lives in
mnemo.openai_sandbox.manifest, and what remains per backend is a
~5-method object-store adapter. An ABC over five methods would add a
layer without removing duplication.
One spec shape, four backends
RemoteSnapshotSpec has a single bucket field. Azure calls its
top-level namespace a container; that name goes in bucket anyway.
RemoteSnapshotSpec(backend="azure", bucket="<container-name>", ...)
This is intentional. A provider-specific field would push a conditional
into every consumer of a spec; one field keeps MnemoSnapshotStore’s
dispatch to a single spec.backend lookup.
Construction
# AWS S3 — standard credential chain
from mnemo.openai_sandbox import S3Workspace
ws = S3Workspace(bucket="agent-snapshots")
# Cloudflare R2 — account ID + access keys
from mnemo.openai_sandbox import CloudflareR2Workspace
ws = CloudflareR2Workspace(
bucket="agent-snapshots", account_id="abc123",
access_key_id="...", secret_access_key="...",
)
# GCS — Application Default Credentials
from mnemo.openai_sandbox import GCSWorkspace
ws = GCSWorkspace(bucket="agent-snapshots")
# Azure Blob — connection string (also works against Azurite)
from mnemo.openai_sandbox import AzureBlobWorkspace
ws = AzureBlobWorkspace.from_connection_string(
"DefaultEndpointsProtocol=...", container="agent-snapshots",
)
Every backend then exposes the same three methods: save_workspace,
load_workspace, delete_workspace.
Provider behaviour worth knowing
| Behaviour | S3 / R2 | GCS | Azure Blob |
|---|---|---|---|
| Overwrite existing object | silent | silent | rejects unless overwrite=True |
| Prefix listing | explicit paginator | auto-paginated | auto-paginated |
| Batch delete | delete_objects (1000/call) | per blob | per blob |
The Azure row is the one that bites. upload_blob defaults to raising
ResourceExistsError on an existing name, so AzureBlobWorkspace
passes overwrite=True — without it, re-saving a workspace to the same
key_prefix would fail on the second attempt, making save_workspace
non-idempotent across retries. A regression test pins this.
Integrity model (identical across backends)
save_workspacewalks the tree, records a SHA-256 per file, buildsmanifest.json, and signs it with Ed25519.- The returned spec carries
manifest_sha256. load_workspacere-checks that digest against what the provider served before verifying the signature, then verifies every per-file digest while materialising the tree.
Step 2/3 is what catches tampering in the bucket even if an attacker also re-signs the manifest with a rotated key. A tampered manifest fails closed, and each backend has a test that mutates the stored manifest and asserts the load raises.
Testing
| Backend | Unit substrate | Live gate |
|---|---|---|
| S3, R2 | moto (in-memory S3) | R2_ACCOUNT_ID + R2_ACCESS_KEY_ID + R2_SECRET_ACCESS_KEY + R2_BUCKET |
| GCS | in-process fake client | GCS_BUCKET + Application Default Credentials |
| Azure | in-process fake container client | AZURE_STORAGE_CONNECTION_STRING + AZURE_CONTAINER |
GCS has no moto equivalent and its official emulator (fake-gcs-server)
is a Docker image; Azurite likewise needs Docker or npm. So those two use
small in-process fakes that implement only the handful of client methods
the backend actually calls — a new call reaching for an unfaked method
fails loudly rather than passing against a permissive mock. The live
gates are skipped by default and never run in CI.
The Azure live test works unmodified against Azurite, whose connection string is well-known — the cheapest way to exercise the real SDK path locally.
Cloudflare R2 workspace backend
Mnemo’s MnemoSnapshotStore — the OpenAI
Agents SDK GA snapshot store — supports persisting workspace trees to
any S3-compatible object store. v0.3.4 ships
CloudflareR2Workspace
as a thin subclass of S3Workspace
so R2-backed snapshots inherit every feature of the AWS path
unchanged: signed manifests, Ed25519 verification, per-blob digest
checks, batched delete.
Install
pip install 'mnemo-db[openai-sandbox-r2]'
The extra pulls boto3>=1.34 and cryptography>=42. R2’s S3 API is
wire-compatible with AWS SDK v4 signing, so no R2-specific client
library is needed.
Quick start
from mnemo.openai_sandbox.r2_workspace import CloudflareR2Workspace
from mnemo.openai_sandbox.manifest import WorkspaceSigner
signer = WorkspaceSigner.generate_ephemeral() # or load yours from KMS
ws = CloudflareR2Workspace(
bucket="agent-snapshots",
account_id="abc123def456", # R2 account ID
access_key_id="...", # R2 access key
secret_access_key="...", # R2 secret access key
)
spec = ws.save_workspace(
workspace_root="/tmp/agent-state",
signer=signer,
workspace_id="run-2026-04-25-1",
created_at="2026-04-25T00:00:00Z",
key_prefix="agents/agent-1",
)
# `spec` carries backend="r2" — MnemoSnapshotStore dispatches via
# this field to keep the load path symmetric with save.
print(spec)
# RemoteSnapshotSpec(backend='r2', bucket='agent-snapshots',
# key_prefix='agents/agent-1', manifest_sha256='...')
Differences from S3 in one line
| Knob | AWS S3 | Cloudflare R2 |
|---|---|---|
endpoint_url | regional default | https://{account_id}.r2.cloudflarestorage.com |
region | us-east-1 etc. | "auto" (literal) |
| Addressing | path or virtual | "virtual" |
| Signature | sigv4 | sigv4 |
| Credential providers | full AWS chain | access keys only |
CloudflareR2Workspace sets every R2-specific knob in its
constructor; nothing else in the snapshot path needs to know it’s R2.
Storage layout
Same as S3Workspace. One R2 object per file in the workspace plus
two top-level objects per snapshot:
<bucket>/<key_prefix>/manifest.json # signed JSON manifest
<bucket>/<key_prefix>/manifest.sig # detached Ed25519 signature
<bucket>/<key_prefix>/files/<rel_path> # one per source file
Symlinks are recorded in the manifest (not as separate objects) so the load path can recreate them after every regular file is fetched
- verified. See the OpenAI Agents GA integration page for the manifest schema.
Live-credential test
The Mnemo test suite runs a moto-S3 round-trip against
CloudflareR2Workspace on every CI build. To run a real R2 round-trip
locally, export:
export R2_ACCOUNT_ID=<account>
export R2_ACCESS_KEY_ID=<key>
export R2_SECRET_ACCESS_KEY=<secret>
export R2_BUCKET=<bucket>
pytest python/tests/test_r2_workspace.py::test_live_r2_round_trip -v
The test creates a small workspace tree, dumps it to R2 under the
mnemo-tests/live-r2/ prefix, fetches it back, asserts file contents
match, and cleans up. Skipped silently when any of the four env vars
are unset.
Cost note
R2’s free tier is 10 GB storage + 1M Class-A operations / month + 10M Class-B operations / month. A typical mnemo workspace snapshot is ~10 files at ~1 MB each, so a few thousand snapshots fit inside the free tier — see Cloudflare R2 pricing.
R2 also has zero egress fees, which makes it a good fit for snapshot restore traffic patterns (lots of reads on a bad day, very few on a good one).
Sources
Letta Conversations-style shared memory
Letta’s Letta-Code release (2026-04-06) introduced a Conversations API where multiple agents share a single memory stream rather than each maintaining its own.
Mnemo v0.4.0-rc1 ships
MnemoLettaShared
— the same shape (attach / detach / read / write /
list_participants) backed by Mnemo memories rather than a remote
Letta service. That keeps shared state on Mnemo’s audit log + hash
chain + ACL surface even when the agents are running through Letta’s
orchestration.
Install
MnemoLettaShared lives in the core mnemo package — no extra
needed:
pip install mnemo-db
Quick start
from mnemo import MnemoClient
from mnemo.letta_adapter import MnemoLettaShared
client = MnemoClient(db_path="conversation.mnemo.db", agent_id="orchestrator")
shared = MnemoLettaShared(
client=client,
conversation_id="design-review-2026-04-25",
)
shared.attach("agent-architect")
shared.attach("agent-reviewer")
shared.write(
"Initial proposal: split the API into v1 / v2 prefixes.",
source_agent_id="agent-architect",
)
shared.write(
"Concern: deprecation timeline for v1 is unclear.",
source_agent_id="agent-reviewer",
)
for msg in shared.read():
print(f"[{msg.source_agent_id}] {msg.content}")
Storage shape
- Each shared message is one Mnemo
MemoryRecordwith two tags:conversation:<id>— every record in the conversation carries this.participant:<source_agent_id>— the author.
- Participants list is a single Mnemo record tagged
conversation:<id>+meta:participants, body = a JSON list of agent IDs. Updated on everyattach/detach.
This keeps the conversation audit-log-replayable: every write is a hash-chained Mnemo memory, every participant change is a discrete write the operator can replay.
Conflict policy
The adapter does not pre-resolve conflicts at write time. When
two participants write overlapping content within 60 seconds, both
records land in Mnemo and the existing
ResolutionStrategy::EvidenceWeighted
scorer ranks them at recall time. Pre-resolving at write time would
amount to silently dropping one participant’s contribution — the
exact failure mode shared memory is supposed to avoid.
To inspect cross-participant overlaps for operator review:
for earlier, later in shared.overlapping_writes_within(seconds=60.0):
print(f"{earlier.source_agent_id} → {later.source_agent_id}: "
f"{earlier.content[:50]}... / {later.content[:50]}...")
Read semantics
# Full stream, time-ordered.
shared.read()
# Filter by author.
shared.read(from_agent="agent-reviewer")
# Forward a query through Mnemo's hybrid retrieval (vector + BM25).
shared.read(query="deprecation timeline", limit=10)
read() excludes the meta:participants metadata record
automatically, so callers see only real messages.
Why a Mnemo-backed adapter rather than a Letta-API client
The blog post called for a MnemoLettaShared adapter, not a
client. The shape — attach / detach / read / write — is
useful by itself: any time multiple agents need a shared, audited,
queryable history, this adapter does the job without needing a Letta
account or API key. If you also use Letta’s orchestrator, point its
agents at this adapter as their memory backend and the conversation
state is portable.
Sources
- Letta-Code release (2026-04-06)
- Letta — Benchmarking AI Agent Memory
letta_adapter.pysource- Example:
examples/letta_shared_conversation.py
mnemo mcp-server — hardened MCP STDIO mode
v0.4.0-rc3, Task B2. Defends against the OX-MCP “exfiltrate-then-act” disclosure (2026-04-24) by refusing inherited secrets, JSON-injection argv, and untrusted parent processes BEFORE any engine state is constructed.
Why this exists
The default mnemo startup path is convenient: it reads OPENAI_API_KEY,
MNEMO_ENCRYPTION_KEY, MNEMO_POSTGRES_URL, and a stack of CLI flags
straight from the environment. That’s fine for local development. It is
not fine when an attacker can spawn the binary inside someone else’s
shell — the OX-MCP disclosure showed how a poisoned Claude Code session
can cause the host to exec an MCP server with the attacker’s manifest
attached and the user’s secrets visible.
mnemo mcp-server --manifest <path> is a hardened entry point with a
narrower trust boundary:
- All privileged knobs (keystore, audit log destination, allowed tools, allowed agents, allowed parents) live in a TOML manifest the operator controls.
- Sensitive env vars are an automatic refusal.
--config-style argv injection is an automatic refusal.- Non-TTY parents that aren’t on the manifest’s allow-list are an automatic refusal.
The manifest
keystore_path = "/etc/mnemo/keystore.toml"
audit_log_path = "/var/log/mnemo/audit.jsonl"
allowed_tools = ["mnemo.recall", "mnemo.verify"]
allowed_agents = ["claude-prod"]
allowed_parents = ["claude", "systemd"]
A full annotated example lives at
examples/mcp-server/manifest.toml.
Keystore
The manifest’s keystore_path points at a chmod-restricted TOML file:
key_id = "mnemo-prov-2026-04"
key_hex = "<64 hex chars / 32 bytes / openssl rand -hex 32>"
The hardened mode loads this file at startup and attaches a
ProvenanceSigner (B1) to the engine. Every
recall(..., with_provenance=true) returns a verifiable HMAC receipt.
Rotate by writing a new file with a fresh key_id and updating the
manifest.
The safe-spawn gauntlet
Before constructing any engine state, the binary runs three checks:
- Inherited secrets. Refuses if the env carries any of:
ANTHROPIC_API_KEY,OPENAI_API_KEY,HF_TOKEN,AWS_SECRET_ACCESS_KEY,GITHUB_TOKEN,MNEMO_ENCRYPTION_KEY. Run the binary through a secret-clearing wrapper (env -i, systemdEnvironment=). Override at your own risk:MNEMO_REJECT_INHERITED_SECRETS=0. - Args-based config. Refuses if argv contains
--config,--config-json,--inline-config,-c, or--secret(in any=valueform). All config must live in the manifest. - Untrusted parent. When stdin is not a TTY, the parent
process basename (set via
MNEMO_PARENT_BASENAME) must appear inmanifest.allowed_parents. If the variable is unset the check is skipped — running interactively (TTY present) also lifts the check.
Each refusal exits non-zero with a stderr message that names the violating key/arg/parent.
Running it
# 1. Clear the env, set the parent assertion, pass the manifest.
env -i \
PATH="$PATH" HOME="$HOME" \
MNEMO_PARENT_BASENAME=systemd \
mnemo mcp-server --manifest /etc/mnemo/manifest.toml
Under systemd:
[Service]
Type=simple
Environment=MNEMO_PARENT_BASENAME=systemd
ExecStart=/usr/local/bin/mnemo mcp-server --manifest /etc/mnemo/manifest.toml
ProtectSystem=strict
PrivateTmp=true
NoNewPrivileges=true
Verifying it
A quick “does the gauntlet actually fire” smoke test:
ANTHROPIC_API_KEY=leak mnemo mcp-server --manifest /etc/mnemo/manifest.toml
# refused to start: inherited sensitive env var "ANTHROPIC_API_KEY" ...
mnemo mcp-server --manifest /etc/mnemo/manifest.toml --config-json '{}'
# refused to start: command-line carries config-style argument ...
The full integration suite that exercises every refusal path lives in
crates/mnemo-cli/tests/safe_spawn_integration.rs.
Role-aware tool filter (v0.4.2 — A1)
Mnemo’s MCP server aligns with the 2025-11-25
MCP authorization spec
role-based annotations. The manifest can declare an optional
[role_filter] block that gates tools/list (filters the advertised
catalog) and tools/call (denies disallowed calls with a spec-compliant
-32601).
[role_filter]
caller_roles = ["auditor"]
default = "deny_all"
[role_filter.allow]
"mnemo.recall" = ["auditor", "agent"]
"mnemo.verify" = ["auditor"]
"mnemo.remember" = ["agent"]
"mnemo.forget" = ["agent"]
[role_filter.deny]
"mnemo.delegate" = ["auditor"]
Rules:
- Deny always wins. A tool that appears in both
allowanddenyfor the same role is denied. default = "allow_all"(the implicit default) lets any tool not named inallow/denythrough. Usedeny_allfor a strict allow-list.caller_rolesdeclares the role assignment the operator has made for the binary itself. In stdio transport this is the entire caller identity; in future HTTP transports it composes with roles inferred from theAuthorizationheader.- Every denied call emits an
McpRoleDenied { caller_id, tool_name, attempted_at, reason }row toaudit_log_path. - Omitting the block keeps pre-v0.4.2 behaviour byte-for-byte. Every advertised tool stays reachable and no audit events are emitted.
The filter contract (RoleFilter trait + ManifestRoleFilter impl) is
public, so a custom filter can replace the manifest-driven default at
test time. See
crates/mnemo-mcp/src/role_filter.rs
and the three integration tests under
crates/mnemo-mcp/tests/
(role_filter_allow_deny.rs, role_filter_audit_event.rs,
role_filter_no_block_when_unset.rs).
What this does NOT cover
- Capability-leased reads (the old B2 follow-up) are not shipped: the
never-wired lease store was removed as dead code. The design — per-read lease
tokens gating
forget_subject— is captured in #126 for a future authenticated, multi-caller transport where a lease has real cross-caller value (on stdio the operator is the only caller). - The DPDPA consent-token-per-write path (B4).
- The Letta-protocol-compat surface (B5).
- Per-tool-method enforcement of the role filter at
tools/calldispatch — the manifest schema, the filter trait/impl, and the audit emission are shipped in v0.4.2; threading the filter through everyMnemoServertool method body is still pending. Themnemo-envelopeOTel exporter kind that a later step depends on is not built — seedocs/roadmap/planned-crates.md.
For the threat model and the full design notes, see the rationale at
the top of crates/mnemo-cli/src/safe_spawn.rs.
Compatibility note (v0.4.3 — U1)
The MCP wire-protocol version mnemo’s server speaks (2024-11-05,
with the 2025-11-25 authorization spec
layered on top) is independent of the client SDK version your
agent uses. SDK refreshes are common and don’t require a mnemo-side
rev unless the spec itself moves.
The current version-skew matrix tracks tested combinations of the four official client SDKs:
mcp-python(refreshed 2026-05-01)mcp-go(refreshed 2026-05-01)mcp-ruby(refreshed 2026-05-02)mcp-csharp(refreshed 2026-05-02)
If your agent hits an SDK-side incompatibility, consult the matrix
first — most issues land on a row that documents which mnemo cut
shipped against that SDK pair. The matrix is enforced in CI by
crates/mnemo-mcp/tests/sdk_matrix_doc_present.rs, so the doc itself
cannot silently disappear ahead of an SDK-bump release.
MCP 2026 Roadmap alignment (v0.4.4 — U1)
Superseded as a statement of current direction. This section maps mnemo against the March 2026 roadmap. The 2026-07-28 spec release came after it. For what mnemo actually implements today, revision by revision, see MCP 2026-07-28 conformance. The mapping below is kept as history.
The MCP 2026 Roadmap (published 2026-03-09 by lead maintainer David Soria Parra) reorganises the protocol’s direction around four priority areas. The honest mnemo stance against each is below — spec-context anchor, not compliance claim.
| MCP 2026 priority | What it covers | mnemo stance |
|---|---|---|
| Transport Evolution and Scalability | Stateless Streamable HTTP, .well-known server-discovery metadata, multi-tenant gateway behavior | Follower. mnemo speaks MCP via the rmcp = "3.0" workspace dep. SEPs land in rmcp first; mnemo upgrades when they’re stable, not before. |
| Agent Communication | Tasks-primitive lifecycle gaps; agent ↔ agent semantics outside the tool/resource layer | Observer. mnemo’s mnemo.delegate + ACL/permission model is the existing surface; further coupling to a Tasks primitive waits on the SEP outcome. |
| Governance Maturation | Contributor ladder + WG delegation for the spec itself | Observer. Not a downstream surface mnemo participates in; we follow the spec the WGs ship. |
| Enterprise Readiness | Audit trails, SSO-integrated auth, gateway behavior, configuration portability | Aligned-by-design. Operator-held HMAC keystore (keystore_path in the manifest), AES-256-GCM at-rest content encryption (MNEMO_ENCRYPTION_KEY), mnemo-compliance crate’s DPDPA consent-token-per-write surface, dual DuckDB / PostgreSQL backend portability, and the role-aware tool filter (v0.4.2 §“Role-aware tool filter”) together form the attestable memory layer regulated-workflow buyers can defend today — independent of any one cloud’s audit boundary. |
The honest framing: mnemo claims alignment-by-design with one of
four priorities, not roadmap compliance. The other three priorities
are spec-evolution work where mnemo follows rmcp’s implementation
of the SEPs as they’re written. Buyers reading the roadmap should
hear “mnemo’s existing audit story already serves the Enterprise
Readiness ask,” not “mnemo is MCP-2026-ready.”
AMP / memorywire conformance (v0.4.13)
Alongside the MCP STDIO surface, mnemo ships an AMP / memorywire
interop adapter in the mnemo-amp crate.
AMP models memory as 5 operations (remember / recall /
forget / merge / expire) over 4 memory types (episodic /
semantic / procedural / working), carried in a self-describing
JSON envelope validated against a JSON-Schema 2020-12 document
(mnemo_amp::schema()).
The adapter is a MemoryStore-conformant surface over a real
MnemoEngine. Two ops are deliberately thin compositions over
existing primitives rather than assumed engine methods:
mergefolds N records into one consolidated record (rememberwithSourceType::Consolidation) and retires the originals (forgetwith theConsolidatestrategy). It is not mnemo’sengine.merge, which is a branch-timeline merge.expiresetsexpires_atand runs the existingrun_ttl_sweeplifecycle path (there is noengine.expire).
A fan-out AmpRouter broadcasts writes to several backends and fuses
multi-adapter recall with Reciprocal Rank Fusion. An optional
HITL diff-and-approve hook gates long-term (semantic /
procedural) writes and records each approval as a Decision event in
mnemo’s hash-chained audit log, so the approve trail is tamper-evident
and replayable.
Conformance mirrors the cross-adapter suite: recall@5 on a small
labelled corpus end-to-end against the embedded DuckDB backend, and
RRF-holds-under-rank-0-injection vs max-fusion (RRF keeps a
genuinely-relevant item on top; max-fusion is fooled by an adversarial
rank-0 injection). Run the end-to-end smoke binary with
cargo run --release --bin amp_conformance -p mnemo-amp.
Honest scope: the crate provides the wire format, the schema
document, and the engine-backed surface. AMP transport framing
(HTTP / stdio, .well-known schema discovery) is left to the
embedding application.
MCP 2026-07-28 conformance
State of mnemo against the 2026-07-28 MCP specification revision, row by
row. Anchored at commit aec648f
against rmcp 3.1.3 (what the workspace rmcp = "3.0" requirement resolves to).
This page exists because mnemo’s posture is to follow rmcp’s implementation rather than race the spec, and that posture is only defensible if there is a document saying which parts are and are not done. Without one, “we follow rmcp” is indistinguishable from “we have not looked”.
What the statuses mean
| Status | Meaning |
|---|---|
| CONFORMS | mnemo satisfies the requirement today, either directly or because it never adopted the thing being removed. |
| GAP | mnemo could close this without waiting for anyone. It has not. |
| UPSTREAM-BLOCKED | Closing it requires rmcp to move first. The rmcp version that would unblock it is named in the row. mnemo does not fork rmcp. |
The headline
mnemo negotiates 2025-11-25, not 2026-07-28.
rmcp 3.1.3 knows the newer revision - it carries ProtocolVersion::V_2026_07_28,
the CacheScope type, the SEP-2243 header constants and the MRTR result types -
but its ProtocolVersion::LATEST is still V_2025_11_25, so that is what an
initialize handshake settles on. Most of what follows falls out of that one
fact.
The revisions mnemo advertises, which
crates/mnemo-mcp/tests/mcp_2026_07_28_conformance.rs asserts against the
running server:
2024-11-052025-03-262025-06-182025-11-25
2026-07-28 is deliberately not in that list, and goes back in when mnemo
implements it, not when rmcp does.
Lifecycle and transport
| Spec change | What the spec requires | mnemo today | rmcp 3.1.3 today | Status |
|---|---|---|---|---|
| Sessions removed (SEP-2567) | No protocol sessions, no Mcp-Session-Id. List endpoints must not vary per connection. Cross-call state uses “explicit, server-minted handles passed as ordinary tool arguments”. | Never used sessions. Cross-call state is already handle-threaded: checkpoint_id carries checkpoint → branch/replay, and lease_token carries recall → forget_subject. tools/list varies by the capability presented on that request (ADR 0002), which is a per-request property, not a per-connection one. Pinned by explicit_handle_roundtrip.rs. | Still implements Mcp-Session-Id for revisions at or below 2025-11-25. | CONFORMS |
| Stateless lifecycle (SEP-2575) | Remove initialize / notifications/initialized. Every request carries io.modelcontextprotocol/protocolVersion and client capabilities in _meta. | Uses the initialize handshake; get_info() answers it. The per-request _meta plumbing already exists - mnemo reads its own capability key out of _meta on every call (ADR 0002) - so this is a lifecycle change, not new plumbing. A caller should assume it must perform the initialize handshake and must not send per-request io.modelcontextprotocol/protocolVersion; it will be ignored. | Gated behind negotiating 2026-07-28; LATEST is 2025-11-25, so the gate never opens by default. | UPSTREAM-BLOCKED - unblocks when rmcp moves ProtocolVersion::LATEST to V_2026_07_28. |
server/discover (SEP-2575) | Servers MUST advertise supported protocol versions, capabilities and identity. | Inherits rmcp’s default discover, derived from supported_protocol_versions() and get_info(). | Provides the RPC and the default implementation. | CONFORMS |
| Advertised version set | What server/discover reports must be what the server serves. | Narrowed to the four revisions above. Previously took rmcp’s default (ProtocolVersion::KNOWN_VERSIONS), which advertised 2026-07-28 - a revision mnemo does not serve. See “The defect this page found”. | Default is every revision the SDK knows, 2026-07-28 included. | CONFORMS (fixed here) |
subscriptions/listen (SEP-2575) | Replaces the HTTP GET endpoint and resources/subscribe / resources/unsubscribe. | Implements no subscriptions at all, so there is nothing to migrate. | Implements subscriptions/listen. | CONFORMS |
ping, logging/setLevel, notifications/roots/list_changed removed (SEP-2575) | These methods go away. | Overrides none of them; rmcp’s set_level default already answers method-not-found. | Retains them for older revisions. | CONFORMS |
| SSE resumability removed (SEP-2575) | No Last-Event-ID, no SSE event IDs. A broken stream loses the in-flight request. | stdio by default. Under the optional http-transport feature the stream is rmcp’s Streamable HTTP, so resumability is rmcp’s behaviour, not mnemo’s. A caller should assume Last-Event-Id resumption still works at the negotiated revision, and should not rely on it, since it disappears when mnemo adopts 2026-07-28. | Still implements Last-Event-Id for older revisions. | UPSTREAM-BLOCKED |
Headers and caching
| Spec change | What the spec requires | mnemo today | rmcp 3.1.3 today | Status |
|---|---|---|---|---|
Mcp-Method / Mcp-Name (SEP-2243) | Required on Streamable HTTP POST requests so gateways can route without reading the body. | Not applicable on stdio, which is the default transport. Under http-transport the headers are rmcp’s to emit and validate. A caller should assume these headers are neither required nor validated, so a gateway cannot route mnemo traffic on them today and must read the body. | Has HEADER_MCP_METHOD and HEADER_MCP_NAME, explicitly gated on ProtocolVersion::STANDARD_HEADERS, which is V_2026_07_28. | UPSTREAM-BLOCKED - the code path exists but cannot activate while the negotiated revision is 2025-11-25. |
ttlMs on list results (SEP-2549) | tools/list, prompts/list, resources/list, resources/read and resources/templates/list carry a freshness hint in milliseconds. | Implemented. tools/list advertises 60000 (the catalog is compiled in and role-filtered, so it is stable for a fixed capability, but an operator manifest change must not be cached for an hour). resources/list advertises 0, immediately stale, because any mnemo.remember invalidates the listing. mnemo serves no prompts/* or resources/templates/*. | Fields present on the list-result types. | CONFORMS |
cacheScope on list results (SEP-2549) | "public" or "private", controlling whether shared intermediaries may cache. | Implemented as private on both listings, and that is a correctness requirement rather than a tuning choice. See the note below. | CacheScope enum present; note its Default is Public. | CONFORMS |
Deterministic tools/list order | Servers SHOULD return tools in a deterministic order so clients can cache. | Filters the router’s list by caller role; does not reorder it. | ToolRouter::list_all() sorts by name before returning. | CONFORMS (via rmcp) |
| Resource-not-found error code | -32002 becomes -32602 (Invalid Params). | Calls McpError::resource_not_found, which emits -32002. A caller should assume resource-not-found arrives as -32002, not -32602. | That constructor still emits ErrorCode::RESOURCE_NOT_FOUND, which is -32002. | GAP, deliberately unclosed - see below. |
Why
cacheScopeisprivateand must stay that way. Both of mnemo’s listings are scoped to the caller’s per-request identity (ADR 0002): two callers presenting different capabilities see different tool catalogs, andresources/listreturns one agent’s memory records. Apublicscope would permit a shared intermediary to serve one caller’s catalog, or one agent’s memories, to a different caller.CacheScope::default()isPublic, so leaving the field unset is not the safe option it appears to be once anything downstream begins reading it.per_caller_listings_are_never_publicly_cacheableincrates/mnemo-mcp/tests/mcp_2026_07_28_conformance.rsasserts this on both surfaces, and was verified by mutation.These fields belong to
2026-07-28while mnemo negotiates2025-11-25, so no client is currently promised them. They are emitted regardless: a client that does not know the field ignores it, while an intermediary that does understandcacheScopeis told the truth now rather than after the eventual revision bump.
Why the error-code row stays open on purpose. mnemo emits
-32002because that is what2025-11-25- the revision it actually negotiates - specifies. Changing it to-32602now would make mnemo non-conformant with the version it speaks in order to match a version it does not. The row closes when mnemo adopts2026-07-28, not before.
Results
| Spec change | What the spec requires | mnemo today | rmcp 3.1.3 today | Status |
|---|---|---|---|---|
resultType on all results (SEP-2322) | Required field, "complete" or "input_required". Clients MUST read an absent field as "complete". | Returns CallToolResponse::Complete. Observed on the wire at the negotiated revision: result_type: None, which is correct - rmcp clears the field for peers that negotiated an older version. | Models ResultType and clears it per negotiated version. | CONFORMS (via rmcp) |
| Multi Round-Trip Requests (SEP-2322) | Server-initiated requests are replaced by InputRequiredResult plus a client retry. | Every mnemo tool is synchronous and always resolves to Complete, so there is nothing to implement. | Implements the MRTR types and the client-side retry loop. | CONFORMS |
Authorization (RFC 9728, RFC 8707)
This section exists because its absence was the most likely thing to be mistaken for conformance. The rows above are about the wire protocol; nothing in them says whether mnemo does OAuth. It does not.
mnemo implements no OAuth authorization. There is no
/.well-known/oauth-protected-resource document, no WWW-Authenticate
challenge, no authorization-server discovery and no bearer-token audience
validation. Authentication is an HMAC capability presented per request
(ADR 0002), which is a different mechanism, not a partial implementation of this
one.
That is a conformant position rather than a gap, and the spec says so in the paragraph that governs the whole authorization document:
Authorization is OPTIONAL for MCP implementations. When supported: Implementations using an HTTP-based transport SHOULD conform to this specification. Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment.
mnemo’s default transport is stdio and it takes its capability key from the
environment (MNEMO_CAPABILITY_KEY), which is the behaviour that paragraph
prescribes.
| Requirement | What the spec requires | What mnemo does today | Status |
|---|---|---|---|
| RFC 9728 Protected Resource Metadata | “MCP servers MUST implement OAuth 2.0 Protected Resource Metadata.” The MUST is real, and conditional: it binds servers that support authorization, which is itself OPTIONAL, and stdio servers are told SHOULD NOT. | Not implemented. No /.well-known/oauth-protected-resource is served on any transport. A caller should assume discovery will 404 and must not infer from that that the server is unprotected; authenticate with an HMAC capability instead. | Not implemented, and conformant on stdio |
RFC 8707 Resource Indicators, resource parameter | “MCP clients MUST implement Resource Indicators … MUST be included in both authorization requests and token requests.” | Does not apply. This MUST binds clients, and mnemo ships an MCP server; the repo publishes no MCP client. | Not applicable |
| RFC 8707 audience validation | “MCP servers MUST validate that access tokens were issued specifically for them as the intended audience.” | Not implemented as written, because mnemo accepts no OAuth access tokens to validate. The equivalent property is enforced on its own mechanism: a capability verifies against the issuer key and its principal, and an unverifiable capability is rejected rather than downgraded. A caller should assume an OAuth bearer token will not be accepted. | Not implemented (no OAuth tokens accepted) |
RFC 9207 iss in authorization responses | Authorization servers SHOULD include iss; clients MUST validate a present iss. | Does not apply. mnemo is neither an authorization server nor a client. | Not applicable |
Why this is worth stating rather than leaving blank
Serving RFC 9728 discovery is close to unheard of in practice: the scanning work in this project’s sibling reports 0 of 2,303 public MCP server configurations serving it. That figure is reported rather than reproduced here, and it is context, not an excuse. The useful thing is not that mnemo is normal, it is that a reader can find out in one place instead of inferring from silence.
An operator putting mnemo behind an OAuth-protected gateway should terminate authorization at that gateway and pass a capability inward. mnemo will not participate in the OAuth flow, will not advertise an authorization server, and will not accept the gateway’s access token in place of a capability.
Deprecations
None of these require work: mnemo never adopted any of them.
| Deprecated feature | mnemo | Status |
|---|---|---|
| Roots (SEP-2577) | Never implemented. The spec’s suggested migration, passing paths as tool parameters, is what mnemo already does. | CONFORMS |
| Sampling (SEP-2577) | Never implemented. mnemo does not call an LLM; it stores and retrieves. | CONFORMS |
| Logging (SEP-2577) | Never implemented. mnemo logs through tracing to stderr, which is the spec’s suggested migration. | CONFORMS |
| HTTP+SSE transport (SEP-2596) | Never implemented. The optional http-transport feature is Streamable HTTP (ADR 0002). | CONFORMS |
The explicit-handle pattern, and the audit behind it
SEP-2567 states the replacement for sessions directly:
Servers that need cross-call state use explicit, server-minted handles passed as ordinary tool arguments.
mnemo already worked this way. Under 2025-11-25 that was one valid option
among several; under 2026-07-28 it is the sanctioned one. Being on the right
side of a change by accident is not the same as being on it on purpose, so the
property is now tested rather than assumed
(crates/mnemo-mcp/tests/explicit_handle_roundtrip.rs).
Two handles carry every piece of cross-call state mnemo has:
| Handle | Minted by | Consumed by | Passed as |
|---|---|---|---|
checkpoint_id | mnemo.checkpoint | mnemo.branch, mnemo.replay | an ordinary tool argument |
lease_token | mnemo.recall (ADR 0001) | mnemo.forget_subject | an ordinary tool argument |
The LeaseStore is server-side state, but it is not a session: it is the
bookkeeping behind a server-minted handle that the caller threads back through
a tool argument, which is precisely the shape the SEP prescribes. A lease is
bound to the principal that minted it and expires, so it is narrower than a
session, not a rebranding of one.
Audit: what depends on identity that is not an argument
Every tool in crates/mnemo-mcp/src/tools/ was checked for behaviour that
depends on connection-scoped identity rather than on a value in the request.
- 19 of 23 tools take
Parameters<T>and nothing else. Their signatures admit no context at all, so connection-scoped state is not merely unused, it is unrepresentable. - 4 tools take
Extension<CallerContext>:mnemo.recall,mnemo.forget_subject,mnemo.delegate,mnemo.trajectory_audit. This is not connection state. TheCallerContextis resolved per request byidentity.rsfrom that request’s own_meta(ADR 0002), andcall_toolinserts it immediately before dispatch. Two calls on one connection carrying two different capabilities produce two different callers._metaremains a per-request carrier under2026-07-28, which puts protocol version and client capabilities there too, so this shape survives the revision unchanged.
The one deliberate deviation
When a request presents no capability at all, resolve_caller falls back to
a boot-derived identity: the agent_id fixed when the server process started.
That value is process-scoped, so this is a genuine departure from
“everything arrives in the request”, and it is kept on purpose.
The reason is that on stdio one process is one peer, and the operator who started the server genuinely is the caller. Requiring a capability there would break every existing stdio deployment on upgrade to buy no security, because there is no second caller to distinguish from.
The deviation is bounded rather than open-ended:
- It applies only where no capability was presented. A capability that is present but unverifiable is an error, never a downgrade to the boot identity, since silently downgrading would hand a forged token the operator’s authority.
- On the network-facing
http-transport, the fallback does not apply at all: an unauthenticated request getsAnonymousOnAuthenticatedTransport, because there the fallback would let anyone who can reach the port act as the operator.
See crates/mnemo-mcp/src/identity.rs for the full resolution table.
The defect this page found
Writing the table surfaced one live defect rather than only recording known ones.
mnemo did not override supported_protocol_versions(), so it took rmcp’s
default of ProtocolVersion::KNOWN_VERSIONS. rmcp derives server/discover
from that list. The result, confirmed against a running server:
advertised protocol_version = 2025-11-25
supported_protocol_versions = [2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25, 2026-07-28]
mnemo was telling any discovering client that it speaks 2026-07-28 while
answering initialize with 2025-11-25 and implementing none of the newer
revision’s server-side requirements. A client entitled to believe that
advertisement would have sent 2026-07-28 requests to a server that still
expects the handshake that revision removes, and would have received list
results with neither ttlMs nor cacheScope.
That is a machine-readable claim that was not true - the same
claimed-but-not-wired shape this repo has repaired before (role_filter #124,
tool-catalog attestation v0.5.20, LeaseStore under ADR 0001).
The fix is to narrow the list to what mnemo serves. This is rmcp’s own supported
mechanism rather than a workaround: its negotiate_protocol_version documents
that “a server that narrows that list is never made to answer initialize with
a version it cannot serve”, and a client asking for an unlisted revision
negotiates down to the server fallback instead of failing. No client breaks.
What is still open, and what would close it
Four rows are not CONFORMS. None of the four is closable by mnemo alone today, which is the reason each carries a caller-assumption sentence in the table above rather than a promise.
| Open row | Status | What closes it |
|---|---|---|
| Stateless lifecycle | UPSTREAM-BLOCKED | rmcp moving ProtocolVersion::LATEST to V_2026_07_28 |
SEP-2243 Mcp-Method / Mcp-Name | UPSTREAM-BLOCKED | the same move; rmcp gates the headers on that revision |
| SSE resumability removal | UPSTREAM-BLOCKED | the same move; the behaviour is rmcp’s transport, not mnemo’s |
Resource-not-found -32002 | GAP, deliberate | adopting 2026-07-28, not an independent change |
All four collapse into one event: rmcp flips LATEST, mnemo adds V_2026_07_28
to supported_protocol_versions(), and this page is rewritten rather than
amended. The error-code row is listed as a GAP rather than UPSTREAM-BLOCKED
because mnemo could emit -32602 today; it does not, because -32002 is what
the revision it actually speaks specifies, and changing it early would trade real
conformance for imaginary conformance.
Separately, the authorization rows are not on this list. They are not open work: mnemo implements no OAuth and, on stdio, the spec says it should not. See the section above for what a caller should assume instead.
The two rows that were mnemo’s to close, ttlMs and cacheScope, are closed
as of 0.5.26.
Deployment
Mnemo can be deployed in several configurations:
| Mode | Backend | Best For |
|---|---|---|
| Embedded | DuckDB | Single-agent, local development |
| Distributed | PostgreSQL | Multi-agent, production |
| Docker | Either | Container deployments |
| Kubernetes | PostgreSQL | Scalable production |
Environment Variables
| Variable | Description | Default |
|---|---|---|
MNEMO_DB_PATH | DuckDB database path | mnemo.db |
MNEMO_POSTGRES_URL | PostgreSQL connection URL | - |
MNEMO_REST_PORT | REST API port | - |
MNEMO_AGENT_ID | Default agent ID | default |
MNEMO_ORG_ID | Organization ID | - |
OPENAI_API_KEY | OpenAI API key for embeddings | - |
MNEMO_EMBEDDING_MODEL | Embedding model name | text-embedding-3-small |
MNEMO_DIMENSIONS | Embedding dimensions | 1536 |
Docker Deployment
Quick Start
docker run -d \
--name mnemo \
-v mnemo-data:/data \
-e MNEMO_DB_PATH=/data/mnemo.db \
-e OPENAI_API_KEY=sk-... \
ghcr.io/mnemo-ai/mnemo:latest
Docker Compose
docker-compose up -d
The included docker-compose.yml starts Mnemo with PostgreSQL:
services:
mnemo:
build: .
environment:
MNEMO_POSTGRES_URL: postgres://mnemo:${POSTGRES_PASSWORD}@postgres/mnemo
OPENAI_API_KEY: ${OPENAI_API_KEY}
MNEMO_REST_PORT: "8080"
ports:
- "8080:8080"
depends_on:
- postgres
postgres:
image: pgvector/pgvector:pg16
environment:
POSTGRES_USER: mnemo
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set a strong POSTGRES_PASSWORD}
POSTGRES_DB: mnemo
volumes:
- pg-data:/var/lib/postgresql/data
Building the Image
docker build -t mnemo .
The Dockerfile uses a multi-stage build for minimal image size.
Kubernetes Deployment
Helm Chart
Install Mnemo on Kubernetes using the Helm chart:
helm install mnemo deploy/helm/mnemo/ \
--set postgres.url="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/mnemo" \
--set openaiApiKey="${OPENAI_API_KEY}"
Configuration
Key values.yaml options:
| Value | Default | Description |
|---|---|---|
replicaCount | 1 | Number of replicas |
image.repository | ghcr.io/mnemo-ai/mnemo | Container image |
image.tag | latest | Image tag |
postgres.url | - | PostgreSQL connection URL |
openaiApiKey | - | OpenAI API key |
rest.enabled | true | Enable REST API |
rest.port | 8080 | REST API port |
resources.requests.cpu | 100m | CPU request |
resources.requests.memory | 128Mi | Memory request |
ingress.enabled | false | Enable ingress |
Scaling
For production with PostgreSQL:
helm upgrade mnemo deploy/helm/mnemo/ --set replicaCount=3
Multiple replicas share the same PostgreSQL database, each handling MCP or REST connections independently.
PostgreSQL Mode
PostgreSQL mode enables distributed, multi-instance Mnemo deployments with pgvector for vector search.
Setup
1. PostgreSQL with pgvector
docker run -d \
--name mnemo-pg \
-e POSTGRES_USER=mnemo \
-e POSTGRES_PASSWORD=changeme_use_strong_password \
-e POSTGRES_DB=mnemo \
-p 5432:5432 \
pgvector/pgvector:pg16
2. Start Mnemo with PostgreSQL
mnemo --postgres-url "postgres://mnemo:$POSTGRES_PASSWORD@localhost/mnemo"
Or build with the postgres feature:
cargo build --release --features postgres
Schema
Mnemo automatically creates all required tables on first connection:
memorieswith pgvectorvector(N)column and HNSW indexacls,delegations,relations,agent_eventscheckpoints,agent_profiles
Differences from DuckDB Mode
| Feature | DuckDB | PostgreSQL |
|---|---|---|
| Vector index | USearch (HNSW) | pgvector (HNSW) |
| Full-text | Tantivy | PostgreSQL FTS (planned) |
| Concurrency | Single-writer | Multi-writer |
| Persistence | File-based | Server-based |
| Scaling | Single instance | Multiple instances |
Security
Encryption
Mnemo supports AES-256-GCM at-rest encryption for memory content. Enable it by setting:
export MNEMO_ENCRYPTION_KEY=$(openssl rand -hex 32)
mnemo --encryption-key "$MNEMO_ENCRYPTION_KEY" --db-path my.db
Content is encrypted before storage and decrypted on recall. The encryption key must be 64 hex characters (32 bytes).
Access Control Model
Mnemo implements a three-tier access control model:
1. Owner Access
The agent that created a memory has full access (read, write, delete, share, delegate).
2. ACL-Based Sharing
Explicit access grants via the share tool. Each ACL entry specifies:
- Target agent ID
- Permission level (read, write, delete, share, delegate)
- Optional expiration time
3. Delegation
Agents can delegate their permissions to others with:
- Scoping (all memories, by ID, or by tag)
- Maximum transitive depth
- Time bounds
- Automatic revocation on expiry
The REST /v1/delegate endpoint verifies the caller has Delegate permission on each target memory before creating the delegation.
Hash Chain Integrity
Every memory record includes a SHA-256 hash chain:
content_hash = SHA256(content + agent_id + timestamp)prev_hashlinks to the previous record’s hash viaSHA256(content_hash + prev_content_hash)- The
verifytool checks the entire chain for tampering - Hash comparisons use constant-time operations (
subtle::ConstantTimeEq) to prevent timing side-channels
Memory Poisoning Detection
Mnemo monitors agent behavior profiles and flags anomalous memory creation:
- Rapid creation rate (burst detection)
- Content length deviation from agent baseline
- Importance score anomalies
- Prompt injection patterns — 11 common patterns detected (e.g. “ignore all previous instructions”, “override system prompt”)
Flagged memories are quarantined and excluded from recall results. The anomaly score threshold is 0.5; prompt injection detection alone scores +0.5.
Input Validation
- agent_id: validated for length (max 256 characters) and allowed characters (alphanumeric, hyphens, underscores, dots)
- content: must be non-empty
- importance: must be between 0.0 and 1.0
REST API Security
- CORS: configurable origin allowlist via
MNEMO_CORS_ORIGINSenvironment variable. Defaults to localhost only (localhost:3000,localhost:8080). Set to*to allow all origins. - Body limits: 2 MB maximum request body size to prevent denial-of-service
- Error handling: internal errors are logged server-side; clients receive generic “internal server error” messages
pgwire Security
- Authentication: optional cleartext password authentication (configure via
PgWireConfig.password) - Binding: defaults to
127.0.0.1:5433(localhost only) - For production, deploy behind a TLS-terminating proxy
Environment Variables
| Variable | Description |
|---|---|
MNEMO_ENCRYPTION_KEY | AES-256-GCM key (64 hex chars) |
MNEMO_CORS_ORIGINS | Comma-separated allowed origins, or * |
OPENAI_API_KEY | OpenAI API key for embeddings |
Best Practices
- Always set secrets via environment variables, not CLI args
- Use time-bounded delegations with minimum required permissions
- Regularly run
verifyto check hash chain integrity - Monitor quarantine events for potential poisoning attempts
- Use PostgreSQL mode with TLS for production deployments
- Enable encryption for sensitive data with
MNEMO_ENCRYPTION_KEY - Configure
MNEMO_CORS_ORIGINSexplicitly in production
Compliance Overview
Mnemo is an MCP-native memory database for AI agents. This section documents how Mnemo’s architecture and feature set align with industry compliance frameworks, specifically SOC 2 Type II and HIPAA. The goal is to provide operators, auditors, and prospective customers with a clear mapping between regulatory requirements and the technical controls that Mnemo implements.
Compliance Posture Summary
Mnemo was designed with security and auditability as first-class concerns. The following capabilities form the foundation of its compliance story:
| Capability | Module | Description |
|---|---|---|
| Encryption at rest | encryption.rs | AES-256-GCM content encryption with HMAC-based integrity tags |
| Hash chain verification | hash.rs | SHA-256 content hashes linked into a tamper-evident chain |
| Role-based access control | acl.rs | Six-level permission hierarchy (Read through Admin) |
| Delegation model | delegation.rs | Transitive, scoped, time-bounded permission delegation |
| Memory poisoning detection | poisoning.rs | Anomaly scoring against agent behavioral baselines |
| Immutable audit log | event.rs | Append-only AgentEvent log with OpenTelemetry fields |
| TTL enforcement | MemoryRecord.expires_at | Automatic expiration filtering during recall |
| Quarantine | MemoryRecord.quarantined | Flagged memories excluded from recall results |
| Checkpoint/Branch/Merge | checkpoint.rs | Git-like state management with full version history |
| Cognitive forgetting | lifecycle.rs | Ebbinghaus-inspired decay with configurable functions |
Documents in This Section
-
SOC 2 Controls – Maps each SOC 2 Trust Service Criteria category (CC1 through CC9) to specific Mnemo features, implementation modules, and current status.
-
HIPAA Safeguards – Maps HIPAA Administrative, Physical, and Technical Safeguards to Mnemo capabilities, identifies gaps, and provides recommendations for covered-entity deployments.
How to Use This Documentation
For auditors: Each control mapping includes the control identifier, a description of the requirement, the specific Mnemo module or feature that addresses it, the current implementation status, and any known gaps with recommended mitigations.
For operators: Use this documentation to understand which compliance controls Mnemo provides out of the box and which require additional operational procedures, infrastructure configuration, or third-party tooling.
For developers: The module references in each control mapping point directly
to source files in crates/mnemo-core/src/. Consult those files for
implementation details when extending or auditing controls.
Implementation Status Legend
Throughout the compliance documents, the following status labels are used:
| Status | Meaning |
|---|---|
| Implemented | The control is fully implemented in code and tested |
| Partially Implemented | Core functionality exists but additional work is needed for full coverage |
| Planned | The control is on the roadmap but not yet implemented |
| Operational | The control depends on deployment-time configuration or external processes, not application code |
Versioning
This compliance documentation reflects the current compliance posture of Mnemo (23 registered MCP tools; see the tools reference). It should be updated whenever security-relevant features are added or modified.
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2026-02-07 | Initial compliance documentation covering SOC 2 and HIPAA |
SOC 2 Trust Service Criteria – Control Mapping
This document maps the AICPA SOC 2 Trust Service Criteria (2017 revision) to Mnemo’s security architecture. Each section covers one Common Criteria (CC) category, lists the relevant controls, describes how Mnemo addresses them, and identifies any gaps or recommendations.
For background on Mnemo’s security features, see the Security page and the Compliance Overview.
CC1 – Control Environment
The control environment sets the tone for the organization, influencing the control consciousness of its people. It is the foundation for all other components of internal control.
CC1.1 – Commitment to Integrity and Ethical Values
| Field | Detail |
|---|---|
| Control ID | CC1.1 |
| Description | The entity demonstrates a commitment to integrity and ethical values. |
| Mnemo Implementation | Mnemo is an open-source project with public code review. All contributions go through pull request review before merge. The project enforces Rust compiler warnings as errors (#[deny(warnings)]) and maintains a comprehensive test suite (67 tests across unit, integration, and MCP layers). |
| Status | Operational |
| Gaps / Recommendations | Formalize a written code of conduct and contributor ethics policy. Document the review and approval process for security-sensitive changes. |
CC1.2 – Board Oversight
| Field | Detail |
|---|---|
| Control ID | CC1.2 |
| Description | The board of directors demonstrates independence from management and exercises oversight. |
| Mnemo Implementation | As a software component rather than a service organization, Mnemo defers board-level governance to the deploying organization. The project provides tools (audit logs, hash chain verification) that enable oversight. |
| Status | Operational |
| Gaps / Recommendations | Deploying organizations should establish governance committees with visibility into Mnemo audit logs and verification reports. |
CC1.3 – Management Structure and Authority
| Field | Detail |
|---|---|
| Control ID | CC1.3 |
| Description | Management establishes structures, reporting lines, and appropriate authorities and responsibilities. |
| Mnemo Implementation | Mnemo’s RBAC model (crates/mnemo-core/src/model/acl.rs) implements a six-level permission hierarchy: Read, Write, Delete, Share, Delegate, Admin. Each permission level satisfies all lower levels. Principal types include Agent, User, Org, Role, and Public. The delegation model (crates/mnemo-core/src/model/delegation.rs) enforces maximum transitive depth and scoped authority. |
| Status | Implemented |
| Gaps / Recommendations | None. The hierarchical permission model maps well to organizational authority structures. |
CC1.4 – Competence Commitment
| Field | Detail |
|---|---|
| Control ID | CC1.4 |
| Description | The entity demonstrates a commitment to attract, develop, and retain competent individuals. |
| Mnemo Implementation | Mnemo is written in Rust, which enforces memory safety at compile time. The project uses type-safe error handling (crate::error::Error), preventing entire categories of runtime bugs. CI/CD pipelines run the full test suite on every commit. |
| Status | Operational |
| Gaps / Recommendations | Document onboarding procedures for new contributors, including security review training. |
CC1.5 – Accountability
| Field | Detail |
|---|---|
| Control ID | CC1.5 |
| Description | The entity holds individuals accountable for their internal control responsibilities. |
| Mnemo Implementation | Every memory operation is attributed to an agent_id. The AgentEvent log (crates/mnemo-core/src/model/event.rs) records the agent, thread, timestamp, and event type for every action. The created_by field on MemoryRecord tracks who created each memory. Delegation records track both delegator_id and delegate_id. |
| Status | Implemented |
| Gaps / Recommendations | None. Attribution is comprehensive across all data operations. |
CC2 – Communication and Information
The entity uses relevant, quality information to support the functioning of internal control and communicates information internally and externally.
CC2.1 – Information Quality
| Field | Detail |
|---|---|
| Control ID | CC2.1 |
| Description | The entity obtains or generates and uses relevant, quality information to support the functioning of internal control. |
| Mnemo Implementation | The AgentEvent model captures 15 distinct event types covering all data lifecycle operations: MemoryWrite, MemoryRead, MemoryDelete, MemoryShare, Checkpoint, Branch, Merge, UserMessage, AssistantMessage, ToolCall, ToolResult, Error, RetrievalQuery, RetrievalResult, Decision. Each event includes OpenTelemetry fields (trace_id, span_id, model, tokens_input, tokens_output, latency_ms, cost_usd) for observability. Events are hash-chained (content_hash, prev_hash) for integrity. |
| Status | Implemented |
| Gaps / Recommendations | Consider adding structured log export (e.g., to SIEM systems) for centralized monitoring. |
CC2.2 – Internal Communication
| Field | Detail |
|---|---|
| Control ID | CC2.2 |
| Description | The entity internally communicates information necessary to support the functioning of internal control. |
| Mnemo Implementation | The event log is queryable via list_events(), get_events_by_thread(), and list_child_events() on the StorageBackend trait. The mnemo.verify MCP tool allows any authorized agent to verify hash chain integrity and report anomalies. Memory poisoning detection results include detailed reasons arrays explaining each anomaly factor. |
| Status | Implemented |
| Gaps / Recommendations | Add webhook or notification support for critical events (quarantine triggers, chain verification failures). |
CC2.3 – External Communication
| Field | Detail |
|---|---|
| Control ID | CC2.3 |
| Description | The entity communicates with external parties regarding matters affecting the functioning of internal control. |
| Mnemo Implementation | Mnemo provides a REST API and MCP protocol interface for external integration. Audit events can be retrieved programmatically. The Python SDK, TypeScript SDK, and Go SDK enable external systems to consume compliance-relevant data. |
| Status | Partially Implemented |
| Gaps / Recommendations | Implement dedicated compliance reporting endpoints that export audit data in standard formats (e.g., CEF, OCSF). Add support for external audit log forwarding. |
CC3 – Risk Assessment
The entity identifies and assesses risks to the achievement of its objectives, including risks related to fraud.
CC3.1 – Objective Specification
| Field | Detail |
|---|---|
| Control ID | CC3.1 |
| Description | The entity specifies objectives with sufficient clarity to enable the identification and assessment of risks. |
| Mnemo Implementation | Mnemo defines clear security objectives through its data model: memory confidentiality (encryption, scoping), integrity (hash chains, content hashes), availability (TTL management, checkpoint/restore). Each memory has explicit scope (Private, Shared, Public, Global) and importance scoring. |
| Status | Implemented |
| Gaps / Recommendations | None. |
CC3.2 – Risk Identification and Analysis
| Field | Detail |
|---|---|
| Control ID | CC3.2 |
| Description | The entity identifies risks to the achievement of its objectives and analyzes risks as a basis for determining how the risks should be managed. |
| Mnemo Implementation | The memory poisoning detection system (crates/mnemo-core/src/query/poisoning.rs) implements multi-factor anomaly scoring. Three risk indicators are evaluated for every memory write: (1) importance deviation from agent baseline (>0.4 deviation = +0.3 score), (2) content length deviation from agent average (>5x or <0.1x = +0.3 score), (3) high-frequency burst detection (rapid writes = +0.4 score). A composite score >= 0.5 triggers anomaly classification. Agent behavioral baselines are maintained in AgentProfile records (crates/mnemo-core/src/model/agent_profile.rs) with running averages of importance, content length, and total memory count. |
| Status | Implemented |
| Gaps / Recommendations | Consider adding configurable thresholds per agent or organization. Add support for custom anomaly detection rules. |
CC3.3 – Fraud Risk Assessment
| Field | Detail |
|---|---|
| Control ID | CC3.3 |
| Description | The entity considers the potential for fraud in assessing risks. |
| Mnemo Implementation | Memory poisoning detection directly addresses the risk of agents injecting malicious or misleading memories. The quarantine mechanism (MemoryRecord.quarantined, MemoryRecord.quarantine_reason) isolates suspicious memories from recall results. The hash chain prevents retrospective tampering with the historical record. The delegation model prevents privilege escalation through max_depth limits and time bounds on delegated permissions. |
| Status | Implemented |
| Gaps / Recommendations | Add alerting on repeated quarantine events from a single agent (potential coordinated attack). Consider implementing agent reputation scoring. |
CC3.4 – Change-Related Risk Assessment
| Field | Detail |
|---|---|
| Control ID | CC3.4 |
| Description | The entity identifies and assesses changes that could significantly impact the system of internal controls. |
| Mnemo Implementation | The checkpoint/branch/merge system (crates/mnemo-core/src/model/checkpoint.rs) provides git-like versioning for agent state. Every checkpoint captures a state_snapshot, optional state_diff, memory_refs, and event_cursor. The version and prev_version_id fields on MemoryRecord track all changes to individual memories. |
| Status | Implemented |
| Gaps / Recommendations | None. Change tracking is comprehensive. |
CC5 – Control Activities
The entity selects and develops control activities that contribute to the mitigation of risks to the achievement of objectives to acceptable levels.
CC5.1 – Selection of Control Activities
| Field | Detail |
|---|---|
| Control ID | CC5.1 |
| Description | The entity selects and develops control activities that contribute to the mitigation of risks. |
| Mnemo Implementation | Mnemo implements defense in depth through multiple layered controls: encryption at rest, hash chain integrity, RBAC with hierarchical permissions, ACL-based sharing, scoped delegation, anomaly detection, quarantine, and TTL-based expiration. |
| Status | Implemented |
| Gaps / Recommendations | None. Multiple overlapping controls provide robust risk mitigation. |
CC5.2 – Technology-Based Control Activities
| Field | Detail |
|---|---|
| Control ID | CC5.2 |
| Description | The entity selects and develops general control activities over technology. |
| Mnemo Implementation | Access control is enforced at the storage layer through the StorageBackend trait. Key methods include: check_permission(memory_id, principal_id, required_permission) for ACL enforcement, check_delegation(delegate_id, memory_id, required_permission) for delegation enforcement, and list_accessible_memory_ids(agent_id, limit) for permission-safe vector search. The permission hierarchy (Permission::satisfies()) ensures that higher-level permissions automatically grant lower-level access. |
| Status | Implemented |
| Gaps / Recommendations | None. |
CC5.3 – Deployment of Control Activities Through Policies
| Field | Detail |
|---|---|
| Control ID | CC5.3 |
| Description | The entity deploys control activities through policies that establish what is expected and in procedures that put policies into action. |
| Mnemo Implementation | Access policies are encoded in the data model: each Acl record specifies principal_type (Agent, Org, Public, User, Role), principal_id, permission level, granted_by, and optional expires_at. Delegation policies specify scope (AllMemories, ByTag, ByMemoryId), max_depth, current_depth, and expires_at. Memory scope (Private, Shared, Public, Global) sets the default visibility policy. |
| Status | Implemented |
| Gaps / Recommendations | Add organization-level default policies that apply to all agents within an org. |
CC6 – Logical and Physical Access Controls
The entity implements logical access security software, infrastructure, and architectures over protected information assets.
CC6.1 – Logical Access Security
| Field | Detail |
|---|---|
| Control ID | CC6.1 |
| Description | The entity implements logical access security over protected information assets. |
| Mnemo Implementation | Three-tier access control: (1) Owner access – the creating agent has full control. (2) ACL-based sharing – explicit grants with specified permission levels and optional expiration. (3) Delegation – transitive permission chains with depth limits and time bounds. All access checks are performed at the storage layer before data is returned. The list_accessible_memory_ids() method ensures that vector similarity search only returns memories the requesting agent is authorized to see. |
| Status | Implemented |
| Gaps / Recommendations | None. |
CC6.2 – Authentication and Authorization
| Field | Detail |
|---|---|
| Control ID | CC6.2 |
| Description | Prior to issuing system credentials and granting system access, the entity registers and authorizes new users. |
| Mnemo Implementation | Agent identity is established through the agent_id field present on all operations. In MCP mode, the agent identity is bound to the STDIO transport session. The permission system supports five principal types (Agent, User, Org, Role, Public) with hierarchical authorization. |
| Status | Partially Implemented |
| Gaps / Recommendations | Implement formal agent registration and credential management. Add support for authentication tokens or API keys. Consider integration with external identity providers (OIDC, SAML). |
CC6.3 – Data Encryption
| Field | Detail |
|---|---|
| Control ID | CC6.3 |
| Description | The entity protects data in transit and at rest using encryption. |
| Mnemo Implementation | At rest: The ContentEncryption module (crates/mnemo-core/src/encryption.rs) provides AES-256-based content encryption. Keys are 256-bit (32 bytes), loaded from the MNEMO_ENCRYPTION_KEY environment variable or provided directly as hex-encoded strings. Each encryption operation produces nonce || ciphertext || tag with a 12-byte nonce and 16-byte HMAC integrity tag. Decryption verifies the tag before returning plaintext, detecting any tampering. In transit: When deployed with PostgreSQL mode, TLS is recommended. The Docker deployment guide recommends reverse proxy with TLS termination. |
| Status | Partially Implemented |
| Gaps / Recommendations | Upgrade the encryption implementation from the current simplified XOR-based cipher to the aes-gcm crate for production-grade AES-256-GCM (the code contains a comment noting this: “In production, use aes-gcm crate”). Implement key rotation support. Add envelope encryption for per-record keys. Enforce TLS for all network transports. |
CC6.4 – Restriction of Physical Access
| Field | Detail |
|---|---|
| Control ID | CC6.4 |
| Description | The entity restricts physical access to facilities and protected information assets. |
| Mnemo Implementation | As a software component, Mnemo defers physical access controls to the deployment environment. The Docker deployment (Dockerfile, docker-compose.yml) uses a non-root container image based on debian:bookworm-slim. The data volume (/data) can be mounted with appropriate filesystem permissions. |
| Status | Operational |
| Gaps / Recommendations | Document recommended filesystem permissions for the data volume. Provide Kubernetes deployment guidance with pod security policies and network policies. |
CC6.5 – Disposal of Information Assets
| Field | Detail |
|---|---|
| Control ID | CC6.5 |
| Description | The entity disposes of protected information assets in a secure manner. |
| Mnemo Implementation | Mnemo implements both soft delete (soft_delete_memory) and hard delete (hard_delete_memory) operations. Soft delete sets deleted_at timestamp, preserving the record for audit purposes. Hard delete permanently removes the record from storage. The cleanup_expired() method removes memories past their TTL. Cognitive forgetting (lifecycle.rs) provides decay-based archival and forgetting with configurable thresholds. Consolidation states track the full lifecycle: Raw, Active, Pending, Consolidated, Archived, Forgotten. |
| Status | Implemented |
| Gaps / Recommendations | Add secure wipe (zeroing) for hard-deleted records to prevent forensic recovery. Document data retention policies and destruction schedules. |
CC6.6 – Protection Against External Threats
| Field | Detail |
|---|---|
| Control ID | CC6.6 |
| Description | The entity implements controls to prevent or detect and act upon the introduction of unauthorized or malicious software. |
| Mnemo Implementation | Memory poisoning detection (crates/mnemo-core/src/query/poisoning.rs) monitors all incoming memories against agent behavioral baselines. Anomalous memories are automatically quarantined. The hash chain prevents injection of fabricated historical records. Content hashing detects any post-insertion modification. Source type tracking (SourceType enum with 9 variants) identifies the provenance of each memory. |
| Status | Implemented |
| Gaps / Recommendations | Add content validation rules (e.g., maximum content length, prohibited patterns). Consider integrating with external threat intelligence feeds. |
CC7 – System Operations
The entity uses detection and monitoring procedures to identify changes to configurations and system components that may indicate an attack.
CC7.1 – Detection of System Changes
| Field | Detail |
|---|---|
| Control ID | CC7.1 |
| Description | The entity detects changes to system components and configurations. |
| Mnemo Implementation | The hash chain verification system (crates/mnemo-core/src/hash.rs) enables detection of any tampering with stored memories. verify_chain() iterates through all records, verifying both content hashes and chain linkage. The ChainVerificationResult reports: valid (boolean), total_records, verified_records, first_broken_at (UUID of first tampered record), and error_message. The mnemo.verify MCP tool exposes this capability to agents. The checkpoint system tracks state changes with state_diff fields. |
| Status | Implemented |
| Gaps / Recommendations | Add automated periodic verification (cron-based or event-triggered). Implement alerting on verification failures. |
CC7.2 – Monitoring for Anomalies
| Field | Detail |
|---|---|
| Control ID | CC7.2 |
| Description | The entity monitors system components and operations for anomalies indicative of malicious acts, natural disasters, or errors. |
| Mnemo Implementation | Anomaly detection runs on every memory write via check_for_anomaly(). Three indicators are scored: importance deviation (+0.3), content length deviation (+0.3), and burst frequency (+0.4). The AnomalyCheckResult struct provides is_anomalous, score, and detailed reasons. Agent profiles (AgentProfile) track running averages to establish baselines. The event log captures all operations with timestamps and OpenTelemetry correlation IDs for distributed tracing. |
| Status | Implemented |
| Gaps / Recommendations | Add configurable anomaly thresholds. Implement time-series anomaly detection for longer-term behavioral drift. Export metrics to Prometheus or similar monitoring systems. |
CC7.3 – Evaluation and Response
| Field | Detail |
|---|---|
| Control ID | CC7.3 |
| Description | The entity evaluates anomalies to determine whether they represent security events and responds accordingly. |
| Mnemo Implementation | When a memory scores >= 0.5 on the anomaly scale, it is automatically quarantined via quarantine_memory(). Quarantined memories have quarantined = true and quarantine_reason set with the specific anomaly details. Quarantined memories are excluded from recall results, preventing poisoned data from affecting agent behavior. The agent profile is updated after each write via update_agent_profile() to refine baselines. |
| Status | Implemented |
| Gaps / Recommendations | Add a quarantine review workflow allowing administrators to release or permanently delete quarantined memories. Implement escalation procedures for repeated anomalies from the same agent. |
CC7.4 – Incident Response
| Field | Detail |
|---|---|
| Control ID | CC7.4 |
| Description | The entity responds to identified security incidents. |
| Mnemo Implementation | The delegation revocation mechanism (revoke_delegation()) enables immediate access termination. The event log provides a complete forensic trail. Hash chain verification can identify the exact point of any data tampering. Checkpoint restore enables rollback to a known-good state. |
| Status | Partially Implemented |
| Gaps / Recommendations | Implement a formal incident response runbook. Add bulk quarantine and bulk revocation capabilities. Create forensic export tools for incident investigation. |
CC8 – Change Management
The entity authorizes, designs, develops, configures, documents, tests, approves, and implements changes to infrastructure and software.
CC8.1 – Change Authorization
| Field | Detail |
|---|---|
| Control ID | CC8.1 |
| Description | The entity authorizes, designs, develops, tests, and implements changes to meet its objectives. |
| Mnemo Implementation | The checkpoint/branch/merge system provides version control for agent state. Key features: checkpoint – captures a point-in-time snapshot with state_snapshot, state_diff, memory_refs, event_cursor, and optional label. branch – creates a named branch from a checkpoint (branch_name field, parent_id linking). merge – combines branch state back into the main line. replay – replays events from a checkpoint forward. Every MemoryRecord tracks version (incrementing integer) and prev_version_id (UUID linking to the prior version). The mnemo.checkpoint, mnemo.branch, mnemo.merge, and mnemo.replay MCP tools expose these capabilities. |
| Status | Implemented |
| Gaps / Recommendations | Add merge conflict detection and resolution strategies. Implement branch protection rules. |
CC8.2 – Testing of Changes
| Field | Detail |
|---|---|
| Control ID | CC8.2 |
| Description | The entity tests changes before implementation. |
| Mnemo Implementation | The project maintains 67 tests across three layers: 46 unit tests, 16 integration tests, and 5 MCP protocol tests. Criterion benchmarks (benches/engine_bench.rs) track performance regressions. The branching system allows agents to test changes on a branch before merging into the main line. |
| Status | Implemented |
| Gaps / Recommendations | Add security-specific test suites (fuzzing, property-based testing). Implement CI gates that block merges on test failures. |
CC8.3 – Change Documentation
| Field | Detail |
|---|---|
| Control ID | CC8.3 |
| Description | The entity documents changes to meet its objectives. |
| Mnemo Implementation | Every state change is documented through the event log (AgentEvent). The checkpoint system captures state_diff fields showing what changed between checkpoints. Memory versioning (version, prev_version_id) creates a complete change history for every record. The consolidation state machine (Raw -> Active -> Pending -> Consolidated -> Archived -> Forgotten) tracks lifecycle transitions. |
| Status | Implemented |
| Gaps / Recommendations | None. Change documentation is thorough and machine-readable. |
CC9 – Risk Mitigation
The entity identifies, selects, and develops risk mitigation activities.
CC9.1 – Risk Mitigation Selection
| Field | Detail |
|---|---|
| Control ID | CC9.1 |
| Description | The entity identifies, selects, and develops risk mitigation activities. |
| Mnemo Implementation | Mnemo provides a comprehensive set of risk mitigation controls: TTL enforcement – memories with expires_at are automatically excluded from recall results and cleaned up by cleanup_expired(). Quarantine – anomalous memories are isolated from the data pool. Cognitive forgetting – the Ebbinghaus-inspired decay model (lifecycle.rs) automatically reduces the importance of aging memories through configurable functions (Exponential, Linear, StepFunction, PowerLaw). run_decay_pass() archives or forgets memories below configurable thresholds. Delegation bounds – max_depth prevents infinite permission chains, expires_at ensures time-limited grants, DelegationScope restricts access to specific memories or tags. |
| Status | Implemented |
| Gaps / Recommendations | None. Multiple complementary mitigation strategies are available. |
CC9.2 – Vendor and Business Partner Risk
| Field | Detail |
|---|---|
| Control ID | CC9.2 |
| Description | The entity assesses and manages risks associated with vendors and business partners. |
| Mnemo Implementation | Mnemo tracks the source of every memory via SourceType (Agent, Human, System, UserInput, ToolOutput, ModelResponse, Retrieval, Consolidation, Import) and source_id. The created_by field identifies the creating entity. The poisoning detection system applies equally to memories from all sources, including external imports. |
| Status | Partially Implemented |
| Gaps / Recommendations | Add vendor/source trust levels with different anomaly thresholds. Implement source allowlisting for import operations. |
Summary Matrix
| CC Category | Status | Key Modules |
|---|---|---|
| CC1 – Control Environment | Implemented / Operational | acl.rs, delegation.rs, event.rs |
| CC2 – Communication and Information | Implemented | event.rs, hash.rs, StorageBackend |
| CC3 – Risk Assessment | Implemented | poisoning.rs, agent_profile.rs, checkpoint.rs |
| CC5 – Control Activities | Implemented | acl.rs, delegation.rs, StorageBackend |
| CC6 – Logical and Physical Access | Partially Implemented | encryption.rs, acl.rs, delegation.rs |
| CC7 – System Operations | Implemented | hash.rs, poisoning.rs, event.rs |
| CC8 – Change Management | Implemented | checkpoint.rs, event.rs, MemoryRecord versioning |
| CC9 – Risk Mitigation | Implemented | lifecycle.rs, poisoning.rs, delegation.rs |
Priority Gaps
The following items represent the highest-priority gaps for achieving full SOC 2 compliance. They are listed in recommended order of implementation:
-
Upgrade encryption to production-grade AES-256-GCM (CC6.3) – Replace the simplified XOR cipher with the
aes-gcmcrate. This is the most critical gap. -
Implement formal authentication (CC6.2) – Add agent registration, API key management, and external identity provider integration.
-
Add automated hash chain verification (CC7.1) – Schedule periodic verification runs with alerting on failures.
-
Implement incident response tooling (CC7.4) – Build forensic export, bulk quarantine, and bulk revocation capabilities.
-
Add compliance reporting endpoints (CC2.3) – Export audit data in standard formats for external consumption.
HIPAA Safeguards – Control Mapping
This document maps the HIPAA Security Rule safeguards (45 CFR Part 164, Subpart C) to Mnemo’s security architecture. It is intended for organizations that deploy Mnemo in environments where Protected Health Information (PHI) may be stored as agent memories.
HIPAA compliance is a shared responsibility between Mnemo (as the software component) and the deploying organization (as the covered entity or business associate). This document identifies which safeguards Mnemo addresses through its architecture and which require operational controls from the deploying organization.
For background on Mnemo’s security features, see the Security page and the Compliance Overview.
Administrative Safeguards (Section 164.308)
Administrative safeguards are administrative actions, policies, and procedures to manage the selection, development, implementation, and maintenance of security measures to protect ePHI.
164.308(a)(1) – Security Management Process
Requirement: Implement policies and procedures to prevent, detect, contain, and correct security violations.
(i) Risk Analysis (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(1)(ii)(A) |
| Requirement | Conduct an accurate and thorough assessment of the potential risks and vulnerabilities to the confidentiality, integrity, and availability of ePHI. |
| Mnemo Implementation | Mnemo provides built-in risk analysis capabilities through the memory poisoning detection system (crates/mnemo-core/src/query/poisoning.rs). The check_for_anomaly() function evaluates three risk vectors for every memory write: importance deviation from agent baseline, content length anomalies, and high-frequency burst detection. Agent behavioral profiles (AgentProfile) are maintained with running averages to establish baselines. The anomaly scoring system produces quantified risk assessments (AnomalyCheckResult with score and reasons). |
| Status | Partially Implemented |
| Gaps | Mnemo provides automated risk detection for data integrity threats but does not replace a comprehensive organizational risk analysis. Deploying organizations must conduct their own risk assessment covering infrastructure, personnel, and operational risks. |
(ii) Risk Management (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(1)(ii)(B) |
| Requirement | Implement security measures sufficient to reduce risks and vulnerabilities to a reasonable and appropriate level. |
| Mnemo Implementation | Mnemo implements multiple security measures: AES-256-based encryption at rest (encryption.rs), SHA-256 hash chain integrity verification (hash.rs), six-level RBAC (acl.rs), scoped delegation with depth limits (delegation.rs), automatic quarantine of anomalous memories, TTL enforcement for data retention, and cognitive forgetting for automatic data lifecycle management (lifecycle.rs). |
| Status | Implemented |
| Gaps | Encryption implementation should be upgraded to production-grade aes-gcm crate. See SOC 2 CC6.3 for details. |
(iii) Sanction Policy (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(1)(ii)(C) |
| Requirement | Apply appropriate sanctions against workforce members who fail to comply with security policies. |
| Mnemo Implementation | The delegation model supports revocation (revoke_delegation()) to immediately terminate an agent’s delegated access. Quarantine isolates suspect agent activity. The event log provides evidence for sanction decisions. |
| Status | Partially Implemented |
| Gaps | Sanction policies are organizational responsibilities. Mnemo provides the enforcement mechanisms but does not define the policies themselves. |
(iv) Information System Activity Review (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(1)(ii)(D) |
| Requirement | Implement procedures to regularly review records of information system activity, such as audit logs, access reports, and security incident tracking reports. |
| Mnemo Implementation | The AgentEvent log (crates/mnemo-core/src/model/event.rs) provides an immutable, hash-chained audit trail. It captures 15 event types covering all data operations. Events include OpenTelemetry fields for correlation. The StorageBackend trait provides query methods: list_events(agent_id, limit, offset), get_events_by_thread(thread_id, limit), list_child_events(parent_event_id, limit). The mnemo.verify MCP tool enables integrity verification of the event chain. |
| Status | Implemented |
| Gaps | Add scheduled activity review reports and dashboards. Implement automated alerting for suspicious activity patterns. |
164.308(a)(2) – Assigned Security Responsibility
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(2) |
| Requirement | Identify the security official responsible for developing and implementing security policies. |
| Mnemo Implementation | Mnemo’s permission model supports Admin-level principals who have full control over all operations. The PrincipalType::Role type enables mapping organizational security roles to Mnemo permissions. |
| Status | Operational |
| Gaps | This is an organizational requirement. Mnemo provides the RBAC infrastructure to support it. The deploying organization must designate a security official and map their role to Mnemo’s Admin permission. |
164.308(a)(3) – Workforce Security
Requirement: Implement policies and procedures to ensure that all members of the workforce have appropriate access to ePHI.
(i) Authorization and/or Supervision (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(3)(ii)(A) |
| Requirement | Implement procedures for the authorization and/or supervision of workforce members who work with ePHI. |
| Mnemo Implementation | The three-tier access control model (Owner, ACL, Delegation) ensures that agents only access memories they are authorized for. The list_accessible_memory_ids() method on StorageBackend enforces this during vector search. Every ACL entry records granted_by to track authorization chains. Delegation records track both delegator_id and delegate_id with max_depth and current_depth for oversight. |
| Status | Implemented |
| Gaps | None at the application level. |
(ii) Workforce Clearance Procedure (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(3)(ii)(B) |
| Requirement | Implement procedures to determine that the access of a workforce member to ePHI is appropriate. |
| Mnemo Implementation | The permission hierarchy (Permission::satisfies()) enforces that each agent has only the minimum required permission level. Delegation scope (DelegationScope::AllMemories, ByTag, ByMemoryId) restricts access to relevant data subsets. Time-bounded ACLs and delegations (expires_at) ensure access is reviewed and renewed. |
| Status | Implemented |
| Gaps | Add periodic access review reports listing all active permissions and delegations per agent. |
(iii) Termination Procedures (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(3)(ii)(C) |
| Requirement | Implement procedures for terminating access to ePHI when employment or access is no longer required. |
| Mnemo Implementation | Delegation revocation (revoke_delegation()) sets revoked_at timestamp to immediately terminate delegated access. ACL entries support expires_at for automatic expiration. Soft delete (soft_delete_memory()) preserves audit history while removing access to the content. |
| Status | Implemented |
| Gaps | Add a bulk access termination API that revokes all permissions for a given agent in a single operation. |
164.308(a)(4) – Information Access Management
Requirement: Implement policies and procedures for authorizing access to ePHI.
(i) Isolating Health Care Clearinghouse Functions (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(4)(ii)(A) |
| Requirement | If a health care clearinghouse is part of a larger organization, isolate its functions. |
| Mnemo Implementation | Memory scoping (Private, Shared, Public, Global) combined with org_id field enables organizational isolation. Multi-tenant deployments can use org_id to enforce data separation at the storage layer. |
| Status | Partially Implemented |
| Gaps | Implement strict tenant isolation enforcement at the database level. Add cross-org access prevention in all query paths. |
(ii) Access Authorization (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(4)(ii)(B) |
| Requirement | Implement policies and procedures for granting access to ePHI. |
| Mnemo Implementation | The mnemo.share MCP tool provides explicit access granting. The mnemo.delegate MCP tool enables controlled permission delegation. Both record the granting agent and support time bounds. |
| Status | Implemented |
| Gaps | None. |
(iii) Access Establishment and Modification (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(4)(ii)(C) |
| Requirement | Implement policies and procedures that establish, document, review, and modify access. |
| Mnemo Implementation | All access changes are logged as AgentEvent records (MemoryShare event type). ACL entries include created_at and expires_at for temporal tracking. Delegation records include creation time, expiration, and revocation timestamps. |
| Status | Implemented |
| Gaps | None. |
164.308(a)(5) – Security Awareness and Training
Requirement: Implement a security awareness and training program for all members of the workforce.
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(5) |
| Requirement | Security reminders, malicious software protection, log-in monitoring, password management. |
| Mnemo Implementation | Mnemo provides documentation on security best practices (see docs/src/security.md). The memory poisoning detection system protects against malicious data injection. The event log enables monitoring of all access attempts. |
| Status | Partially Implemented |
| Gaps | This is primarily an organizational requirement. Create deployment-specific security guides for teams handling PHI. Add security warning messages for operations involving high-sensitivity memories. |
164.308(a)(6) – Security Incident Procedures
Requirement: Implement policies and procedures to address security incidents.
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(6)(ii) |
| Requirement | Identify and respond to suspected or known security incidents; mitigate harmful effects; document incidents and outcomes. |
| Mnemo Implementation | Quarantine mechanism automatically responds to detected anomalies. Hash chain verification (mnemo.verify) identifies data tampering incidents. The event log provides a forensic trail for incident investigation. Checkpoint restore enables rollback to pre-incident state. Delegation revocation enables immediate access termination. |
| Status | Partially Implemented |
| Gaps | Implement a formal incident tracking system within Mnemo (incident records, severity levels, resolution status). Add automated incident notification capabilities. |
164.308(a)(7) – Contingency Plan
Requirement: Establish policies and procedures for responding to an emergency or other occurrence that damages systems containing ePHI.
(i) Data Backup Plan (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(7)(ii)(A) |
| Requirement | Establish and implement procedures to create and maintain retrievable exact copies of ePHI. |
| Mnemo Implementation | The checkpoint system (crates/mnemo-core/src/model/checkpoint.rs) creates point-in-time snapshots with state_snapshot, memory_refs, and event_cursor. Checkpoints include parent_id for history linking. The mnemo.checkpoint MCP tool enables programmatic backup creation. DuckDB storage supports file-level backups of the database file. |
| Status | Partially Implemented |
| Gaps | Implement automated scheduled backups. Add backup verification (restore testing). Implement offsite backup replication. |
(ii) Disaster Recovery Plan (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(7)(ii)(B) |
| Requirement | Establish procedures to restore any loss of data. |
| Mnemo Implementation | The mnemo.replay MCP tool replays events from a checkpoint to restore state. Branch and merge operations enable state recovery from alternative timelines. The checkpoint system captures sufficient state for full reconstruction. |
| Status | Partially Implemented |
| Gaps | Document formal disaster recovery procedures. Define Recovery Time Objective (RTO) and Recovery Point Objective (RPO). Implement automated recovery testing. |
(iii) Emergency Mode Operation Plan (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(7)(ii)(C) |
| Requirement | Establish procedures to enable continuation of critical business processes during an emergency. |
| Mnemo Implementation | Mnemo can operate with a local DuckDB file, enabling standalone operation without network dependencies. The NoopEmbedding provider allows operation without external API access. |
| Status | Partially Implemented |
| Gaps | Document emergency operating procedures. Define minimum viable configuration for emergency operation. |
164.308(a)(8) – Evaluation
| Field | Detail |
|---|---|
| HIPAA Reference | 164.308(a)(8) |
| Requirement | Perform periodic technical and nontechnical evaluation of security controls. |
| Mnemo Implementation | The mnemo.verify MCP tool enables on-demand integrity verification. Criterion benchmarks track performance characteristics. The test suite (67 tests) validates security controls. |
| Status | Partially Implemented |
| Gaps | Implement scheduled security evaluations. Add compliance assessment tooling. Create security metrics dashboards. |
Physical Safeguards (Section 164.310)
Physical safeguards are physical measures, policies, and procedures to protect electronic information systems and related buildings and equipment from natural and environmental hazards and unauthorized intrusion.
164.310(a)(1) – Facility Access Controls
| Field | Detail |
|---|---|
| HIPAA Reference | 164.310(a)(1) |
| Requirement | Implement policies and procedures to limit physical access to electronic information systems while ensuring that properly authorized access is allowed. |
| Mnemo Implementation | As a software component, Mnemo defers facility-level controls to the deployment environment. The Docker deployment (Dockerfile) uses a minimal debian:bookworm-slim base image, reducing the attack surface. The Kubernetes deployment guide provides pod security recommendations. |
| Status | Operational |
| Gaps | This is entirely an operational requirement. Document recommended deployment environments with facility access controls. |
164.310(b) – Workstation Use
| Field | Detail |
|---|---|
| HIPAA Reference | 164.310(b) |
| Requirement | Implement policies and procedures that specify the proper functions to be performed and the physical attributes of the surroundings of workstations that access ePHI. |
| Mnemo Implementation | Not directly applicable to Mnemo as a server-side component. The MCP STDIO transport binds sessions to individual agent processes. |
| Status | Operational |
| Gaps | Document workstation security requirements for operators who administer Mnemo deployments. |
164.310(c) – Workstation Security
| Field | Detail |
|---|---|
| HIPAA Reference | 164.310(c) |
| Requirement | Implement physical safeguards for all workstations that access ePHI. |
| Mnemo Implementation | Not directly applicable. See workstation use above. |
| Status | Operational |
| Gaps | Document workstation security requirements in the deployment guide. |
164.310(d)(1) – Device and Media Controls
Requirement: Implement policies and procedures that govern the receipt and removal of hardware and electronic media containing ePHI.
(i) Disposal (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.310(d)(2)(i) |
| Requirement | Implement policies for the final disposition of ePHI and/or the hardware or electronic media on which it is stored. |
| Mnemo Implementation | hard_delete_memory() permanently removes records from DuckDB storage. cleanup_expired() removes expired memories. Cognitive forgetting (run_decay_pass()) automatically transitions aging memories through the Archived and Forgotten states. Encrypted content requires the encryption key for meaningful access. |
| Status | Partially Implemented |
| Gaps | Implement secure wipe (zero-fill) for hard-deleted records. Add cryptographic erasure support (destroying the encryption key to render stored ciphertext unrecoverable). Document media disposal procedures. |
(ii) Media Re-use (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.310(d)(2)(ii) |
| Requirement | Implement procedures for removal of ePHI from electronic media before re-use. |
| Mnemo Implementation | DuckDB file storage can be wiped by deleting the database file. Encrypted content is not recoverable without the encryption key. |
| Status | Operational |
| Gaps | Document media re-use procedures. Implement database purge utilities. |
Technical Safeguards (Section 164.312)
Technical safeguards are the technology, and the policy and procedures for its use, that protect ePHI and control access to it.
164.312(a)(1) – Access Control
Requirement: Implement technical policies and procedures for electronic information systems that maintain ePHI to allow access only to those persons or software programs that have been granted access rights.
(i) Unique User Identification (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(a)(2)(i) |
| Requirement | Assign a unique name and/or number for identifying and tracking user identity. |
| Mnemo Implementation | Every agent is identified by a unique agent_id string. All operations (memory CRUD, events, delegations) are attributed to the performing agent. The PrincipalType enum supports five identity types: Agent, User, Org, Role, Public. Memory records track created_by for creator attribution. Event records include agent_id, thread_id, and run_id for operation attribution. |
| Status | Implemented |
| Gaps | None. Unique identification is comprehensive. |
(ii) Emergency Access Procedure (Required)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(a)(2)(ii) |
| Requirement | Establish procedures for obtaining necessary ePHI during an emergency. |
| Mnemo Implementation | Admin-level permissions provide unrestricted access. Mnemo can operate locally with DuckDB without network dependencies. Checkpoint restore enables recovery of specific state snapshots. |
| Status | Partially Implemented |
| Gaps | Document emergency access procedures. Implement break-glass access mechanism with enhanced audit logging. |
(iii) Automatic Logoff (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(a)(2)(iii) |
| Requirement | Implement electronic procedures that terminate an electronic session after a predetermined time of inactivity. |
| Mnemo Implementation | MCP STDIO sessions are bound to process lifetime. ACL entries and delegations support expires_at for time-based access termination. |
| Status | Partially Implemented |
| Gaps | Implement session timeout for REST API connections. Add configurable inactivity timeout for MCP sessions. |
(iv) Encryption and Decryption (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(a)(2)(iv) |
| Requirement | Implement a mechanism to encrypt and decrypt ePHI. |
| Mnemo Implementation | The ContentEncryption module (crates/mnemo-core/src/encryption.rs) provides encryption/decryption of memory content. Keys are 256-bit, loaded from environment variables. The encryption produces nonce || ciphertext || tag format with integrity verification on decryption. |
| Status | Partially Implemented |
| Gaps | Upgrade to production-grade AES-256-GCM using the aes-gcm crate (currently uses a simplified XOR cipher). Implement key rotation. Add per-field encryption for metadata. |
164.312(b) – Audit Controls
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(b) |
| Requirement | Implement hardware, software, and/or procedural mechanisms that record and examine activity in information systems that contain or use ePHI. |
| Mnemo Implementation | The AgentEvent system provides comprehensive audit logging. Every data access operation generates an event record with: unique id (UUID v7, time-ordered), agent_id, thread_id, run_id, event_type (15 types covering all operations), payload (JSON with operation details), timestamp, logical_clock (monotonic ordering), content_hash and prev_hash (hash chain integrity). OpenTelemetry fields (trace_id, span_id) enable correlation with external observability systems. Query methods support review by agent, thread, or event hierarchy. |
| Status | Implemented |
| Gaps | Add tamper-evident log export to external storage. Implement log retention policies. Add real-time audit stream for SIEM integration. |
164.312(c)(1) – Integrity
Requirement: Implement policies and procedures to protect ePHI from improper alteration or destruction.
(i) Mechanism to Authenticate ePHI (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(c)(2) |
| Requirement | Implement electronic mechanisms to corroborate that ePHI has not been altered or destroyed in an unauthorized manner. |
| Mnemo Implementation | The hash chain system (crates/mnemo-core/src/hash.rs) provides two levels of integrity verification: (1) Content hash – compute_content_hash(content, agent_id, timestamp) produces a SHA-256 hash of each memory’s content, agent, and timestamp. (2) Chain hash – compute_chain_hash(content_hash, prev_hash) links each record to its predecessor, creating a tamper-evident chain. verify_chain() validates both content hashes and chain linkage, reporting ChainVerificationResult with the exact record where tampering is detected. The encryption module adds a 16-byte HMAC tag to ciphertext, verified on decryption. Memory versioning (version, prev_version_id) tracks all modifications. |
| Status | Implemented |
| Gaps | Add automated periodic integrity verification. Consider adding digital signatures for non-repudiation. |
164.312(d) – Person or Entity Authentication
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(d) |
| Requirement | Implement procedures to verify that a person or entity seeking access to ePHI is the one claimed. |
| Mnemo Implementation | Agent identity is established through the agent_id field on all operations. The MCP STDIO transport binds sessions to OS-level processes. The permission system verifies that the requesting agent has appropriate authorization before returning data. |
| Status | Partially Implemented |
| Gaps | Implement cryptographic authentication (API keys, mTLS, JWT). Add support for multi-factor authentication for administrative operations. Integrate with external identity providers (OIDC, SAML, LDAP). |
164.312(e)(1) – Transmission Security
Requirement: Implement technical security measures to guard against unauthorized access to ePHI that is being transmitted over an electronic communications network.
(i) Integrity Controls (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(e)(2)(i) |
| Requirement | Implement security measures to ensure that electronically transmitted ePHI is not improperly modified without detection. |
| Mnemo Implementation | Content hashes travel with memory records, enabling integrity verification at the receiving end. The hash chain provides ordering integrity across sequences of records. |
| Status | Partially Implemented |
| Gaps | Implement message-level signatures for MCP protocol messages. Add integrity verification for REST API responses. |
(ii) Encryption (Addressable)
| Field | Detail |
|---|---|
| HIPAA Reference | 164.312(e)(2)(ii) |
| Requirement | Implement a mechanism to encrypt ePHI whenever deemed appropriate during transmission. |
| Mnemo Implementation | The MCP STDIO transport operates over local Unix pipes, which are not exposed to network transmission. For network deployments, the documentation recommends TLS. The PostgreSQL mode supports TLS connections. The Docker deployment guide recommends reverse proxy with TLS termination. |
| Status | Partially Implemented |
| Gaps | Enforce TLS for all network transports (reject non-TLS connections). Implement TLS certificate pinning for PostgreSQL connections. Add MCP-over-TLS support for remote agent connections. |
Summary Matrix
| Safeguard Category | Section | Status | Key Modules |
|---|---|---|---|
| Security Management | 164.308(a)(1) | Partially Implemented | poisoning.rs, encryption.rs, hash.rs, acl.rs |
| Assigned Security Responsibility | 164.308(a)(2) | Operational | acl.rs (Admin role) |
| Workforce Security | 164.308(a)(3) | Implemented | acl.rs, delegation.rs |
| Information Access Management | 164.308(a)(4) | Implemented | acl.rs, delegation.rs, MCP tools |
| Security Awareness | 164.308(a)(5) | Partially Implemented | poisoning.rs, documentation |
| Security Incident Procedures | 164.308(a)(6) | Partially Implemented | Quarantine, hash.rs, event.rs |
| Contingency Plan | 164.308(a)(7) | Partially Implemented | checkpoint.rs, MCP tools |
| Evaluation | 164.308(a)(8) | Partially Implemented | hash.rs, test suite |
| Facility Access | 164.310(a)(1) | Operational | Docker, Kubernetes |
| Workstation Use/Security | 164.310(b-c) | Operational | N/A |
| Device and Media Controls | 164.310(d)(1) | Partially Implemented | Delete operations, encryption.rs |
| Access Control | 164.312(a)(1) | Partially Implemented | acl.rs, delegation.rs, encryption.rs |
| Audit Controls | 164.312(b) | Implemented | event.rs |
| Integrity | 164.312(c)(1) | Implemented | hash.rs, encryption.rs |
| Authentication | 164.312(d) | Partially Implemented | agent_id, MCP session binding |
| Transmission Security | 164.312(e)(1) | Partially Implemented | TLS recommendations, hash.rs |
Priority Gaps for HIPAA Compliance
The following items represent the highest-priority gaps for organizations deploying Mnemo in HIPAA-regulated environments. They are listed in recommended order of implementation:
-
Upgrade encryption to production-grade AES-256-GCM (164.312(a)(2)(iv)) – Replace the simplified XOR cipher with the
aes-gcmcrate. This is the single most critical gap for HIPAA compliance. -
Implement cryptographic authentication (164.312(d)) – Add API key management, mTLS, or JWT-based authentication. Agent identity must be cryptographically verified, not just asserted.
-
Enforce TLS for all network transports (164.312(e)(2)(ii)) – Reject non-TLS connections in network deployment modes. Implement certificate validation.
-
Add key rotation and management (164.312(a)(2)(iv)) – Implement encryption key rotation without downtime. Add envelope encryption for per-record key management.
-
Implement automated backup and recovery (164.308(a)(7)) – Add scheduled checkpoint creation, backup verification, and documented recovery procedures with defined RTO/RPO.
-
Add session timeout (164.312(a)(2)(iii)) – Implement configurable inactivity timeout for REST API and MCP sessions.
-
Implement tenant isolation (164.308(a)(4)) – Enforce strict data separation by
org_idat the database query level to prevent cross-tenant data leakage. -
Implement break-glass access (164.312(a)(2)(ii)) – Add an emergency access mechanism with enhanced audit logging for HIPAA-mandated emergency access procedures.
Deployment Recommendations for Covered Entities
Organizations subject to HIPAA that deploy Mnemo should implement the following operational controls in addition to Mnemo’s built-in safeguards:
Infrastructure
- Deploy Mnemo behind a TLS-terminating reverse proxy (e.g., nginx, Envoy).
- Use PostgreSQL mode with TLS-encrypted connections for production.
- Store the encryption key (
MNEMO_ENCRYPTION_KEY) in a secrets manager (e.g., HashiCorp Vault, AWS Secrets Manager), not in environment files. - Run Mnemo containers with read-only root filesystems and non-root users.
- Implement network policies restricting Mnemo’s inbound and outbound traffic.
Operations
- Assign a security official responsible for Mnemo deployment and configuration.
- Conduct a risk assessment specific to your PHI data flows through Mnemo.
- Establish backup schedules using the checkpoint system with offsite replication.
- Document and test disaster recovery procedures quarterly.
- Implement log forwarding from Mnemo’s event log to your SIEM system.
- Schedule periodic hash chain verification using the
mnemo.verifytool.
Access Management
- Map organizational roles to Mnemo’s permission hierarchy.
- Use time-bounded delegations with the minimum required permission level.
- Review active ACLs and delegations quarterly.
- Implement agent deprovisioning procedures that revoke all permissions.
- Maintain an access authorization matrix mapping agents to data categories.
Incident Response
- Define incident severity levels for Mnemo security events.
- Establish escalation procedures for quarantine events and verification failures.
- Document breach notification procedures per HIPAA requirements (60-day notification timeline).
- Conduct tabletop exercises simulating data integrity incidents.
Training
- Train operators on Mnemo’s security features and compliance controls.
- Include Mnemo-specific content in HIPAA security awareness training.
- Document procedures for handling PHI within agent memory workflows.
DPDPA — Digital Personal Data Protection Act (India)
Enforceable from 13 November 2026, the DPDPA requires Indian data fiduciaries to consult a DPB-registered Consent Manager before processing personal data and to honour consent withdrawal as an erasure right.
Mnemo’s mnemo-compliance crate provides two primitives to help with
this; both are behind the compliance feature flag so v0.1.1 callers
stay compiling.
ConsentSource trait
crates/mnemo-compliance/src/consent.rs. A pluggable interface that
looks up the current consent state for a subject.
#![allow(unused)]
fn main() {
use mnemo_compliance::{ConsentSource, ConsentState, HttpConsentManager};
let cm = HttpConsentManager::new("https://consent.example.com/v1")
.with_bearer(std::env::var("CONSENT_TOKEN")?);
let state: ConsentState = cm.fetch_consent("user-42").await?;
if state.has_scope("remember") && state.is_active() {
// proceed with writing personal data
}
}
Available implementations
HttpConsentManager— generic HTTP binding. ExpectsGET {base_url}/consent/{subject_id}to return a body matching [ConsentState]. Optional bearer-token auth.StaticConsentSource— in-memory map, for tests and single-tenant self-hosting.
ConsentState shape
#![allow(unused)]
fn main() {
pub struct ConsentState {
pub subject_id: String,
pub scopes: Vec<String>, // granted purposes
pub expires_at: Option<String>, // optional wall-clock expiry
pub token_hash: String, // SHA-256 of the signed token
}
}
Missing scopes are treated as denied. Expired states are rejected by
is_active() and by HttpConsentManager::fetch_consent.
Integration point: write-path consent check
Operators should call fetch_consent before every engine.remember
that touches personal data, and map a missing scope to
ComplianceError::ConsentDenied { subject_id, scope }. A reference
middleware is sketched in the compliance feature’s docs but not yet
wired into the core remember pipeline — doing so is part of the
v0.3.2 roadmap (requires a PolicyHook surface on MnemoEngine).
Consent withdrawal
Wire the consent manager’s withdrawal webhook to
[engine.forget_subject(subject_id, ForgetStrategy::Redact)] (which
ships since v0.2.0). Redact preserves content_hash + prev_hash so
the audit trail stays verifiable even after the content is erased;
alternatively use HardDelete if you have no retention obligation.
Audit trail
Every forget_subject emits a MemoryRedact audit event with a
hash-chain link to the prior event. Combine with the
EU AI Act export surface for a single signed trail.
EU AI Act — audit log export
The EU AI Act (enforceable from 2 August 2026 for GPAI providers)
requires retention of event logs with integrity controls and a path to
export records for AI Office document requests. Mnemo’s
mnemo-compliance crate ships a matching surface.
export_audit_log
#![allow(unused)]
fn main() {
use mnemo_compliance::{
AuditFormat, AuditSigner, export_audit_log, verify_ndjson_signed,
};
let events = engine.storage.list_events("agent-id", 10_000, 0).await?;
let signer = AuditSigner::from_secret_bytes(&ed25519_secret);
// NDJSON with detached Ed25519 signature chain
let bundle = export_audit_log(
&events,
AuditFormat::NdjsonSigned,
Some(&signer),
)?;
std::fs::write("audit.ndjson", bundle.bytes)?;
// Reverse: verify
let verified = verify_ndjson_signed(
&std::fs::read("audit.ndjson")?,
bundle.verifying_key_hex.as_ref().unwrap(),
)?;
}
Supported formats
AuditFormat::NdjsonSigned— one JSON line per event plus a detached Ed25519 signature that coversSHA256(index ∥ prev_hash ∥ event_json). Canonicalises throughserde_json::Valueso the signer and verifier agree on bytes regardless of struct field ordering. Tampering breaks the chain at the first mutated byte andverify_ndjson_signedreturnsComplianceError::ChainBroken { index, reason }.AuditFormat::EuAiOfficeCsv— the columnar template the AI Office consumes for GPAI document requests. RFC4180-escaped; header row first. Columns:event_id, timestamp, agent_id, event_type, model, thread_id, tokens_input, tokens_output, content_hash.
Key management
AuditSigner never generates or stores keys on its own. Operators are
expected to keep the 32-byte Ed25519 secret behind an HSM or KMS and
pass it through AuditSigner::from_secret_bytes. generate_ephemeral
exists purely for tests.
Integration with forget_subject
When a DPDPA consent withdrawal triggers forget_subject, the emitted
MemoryRedact events land in the audit trail with a proper
prev_hash link. A later export_audit_log call signs the chain
end-to-end, giving regulators a single artefact that covers both the
original write and its regulated erasure.
What’s NOT in v0.3.1
- No encryption-key rotation story (deferred to v0.4.0).
- No streaming export from the REST / gRPC surfaces — today the export
function is synchronous against an in-memory event list. For very
long audit windows, callers should batch via
storage.list_events(agent_id, limit, offset)and feed slices intoexport_audit_log. - No retention-policy enforcement. The trail retains whatever the storage backend retains.
Performance
Benchmarks
Every published headline number — retrieval quality, poisoning resistance, audit tamper-evidence — with its exact reproduction command and raw-results file lives in one place: the benchmark index. Start there; each row also says what the number does not show.
Mnemo includes Criterion benchmarks in benches/engine_bench.rs:
cargo bench -p mnemo-core
Retrieval Strategies
| Strategy | Speed | Quality | Best For |
|---|---|---|---|
exact | Fastest | Filter-only | Known queries, tag-based |
bm25 | Fast | Good for keywords | Keyword search |
vector | Medium | Best semantic | Semantic similarity |
graph | Medium | Good for related | Connected memories |
hybrid | Slowest | Best overall | General use (default) |
Storage Backend Comparison
| Metric | DuckDB | PostgreSQL |
|---|---|---|
| Latency (single op) | ~1ms | ~5ms |
| Throughput | High (local) | High (concurrent) |
| Memory usage | Low | Medium |
| Setup | Zero-config | Requires server |
Optimization Tips
- Use noop embeddings during development (faster, no API calls)
- Set appropriate limits in recall to avoid over-fetching
- Use tags and filters to narrow search space before semantic search
- Use exact strategy when you know the filtering criteria
- Run decay passes periodically to clean up low-importance memories