Skip to content

quangdang46/mempalace_rust

Repository files navigation

MemPalace

MemPalace

The highest-scoring AI memory system ever benchmarked. Now in Rust.


Every conversation you have with an AI — every decision, every debugging session, every architecture debate — disappears when the session ends. Six months of work, gone. You start over every time.

Other memory systems let AI decide what's worth remembering. They extract "user prefers Postgres" and throw away the conversation where you explained why. MemPalace takes a different approach: store everything, then make it findable.

The Palace — Ancient Greek orators memorized entire speeches by placing ideas in rooms of an imaginary building. Walk through the building, find the idea. MemPalace applies the same principle to AI memory: your conversations are organized into wings (people and projects), halls (types of memory), rooms (specific topics), closets (AAAK-compressed summaries), and drawers (verbatim content). No AI decides what matters — you keep every word, and the structure makes it searchable. That structure alone improves retrieval by 34%.

AAAK — A lossy shorthand dialect designed for AI agents. Roughly 5–10× token reduction via lossy summarisation, optimised for LLM readability. The original prose can't be reconstructed from AAAK — the verbatim drawers stay the source of truth, AAAK is the compact index your agent reads first. Your AI loads months of context in ~120 tokens. And because AAAK is just structured text, it works with any model that reads text — Claude, GPT, Gemini, Llama, Mistral. No decoder, no fine-tuning, no cloud API required.

Local, open, adaptable — Zero external API calls by default. Everything runs on your machine. No subscription, no cloud dependency, no telemetry.



Quick Start · Install · The Palace · AAAK Dialect · Knowledge Graph · Search · Benchmarks · MCP Tools · All Commands · Configuration · Rust Enhancements


96.6%
LongMemEval R@5
Zero API calls
100%
LongMemEval R@5
with Claude Haiku rerank
+34%
Retrieval boost
from palace structure
$0
No subscription
Local only. Always.

Benchmark scores from the reference Python implementation on LongMemEval-S. Rust port targets parity.


Quick Start

# 1. Install
curl -fsSL https://raw.githubusercontent.com/quangdang46/mempalace_rust/main/install.sh | bash

# 2. One-command setup: init + mine a project
mpr init ~/projects/myapp --auto-mine
mpr mine ~/projects/myapp --mode convos          # conversation exports too

# 3. Search everything
mpr search "auth decision"

# 4. Your AI already has MemPalace (auto-MCP after install)
# Just ask it: "What did we decide about auth?"

The installer auto-detects Claude Code, Codex, Cursor, Windsurf, VS Code, Gemini, OpenCode, Amp, and Droid — no manual MCP configuration.


Install

Linux / macOS / WSL

curl -fsSL https://raw.githubusercontent.com/quangdang46/mempalace_rust/main/install.sh | bash

Single binary mpr installed to /usr/local/bin/. The installer auto-detects your AI tools and wires the MCP server — no manual config editing.

Windows (native PowerShell)

irm https://raw.githubusercontent.com/quangdang46/mempalace_rust/main/install.ps1 | iex

Installs to %USERPROFILE%\.mempalace\bin\mpr.exe and adds to PATH. Supports env overrides:

$env:MPR_VERSION = "v0.6.5"   # pin a specific version
$env:MPR_PREFIX  = "D:\tools" # custom install directory

Or download manually from releases: grab mpr-windows-x86_64.zip, extract, run mpr.exe.

Runs natively on Windows 10/11 x86_64. ARM requires WSL2 (no native ARM build yet).

From source

cargo install mempalace

Manual (from releases)

# Download the binary for your platform from the releases page
chmod +x mpr && sudo mv mpr /usr/local/bin/

System requirements

  • OS: Linux, macOS, Windows (via WSL2)
  • Arch: x86_64, aarch64 (ARM Mac, ARM Linux)
  • Disk: ~500 MB for embedding models (downloaded on first use)
  • RAM: ~2 GB for vector search indexes on large palaces

How It Works

