Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Parity with MemPalace

Feature-by-feature comparison against MemPalace/mempalace (the Python project whose concepts this one reimplements; no source code is shared, see “License lineage” below), updated 2026-08-05.

Ported (Rust equivalent exists)

MemPalaceUndercroft equivalent
Palace model (wings/rooms/drawers, verbatim)undercroft-core (same metadata fields, deterministic ids)
sqlite_exact backendundercroft-store (SQLite system of record)
Chroma/Qdrant/pgvector server backendsundercroft-indexsealed client-side (MemPalace sent plaintext)
Embedder + identity tracking (RFC 001)Embedder trait + per-vault identity enforcement (a swap is refused, not silently ranked; only hash→hash migrates automatically)
Model embeddings (sentence-transformers)four postures — undercroft-embed-onnx (tract, pure Rust), undercroft-embed-ort (ONNX Runtime, ~2.5×/forward + int8), http (any served model, TLS-or-loopback enforced), or caller-supplied external:<name>@<dim>. Models are user-supplied throughout; see EMBEDDERS.md
File minermine --mode files
Conversation miner (--mode convos)mine --mode convos
Sweep (per-message drawers)sweep (idempotent via keyed fingerprints)
Wake-up layers L0/L1wake-up (identity.txt + essential story)
Knowledge graph (temporal, validity windows)kg add/query/rel/invalidate/supersede/timeline/stats
Tunnels (cross-wing)tunnel create/list/follow/delete/traverse
Hallways (entity co-occurrence)hallways (computed on demand; never persisted)
Drawer CRUD, delete-by-source, dup checkdrawer …, keyed fingerprints
Agent diaries + list_agentsdiary write/read/agents
Dedup / stats / taxonomydedup, stats, taxonomy
Backupsbackup create/list/restore (verifies before snapshot)
Repairrepair (fingerprint backfill, re-embed, vacuum, verify)
Export / migrateexport (JSONL) + import (undercroft & mempalace formats)
MCP stdio server (~35 tools)34 tools (daemon/sync/session tools inapplicable — process management moved to the OS). The count is not maintained by hand: crates/undercroft-cli/src/parity.rs holds the inventory and the code is counted against it in both directions, so a tool added without a line fails the build and a line naming a tool that no longer exists fails too
MCP HTTP team server (serve)serve-http (bearer token enforced; --read-only is a posture on the whole process — both stores opened read-only, the route gate in front of dispatch, failing closed)
Daemon / jobs / start / stop / waitdaemon run + systemd/compose units (deploy/) — process management belongs to the OS
tools/render_jsonl.pytranscript render
Auto-save hooks (Claude Code/Codex/Cursor)hooks/, .claude-plugin/hooks/, undercroft hooks claude-code
Claude Code plugin (commands/skills/MCP).claude-plugin/ + root commands/, skills/, rules/
Benchmarks (LongMemEval harness)undercroft-bench longmemeval (same protocol/metrics) + synth CI benchmark
LoCoMo / ConvoMem / MemBench harnesses`undercroft-bench locomo
Embedded ChromaDB’s in-process index roleBundled SQLite store is the system of record; warm_embedding_cache gives long-running servers (serve-mcp / serve-http / daemon) a decrypt-once in-memory vector cache — the in-process index role, with nothing plaintext-derived persisted
Deploy (compose server, systemd)deploy/
Docs / examplesdocs/, examples/

What exists only here (updated for v1.0.0)

Everything below has no upstream equivalent — it is original work of this project, which is why the two codebases share concepts but not code (and why this project’s license is independent of upstream’s; see the “License lineage” section at the end).

Security layer (MemPalace stored everything in plaintext):

  • Vault isolation: per-vault SQLite databases with per-vault HKDF-SHA256-derived keys (enc/mac/manifest domains) from one master key (file or Argon2id passphrase).
  • Sealed-at-rest storage: XChaCha20-Poly1305 over content and embeddings and every derived artifact (ColBERT token matrices, PQ code rows + codebooks + IVF centroids, MUVERA FDE rows + params), each under its own AAD domain bound to vault + record id — cross-vault replay fails cryptographically.
  • Integrity: HMAC-SHA256 tag on every drawer, KG entity/triple, and tunnel; a tamper-evident audit chain advancing inside the same transaction as each write; a MAC’d manifest as an out-of-database rollback anchor with open-time crash-vs-rollback reconciliation.
  • Durability: WAL + synchronous=FULL pinned, fsynced manifest anchor (atomic rename + directory sync), fsynced key material; bulk ingest batches whole transactions (measured ~55× fewer disk syncs).
  • Key rotation (vault rotate): fresh derived keys, every sealed blob re-encrypted byte-exact and every tag/chain re-keyed in one transaction; crash-safe at any instant via a two-phase manifest swap.
  • Recipient-encrypted export bundles (bundle keygen, export --to) — a backup never exists in plaintext, and since C3.4 the key exchange is hybrid post-quantum: keygen mints X25519 + ML-KEM-768 (pq1 identities) and a v2 bundle derives its file key from both shared secrets, closing harvest-now-decrypt-later on the one asymmetric exchange in the codebase. Legacy bare-hex X25519 identities still parse and still receive openable v1 bundles, and a hybrid identity opens old v1 backups with its curve half — but a hybrid recipient never silently downgrades, and an X25519-only secret gets a typed refusal on a v2 bundle (pinned by test). Posture page: PQ.md.
  • Signed bundle manifests — Ed25519 sender attestation beside the recipient flow: encryption says who may READ, the signature says who WROTE. Scope, trust claim, expiry, counts, provenance, and an unconditionally-checked payload digest. A sender-declared trust label is a claim, never a boundary (LABELS.md); legacy payloads import unattested and say so.
  • Write-path admission control — a deterministic tier-1 screen over a closed signal vocabulary (offsets, never content) plus attack-fixture similarity and an optional declared per-writer rate screen; flagged writes divert into a reserved quarantine wing that retrieval, recent and list_drawers all exclude and that MCP cannot read or destroy at all. Rulings are chain-audited, a deny is receipted, and the whole thing is default-off (a byte-identical write contract until a deployment declares it). Screening lives at the store’s single write choke point behind a required argument, so a new write path does not compile until its author decides.
  • Provable forgetting and retention — chain-attested destruction with heads, tombstone interval and unkeyed content fingerprints: the vault verifies by keyed replay, third parties verify the operator’s Ed25519 signature. Retention policies per wing/room are operator-only, HMAC tagged and audited, and enforce through an explicit sweep on the HMAC-covered clock — nothing expires on a timer.
  • Deployment-assigned wing trust — a closed vocabulary the operator assigns (never MCP), HMAC-tagged so a flip fails verification, consumed as a candidate-set floor resolved before candidates are drawn.
  • Read and egress auditing — exports are chain-audited unconditionally on every surface; reads are audited under UNDERCROFT_READ_AUDIT=chain with a keyed fingerprint of the query, never its text.
  • Keyed duplicate fingerprints, token-mandatory non-loopback HTTP bind, per-vault request assertions, read-only serving posture.

Retrieval stack beyond MemPalace’s cosine search:

  • Hybrid semantic + lexical (BM25) + recency fusion with typo tolerance.
  • Optional ONNX embedders on two runtimes (pure-Rust tract, or ONNX Runtime at ~2.5×/forward with int8) selected by env at runtime.
  • Cross-encoder reranking (measured LoCoMo R@10 94.6 → 97.7%).
  • ColBERT late interaction: encode-at-ingest token matrices (PQ-compressed ~16 B/token), one query forward + MaxSim at search (~96.5–96.8% at a flat ~70–93 ms/q independent of core count).
  • Bounded-RAM candidate tiers: PQ/IVF prefilter (~48 B/vector, recall flat in corpus size, sealed at rest with a decrypt-once slab cache, with an optional per-wing codebook/IVF tier) and MUVERA FDE token-aware candidates (recall measured identical to fusion at −25% latency, rows PQ-compressed 32×).
  • Starvation-free scoping: every declared filter (wing, room, kind, trust floor, quarantine fence) is resolved into a scope before candidates are drawn, and pools are sized by the scope — a filter over globally generated candidates can otherwise come back empty while the scope holds the answer.
  • Measured to 10⁶ drawers: shipped defaults hold R@5 100.0% at every checkpoint from 131k to 1M — unscoped, wing-scoped, room-scoped and wing+room — at 20.4–112.7 ms/q unscoped and ~13–32 ms/q flat when scoped. Both the two-stage candidate pool and the scope-sized pools exist because instruments filed recall defects against the previous fixed pool and the gate was not declared met until they closed.
  • Every number above is measured and reproduced in benchmarks/RESULTS.md and RETRIEVAL_SCALING.md.

Multi-tenancy & fleet operation:

  • Versioned /v1 REST engine: per-vault assertions, external embeddings, dedup-refresh, lossless export/import (vectors + token artifacts ride along — restore is a copy, not a re-embed), and operator-plane routes (wing trust, admission review, retention + sweep, attested forgetting) that are deliberately absent from MCP. Import re-stamps the writing surface and is admission-screened, so a restore or a tenant migration is not a route around the screen.
  • undercroft-orchestrator: a separate control plane (instance registry with sealed credentials, HMAC-only tenant tokens shown once, routing proxy with subpath allowlist, token rotation, per-tenant rate limits, count-verified live migration) — the engine never links it.

Operations:

  • Opt-in, metadata-only observability: Prometheus /metrics, OTLP traces (with header auth), structured logs, live SSE, the Palace Monitor UI, and a full Grafana/Alertmanager/Loki/Tempo deploy stack with a tamper runbook. Zero telemetry deps in default builds.
  • Scenario-driven agents implementation guide covering every deployment shape with the complete tool/route/env reference.

Also only here: Weaviate backend; sealed-client remote indexing (all five backends receive ciphertext; MemPalace uploaded plaintext); zstd compress-then-encrypt; int8 embedding quantization; deterministic offline hash embedder as the default.

Ported in v0.5.0 (previously listed as gaps)

MemPalaceUndercroft equivalent
Milvus backendundercroft-index REST v2 client (--backend milvus), tested against live standalone Milvus in compose
LLM refinement pipeline (llm_refine, llm_client)undercroft-llm crate (Ollama + OpenAI-compatible local runtimes) + undercroft refine — extracts entities and KG triples from drawers; never touches verbatim content; only runs when UNDERCROFT_LLM_URL is explicitly set
model_eval multilingual datasets + harnessDatasets restored (10 languages × calibration / entity / memory / room tasks); `undercroft-bench model-eval calibration
AAAK dialect / closets (dialect.py)undercroft closets + undercroft_get_closet_index MCP tool — deterministic compact index (one scannable line per room: counts, date span, key entities, drawer ids); computed on demand, nothing persisted
Spellcheck (query typo tolerance)Levenshtein-1 fuzzy term matching built into the lexical scorer (5+ char terms)
WebsiteRust-native mdBook site in website/ reusing docs/ (docker compose run --rm site)