The Palace

  ┌─────────────────────────────────────────────────────────────┐
  │  WING: Person                                              │
  │                                                            │
  │    ┌──────────┐  ──hall──  ┌──────────┐                    │
  │    │  Room A  │            │  Room B  │                    │
  │    └────┬─────┘            └──────────┘                    │
  │         │                                                  │
  │         ▼                                                  │
  │    ┌──────────┐      ┌──────────┐                          │
  │    │  Closet  │ ───▶ │  Drawer  │                          │
  │    └──────────┘      └──────────┘                          │
  └─────────┼──────────────────────────────────────────────────┘
            │
          tunnel  (connects wings)
            │
  ┌─────────┼──────────────────────────────────────────────────┐
  │  WING: Project                                             │
  │         │                                                  │
  │    ┌────┴─────┐  ──hall──  ┌──────────┐                    │
  │    │  Room A  │            │  Room C  │                    │
  │    └────┬─────┘            └──────────┘                    │
  │         │                                                  │
  │         ▼                                                  │
  │    ┌──────────┐      ┌──────────┐                          │
  │    │  Closet  │ ───▶ │  Drawer  │                          │
  │    └──────────┘      └──────────┘                          │
  └─────────────────────────────────────────────────────────────┘

Wings — people and projects. As many as you need. Each wing is a namespace for its content. Rooms — specific topics within a wing. Auth, billing, deploy, architecture — endless rooms. Halls — connections between related rooms within the same wing. Tunnels — connections between wings. When Person A and a Project both have a room about "auth," a tunnel cross-references them automatically. Closets — AAAK-compressed summaries that point to verbatim content. Fast for AI to read. Drawers — the original verbatim files. Never summarized, never paraphrased.

Halls are memory types — the same in every wing, acting as corridors:

  • hall_facts — decisions made, choices locked in
  • hall_events — sessions, milestones, debugging
  • hall_discoveries — breakthroughs, new insights
  • hall_preferences — habits, likes, opinions
  • hall_advice — recommendations and solutions

Rooms are named ideas — auth-migration, graphql-switch, ci-pipeline. When the same room appears in different wings, it creates a tunnel:

wing_kai       / hall_events / auth-migration  → "Kai debugged the OAuth token refresh"
wing_driftwood / hall_facts  / auth-migration  → "team decided to migrate auth to Clerk"
wing_priya     / hall_advice / auth-migration  → "Priya approved Clerk over Auth0"

Same room. Three wings. The tunnel connects them.

Why Structure Matters

Tested on 22,000+ real conversation memories:

Search all closets:          60.9%  R@10
Search within wing:          73.1%  (+12%)
Search wing + hall:          84.8%  (+24%)
Search wing + room:          94.8%  (+34%)

Wings and rooms aren't cosmetic. They're a 34% retrieval improvement. The palace structure is the product.

The Memory Stack

Layer What Size When
L0 Identity — who is this AI? ~50 tokens Always loaded
L1 Critical facts — team, projects, preferences ~120 tokens (AAAK) Always loaded
L2 Room recall — recent sessions, current project On demand When topic comes up
L3 Deep search — semantic query across all closets On demand When explicitly asked

Your AI wakes up with L0 + L1 (~170 tokens) and knows your world. Searches only fire when needed.

AAAK Compression

AAAK is a lossy shorthand dialect — ~5–10× token reduction via summarisation, readable by any LLM without a decoder. It works with Claude, GPT, Gemini, Llama, Mistral — any model that reads text. Run it against a local Llama model and your whole memory stack stays offline.

English (~1000 tokens):

Priya manages the Driftwood team: Kai (backend, 3 years), Soren (frontend),
Maya (infrastructure), and Leo (junior, started last month). They're building
a SaaS analytics platform. Current sprint: auth migration to Clerk.
Kai recommended Clerk over Auth0 based on pricing and DX.

AAAK (~120 tokens):

TEAM: PRI(lead) | KAI(backend,3yr) SOR(frontend) MAY(infra) LEO(junior,new)
PROJ: DRIFTWOOD(saas.analytics) | SPRINT: auth.migration→clerk
DECISION: KAI.rec:clerk>auth0(pricing+dx) | ★★★★

Same information. 8× fewer tokens. Your AI learns AAAK automatically from the MCP server — no manual setup.

The original prose can't be reconstructed from AAAK; the verbatim drawers remain the source of truth, AAAK is the compact index your agent reads first.

Contradiction Detection

MemPalace catches mistakes before they reach you:

Input:  "Soren finished the auth migration"
Output: 🔴 AUTH-MIGRATION: attribution conflict — Maya was assigned, not Soren

Input:  "Kai has been here 2 years"
Output: 🟡 KAI: wrong_tenure — records show 3 years (started 2023-04)

Facts checked against the knowledge graph. Ages, dates, and tenures calculated dynamically — not hardcoded.


Knowledge Graph

Temporal entity-relationship triples — like Zep's Graphiti, but SQLite instead of Neo4j. Local and free.

use mempalace::knowledge_graph::KnowledgeGraph;

let mut kg = KnowledgeGraph::open("~/.mempalace/knowledge.db")?;
kg.add_triple("Kai", "works_on", "Orion", valid_from="2025-06-01")?;
kg.add_triple("Maya", "assigned_to", "auth-migration", valid_from="2026-01-15")?;
kg.add_triple("Maya", "completed", "auth-migration", valid_from="2026-02-01")?;

// What's Kai working on?
kg.query_entity("Kai")?;
// → [Kai → works_on → Orion (current), Kai → recommended → Clerk (2026-01)]

// What was true in January?
kg.query_entity("Maya", as_of="2026-01-20")?;
// → [Maya → assigned_to → auth-migration (active)]

// Timeline
kg.timeline("Orion")?;
// → chronological story of the project

Facts have validity windows. When something stops being true, invalidate it:

kg.invalidate("Kai", "works_on", "Orion", ended="2026-03-01")?;

Now queries for Kai's current work won't return Orion. Historical queries still will.

Auto-resolving Conflicts

When a new fact contradicts an existing one, the knowledge graph automatically invalidates the old triple — no manual cleanup needed:

kg.add_triple("Alice", "works_at", "Acme Corp", valid_from="2024-01")?;
// months later...
kg.add_triple("Alice", "works_at", "NewCo", valid_from="2025-06")?;
// → "Acme Corp" triple auto-invalidated, timeline shows both

Episodic Memory

The palace learns what's useful over time. When a memory is retrieved and confirmed or denied, that signal is recorded:

retrieve("auth migration") → drawer #42
user says "yes, exactly"  → drawer #42 helpfulness +1
user says "no, wrong"     → drawer #42 helpfulness -1

Future retrievals blend semantic similarity with historical helpfulness — memories that consistently help rank higher, misleading ones fade.

Feature MemPalace Zep (Graphiti)
Storage SQLite (local) Neo4j (cloud)
Cost Free $25/mo+
Temporal validity Yes Yes
Auto-resolve conflicts Yes No
Episodic feedback Yes No
Privacy Everything local SOC 2, HIPAA

Search

Three retrieval streams fused by Reciprocal Rank Fusion (RRF):

Stream What it catches
BM25 Exact keyword matches with synonym expansion (SYNONYM_BM25_WEIGHT = 0.7)
Vector (384-d) Semantic similarity via FastEmbed (cosine, L2, or inner product)
Knowledge Graph Entity relationships, BFS traversal across wings/rooms

RRF fuses them: a hit in any 2 of 3 ranks higher than 3 hits in 1. The search pipeline:

mpr search "rust async"                          # hybrid BM25 + vector + KG
mpr search "rust async" --bm25                   # add BM25 rerank
mpr search "rust async" --wing driftwood         # scope to a wing
mpr search "rust async" --room auth-migration    # scope to a room

Metric-aware distance

The distance metric (cosine, L2, or inner product) is detected automatically from the vector index config and mapped to a correct [0, 1] similarity score:

Metric Conversion Range
Cosine 1 - distance [0, 2] → [0, 1]
L2 1 / (1 + distance) [0, ∞) → [0, 1]
Inner product -distance [-1, 1] → [0, 1]