| Memory-extraction eval task | undercroft-bench model-eval memories — SQuAD-style token-F1 with greedy one-to-one alignment (threshold 0.5), CJK-aware tokenization; reports match P/R/F1, mean token-F1, type accuracy | | i18n (mempalace/i18n) | CLI result strings localized in the 9 dataset languages (de/es/fr/hi/it/ko/pt/ru/zh) via UNDERCROFT_LANG, English default + fallback; errors/help stay English by design (exit codes are the script contract) |

Not ported

Nothing remains. The one permanent role-replacement worth restating: embedded ChromaDB is a Python library and cannot be linked from Rust — its roles (embedded zero-config store + in-process vector index) are filled by the bundled SQLite store and the in-memory embedding cache respectively.

Behavioral differences to know about

  • Sealed vaults trade FTS5 indexing for encryption (decrypt-scan search); hmac-only vaults keep plaintext searchability with integrity tags and, above ~2k drawers, an FTS5 BM25 prefilter (tunable via UNDERCROFT_FTS_PREFILTER_MIN, off to disable) that narrows the candidate scan without changing final scoring.
  • Remote backends receive sealed content; MemPalace uploaded plaintext. A mirror is an accelerator, not a different policy: remote search takes its trust floor, quarantine fence and closed vocabularies from the same resolver the local path uses.
  • Benchmark numbers with the default hash embedder are not comparable to MemPalace’s published model-based numbers — use a model posture with a MiniLM-class model for like-for-like conditions. Measured here, the choice matters more than this repo used to say: hash → any modern model is +3.2 to +4.2pp turn all-gold on LoCoMo, while four modern models span ≤1.0pp among themselves. (The old “a semantic embedder is not the biggest lever” conclusion rested on MiniLM’s +0.3pp, and was a fact about MiniLM.)
  • The default embedder is single-language by construction: feature hashing over surface forms matches only shared literal tokens and trigrams, so car/automobile do not meet and a translation pair scores below an unrelated sentence. Cross-lingual retrieval needs a multilingual model — and, since the script-disjoint fusion reweight, that one condition suffices even across scripts (FLORES-200 cross-script pairs 36–44% → 95–100% R@5 at default weights).

License lineage

MemPalace is Python, published under the MIT License. Undercroft began as a fork and its feature surface was reimplemented in Rust as documented in this file; it contains no MemPalace source code — the two projects share behavior specifications, not expression. Undercroft is therefore licensed independently, under the Business Source License 1.1 (free use including production, one hosted/embedded non-compete carve-out, automatic conversion to MPL 2.0 four years after each release). The MIT notice for MemPalace’s conceptual heritage is preserved in NOTICE.