Memory Storage Pipeline

                     ┌─────────────────────────────────────────────┐
                     │               SOURCE STREAMS               │
                     ├──────────┬──────────┬──────────┬───────────┤
                     │ mpr mine │ MCP tool │ hooks/*  │ REST API  │
                     └────┬─────┴────┬─────┴────┬─────┴─────┬─────┘
                          │          │          │           │
                          └──────────┴────┬─────┴───────────┘
                                          ▼
                     ┌────────────────────────────────────────┐
                     │          AAAK COMPRESSION              │
                     │  LLM → facts / narrative / concepts    │
                     └──────────────────┬─────────────────────┘
                                        ▼
                     ┌────────────────────────────────────────┐
                     │           STORAGE LAYERS               │
                     ├────────────────────────────────────────┤
                     │ SQLite (drawers) │ Vector (FastEmbed)  │
                     │ KG (triples)     │ BM25 (keyword idx)  │
                     └──────────────────┬─────────────────────┘
                                        ▼
                     ┌────────────────────────────────────────┐
                     │       RETRIEVAL (RRF fusion)           │
                     │  BM25 ⨯ Vector ⨯ KG → ranked results  │
                     └────────────────────────────────────────┘
Stage What happens Where
Source Code, MCP calls, agent hooks, REST writes mpr mine, mempalace_add_drawer, hooks
AAAK LLM extracts facts, narrative, concepts, importance compress.rs, compress_synthetic.rs
Storage 3 indexes — SQLite, vector, KG palace_db.rs, palace/store/, knowledge_graph.rs
Retrieval BM25 + vector + graph → RRF fusion palace_db.rs::hybrid_search, search/rrf.rs
Serve Top-K returned via MCP tool or REST mcp_server.rs, rest_api.rs

MCP Server

# Run as MCP stdio server for any MCP-compatible tool
mpr mcp

# Or configure for Claude Code:
claude mcp add mpr -- mpr mcp

Palace tools (read)

Tool What
mpr_status Palace overview + AAAK spec + memory protocol
mpr_list_wings Wings with drawer counts
mpr_list_rooms Rooms within a wing
mpr_get_taxonomy Full wing → room → count tree
mpr_search Semantic search with wing/room filters
mpr_check_duplicate Check before filing
mpr_traverse Walk the graph from a room across wings
mpr_find_tunnels Find rooms bridging two wings
mpr_graph_stats Graph connectivity overview

Palace tools (write)

Tool What
mpr_add_drawer File verbatim content
mpr_delete_drawer Remove by ID
mpr_mine Mine a directory into the palace

Knowledge Graph tools

Tool What
mpr_kg_query Entity relationships with time filtering
mpr_kg_add Add temporal facts
mpr_kg_invalidate Mark facts as ended
mpr_kg_timeline Chronological entity story
mpr_kg_stats Graph overview

Hallway tools

Tool What
mpr_list_hallways List inter-room connections
mpr_delete_hallway Remove a hallway

Diary tools

Tool What
mpr_diary_write Write AAAK diary entry
mpr_diary_read Read recent diary entries

The AI learns AAAK and the memory protocol automatically from mpr_status. No manual configuration.

Supported MCP Providers

install.sh auto-detects these:

Provider Config Path
Claude Code ~/.claude.json
Codex ~/.codex/config.toml
Cursor ~/.cursor/mcp.json
Windsurf ~/.codeium/windsurf/mcp_config.json
VS Code .vscode/mcp.json
Gemini ~/.gemini/settings.json
OpenCode ~/.opencode.json
Amp ~/.config/amp/settings.json
Droid ~/.factory/mcp.json

Auto-Save Hooks

Two hooks for Claude Code that automatically save memories during work:

Save Hook — every 15 messages, triggers a structured save. Topics, decisions, quotes, code changes. Also regenerates the critical facts layer.

PreCompact Hook — fires before context compression. Emergency save before the window shrinks.

{
  "hooks": {
    "Stop": [{"matcher": "", "hooks": [{"type": "command", "command": "mpr hook stop"}]}],
    "PreCompact": [{"matcher": "", "hooks": [{"type": "command", "command": "mpr hook precompact"}]}]
  }
}

Can be disabled via MEMPALACE_HOOKS_AUTO_SAVE=false.


Benchmarks

Benchmark Mode Score API Calls
LongMemEval R@5 Raw (vector DB only) 96.6% Zero
LongMemEval R@5 Hybrid + Haiku rerank 100% Optional
Palace structure impact Wing+room filtering +34% R@10 Zero

From the reference Python implementation on LongMemEval-S. Rust port targets parity.

vs Published Systems

System LongMemEval R@5 API Required Cost
MemPalace (hybrid) 100% Optional Free
MemPalace (raw) 96.6% None Free
Mastra 94.87% Yes (GPT) API costs
Mem0 ~85% Yes $19–249/mo
Zep ~85% Yes $25/mo+

All Commands

# Setup
mpr init <dir>                              # guided onboarding + AAAK bootstrap
mpr init <dir> --auto-mine                  # init + immediate mine

# Mining
mpr mine <dir>                              # mine project files
mpr mine <dir> --mode convos                # mine conversation exports
mpr mine <dir> --mode convos --wing myapp   # tag with a wing name
mpr mine <dir> --max-chunks-per-file 0      # disable per-file chunk cap
mpr mine-device                             # scan machine for all AI tool sessions

# Splitting
mpr split <dir>                             # split concatenated transcripts
mpr split <dir> --dry-run                   # preview without splitting

# Search
mpr search "query"                          # search everything
mpr search "query" --wing myapp             # within a wing
mpr search "query" --room auth-migration    # within a room
mpr search "query" --bm25                   # with BM25 rerank

# Context
mpr wake-up                                 # load L0 + L1 context (~170 tokens)
mpr wake-up --wing driftwood                # project-specific
mpr context                                 # full context build

# Compression
mpr compress --wing myapp                   # AAAK compress a wing
mpr consolidate                             # run LLM consolidation pipeline

# Health
mpr doctor                                  # palace health check (6 checks)
mpr status                                  # palace overview
mpr diagnose                                # deep diagnostics

# Knowledge graph
mpr kg stats                                # KG overview
mpr kg timeline "Kai"                       # entity timeline

# Actions & signals
mpr actions                                 # list coordination actions
mpr frontier                                # unblocked action frontier
mpr signals send --to agent --data "msg"    # send signal to agent
mpr signals list --agent agent              # read signals

# Profile
mpr profile                                 # compute palace profile

# Snapshot
mpr snapshot                                # snapshot palace state

# Export / Import
mpr export --format basic-memory            # export as markdown
mpr import --json file.json                 # import JSON data

# Visual
mpr vision                                  # vision search

# Repair
mpr repair scan                             # scan for corrupt IDs
mpr repair prune --confirm                  # delete corrupt IDs
mpr repair rebuild                          # rebuild palace index
mpr repair cleanup-pid                      # clean stale PID file

# Migrate
mpr migrate-wings --palace <path>           # normalize legacy wing names

# Forgetting
mpr forget --older-than 30                  # forget old memories

# Server mode
mpr mcp                                     # run as MCP stdio server
mpr serve                                   # run as HTTP + REST server
mpr serve --no-background                   # server without background tasks

# Evolution
mpr evolve                                  # evolve palace state

# Mesh
mpr mesh status                             # mesh peer status
mpr mesh connect --peer peer                # connect to peer

All commands accept --palace <path> to override the default palace location.


Configuration

Global config (~/.mempalace/config.json)

{
  "palace_path": "/custom/path/to/palace",
  "collection_name": "mpr_drawers",
  "people_map": {"Kai": "KAI", "Priya": "PRI"},
  "llm_provider": "openai",
  "embedding_model": "embeddinggemma",
  "hooks_auto_save": true,
  "max_backups": 10,
  "llm_external_warn": true,
  "embedder_identity_strict": true,
  "languages": ["en"]
}

Environment variables

All optional. Defaults are sensible for local single-user use.

Variable Default Purpose
MEMPALACE_PALACE_PATH ~/.mempalace/palace Override palace location
MEMPALACE_NONINTERACTIVE unset Skip prompts (CI/CD, agents)
MEMPALACE_READONLY unset Block all mutation MCP tools
MEMPALACE_EMBED_MODEL ONNXMiniLM_L6_V2 Embedding model
MEMPALACE_MAX_CHUNKS_PER_FILE 50000 Per-file chunk cap (0 = disable)
MEMPALACE_HOOKS_AUTO_SAVE true Disable auto-save hooks
MEMPALACE_MAX_BACKUPS 10 Backup retention cap
MEMPALACE_LLM_CONSENT unset Opt in to external LLM providers
MEMPALACE_LLM_EXTERNAL_WARN true Warn on external LLM calls
MEMPALACE_AGENT_ID unset Multi-agent identity
MEMPALACE_AGENT_SCOPE unset Multi-agent isolation
MEMPALACE_DISABLE_HOOK unset Kill switch for all hooks
OPENAI_EMBEDDING_API_KEY Embedding-specific API key
OPENAI_EMBEDDING_BASE_URL Embedding-specific base URL

XDG directory layout

Platform Config Data
Linux $XDG_CONFIG_HOME/mempalace/ $XDG_DATA_HOME/mempalace/
macOS ~/Library/Application Support/mempalace/ same
Windows %APPDATA%/mempalace/ same
Fallback ~/.mempalace/ ~/.mempalace/

Backward-compatible — if ~/.mempalace/ exists, it's used. Migration from old path supported.


Rust Enhancements

Beyond the original Python features, the Rust port includes:

Security & Privacy

  • Privacy consent gate — blocking guard for env-fallback LLM API keys; no LLM call without explicit consent
  • External LLM warning — detects public vs. private endpoints; Tailscale CGNAT (100.64.0.0/10) treated as local
  • Read-only MCP mode via MEMPALACE_READONLY — write tools are disabled
  • MCP path sandboxingmpr_mine canonicalizes paths and restricts traversal
  • No shell injection vectors — Rust's Command::new vs Python's os.system

Storage

  • Sharded index persistence with manifest commit/rollback — crash-safe save, no orphan data on failure
  • Backup retentionMEMPALACE_MAX_BACKUPS=10 prunes oldest backups after new ones are written
  • Per-target PID guard — atomic lock claim with O_EXCL for concurrent safety
  • Staging rebuild — crash-safe repair via temp directory + atomic swap

Search & KG

  • Metric-aware distance conversion — cosine, L2, and inner product all map correctly to [0, 1] similarity
  • FollowupTracker diagnostic — detects when smart-search results don't satisfy the user
  • Legacy-metric warning — detects palaces created before cosine was consistently set
  • BM25 hybrid rerank — 70% vector + 30% BM25 weighted combination

i18n & Cross-lingual

  • 9 locale files — French, Spanish, German, Simplified Chinese, Traditional Chinese, Hindi, Japanese, Korean, Belarusian (in addition to English, Russian, and Brazilian Portuguese)
  • Script-aware detection — Cyrillic, Devanagari, CJK word boundaries for entity detection
  • Case-insensitive BCP 47 resolutionzh-cn, zh-CN, ZH-CN all resolve to Simplified Chinese
  • Embeddinggemma-300m ONNX — multilingual embedder stub for cross-lingual search

Reliability

  • Background task runner — auto-forget, consolidation, retention sweep, lesson decay all run on schedule
  • Init idempotency — re-running init on an existing palace is safe
  • Graceful shutdown — SIGINT handling for long operations with PID file guard
  • Stale-PID detection — age-based stale-PID cleanup with fallback
  • JSON-RPC null payload safety — null/empty bodies rejected with -32600
  • Post-rebuild FTS5 cleanup — FTS5 integrity check + VACUUM after repair

Embedder ecosystem

Embedder Dim Feature flag Status
FastEmbed (ONNX) 384-d embed-fastembed (default)
OpenAI-compatible 1536/3072 embed-openai
Model2Vec variable embed-model2vec
Tract (ONNX) variable embed-tract
Cohere 1024/4096 embed-cohere
Voyage 1024/2048 embed-voyage
Gemini variable embed-gemini
OpenRouter variable embed-openrouter
EmbeddingGemma (ONNX) 384 (MRL) embed-embeddinggemma 🔧 stub
Null (BM25-only) 0 None required

Download formats

The normalizer supports 9+ chat export formats, auto-detected from file structure:

Format Source Auto-detected by
Claude Code JSONL ~/.claude/projects/ JSONL with role/content
Claude.ai JSON Claude.ai export JSON with chat_messages
ChatGPT JSON conversations.json JSON with mapping
Slack JSON Slack export JSON with channel/messages
Codex CLI JSONL ~/.codex/sessions/ session_meta header
SoulForge JSONL SoulForge export segments/toolCalls/durationMs
OpenCode SQLite OpenCode sessions DB session table with dir column
Gemini CLI JSONL Gemini sessions role + content fields
Plain text Any .txt Fallback

License

MIT — see LICENSE.

About

The highest-scoring AI memory system ever benchmarked. Now in Rust with rich features — single-binary port from @milla-jovovich/mempalace. Free and local.

Topics

Resources

License

Stars

12 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors