Local-first encrypted memory for AI agents.
Store an agent's memory on-device, recall it with hybrid vector search, and erase it by destroying its key, with a verifiable erasure receipt. Zero-LLM ingest and an MCP server, all on one encrypted SQL/vector file. No API keys; your data stays on your machine.
cargo add citadeldb citadeldb-memA live model of Citadel's on-disk format: my.db is opaque ciphertext until the passphrase decrypts the page. Nothing is stored - to run real SQL, open the playground.
Citadel Studio.
A native client for Windows, macOS, and Linux. Open encrypted vaults, browse tables and stored memory, run SQL with EXPLAIN and ANALYZE, and inspect vectors and integrity results.

An encrypted file that is both an agent memory and a database.
The same AES-256-CTR + HMAC pages, copy-on-write B+ tree, and shadow-paging commit power both halves. Use the memory engine, the SQL engine, or both in one file.
Memory engine
Memory that lives encrypted at rest. Typed atoms and edges, hybrid recall, and a Model Context Protocol server so Claude Desktop or any MCP client can read and write it.
VECTOR(N)type with a PRISM-backed filtered ANN index- Hybrid recall: vector ANN + BM25 keyword + recency + optional cross-encoder reranker
- Cryptographic forgetting: erase an atom or region by destroying its key
- 15-tool MCP server; historical scores of 87.2% on LoCoMo and 86.2% on LongMemEval-S (gpt-4o)
Encrypted database
An embedded SQL + key-value engine with snapshot isolation and shadow paging. Encrypted pages live in the database file; encryption keys live in a companion key file.
- SQL: FULL OUTER & LATERAL joins, recursive CTEs, window functions, triggers, materialized views
- JSON / JSONB with 14 PostgreSQL operators, GIN indexes, full-text search
- Key-value API and SQL share one transaction
- P2P encrypted sync over Noise; Python, CLI, C FFI, and WebAssembly bindings
A complete engine in a single file.
Full SQL, real ACID, encrypted sync, and a memory engine. Key hierarchy built around RFC 3394, RFC 5869, and a Rust-only crypto core.
Encrypted at rest
AES-256-CTR + HMAC-SHA256 per page, verified before decryption. Fresh random IV on every write.
Real SQL
FULL OUTER + LATERAL joins, recursive CTEs (with-DML), window functions, triggers, materialized views, UPSERT, RETURNING, JSON/JSONB.
ACID, no WAL
Copy-on-Write B+ tree with shadow paging. Snapshot isolation with concurrent readers, atomic single-byte commits.
Vector + memory engine
VECTOR(N) with a PRISM filtered ANN index, plus a memory engine of typed atoms with hybrid vector + keyword recall.
MCP server
Expose encrypted memory to Claude Desktop or any MCP client over stdio. 15 tools for recall, remember, link, evolve, and forget.
Cryptographic forgetting
Erase data by destroying its key, not by overwriting. Whole-store, per-region, and per-atom, with verifiable erasure receipts.
P2P encrypted sync
Merkle-based table diffing over Noise (NNpsk0_25519_ChaChaPoly_BLAKE2s) with ephemeral forward secrecy.
Three-tier key hierarchy
Passphrase → Argon2id → Master Key → AES-KW → REK → HKDF → DEK + MAC. Passphrase changes need no page re-encryption.
Cross-platform bindings
Windows, Linux, macOS. Python, a panic-safe C FFI, and WebAssembly from one Rust core.
Open an encrypted database.
Create a vault, connect to SQL, and run a query.
Add the crates
Engine + SQL frontend from crates.io.
Open with a passphrase
Argon2id derives the master key in memory. Keys live in {dbname}.citadel-keys, not inside the database.
Run SQL, or use the KV API
Both are first class. Mix them in the same transaction.
Keep the vault together
The database has a separate key file and optional key and audit sidecars. Use the backup API for a consistent snapshot.
What "open my.db" actually does.
Six stages from passphrase to a sealed 8,208-byte page on disk. Real algorithm names, real byte counts, drawn from the citadel-crypto source.
my.db.citadel-keys. The passphrase stays in memory only; never touches disk.zeros(32)"citadel-dek-v1"→"citadel-mac-key-v1"→epoch ‖ page_id ‖ IV ‖ ciphertext, so a forged page id or replay from another epoch fails the tag. Page = 16 + 8,160 + 32 = 8,208 bytes.my.db alone and you get opaque ciphertext. Lose the passphrase and nothing opens.Encrypted memory engine.
citadel-mem stores memory as typed atoms grouped into regions and connected by typed edges. Recall blends vector similarity, keyword scoring, and recency; forgetting destroys keys, not just rows.
Hybrid fusion recall. A query over-fetches ANN candidates, then scores each on normalized semantic distance, BM25 keyword overlap, recency (30-day half-life), and importance. An optional cross-encoder reranker (replace or reciprocal-rank-fusion) sharpens the top results, and a graph walk pulls in linked neighbors.
Cryptographic forgetting. Encrypted regions seal atoms under individual keys. Forgetting destroys the active vault's key copies and returns a receipt. External backups, replicas, and physical-media copies are outside that scope.
Explicit semantic embeddings. Use local Candle models such as E5-large or BGE, or a custom embedding backend. Cross-encoder reranking and CUDA support are optional. Stored-memory inspection through MemoryMaintenance needs no model.
Historical results on encrypted regions. LoCoMo uses the harness's prompts with a gpt-4o-mini reader and judge. LongMemEval-S uses the official CoT reader prompt and judge protocol with gpt-4o. Recorded configurations and limitations.
Plug encrypted memory into Claude Desktop.
citadeldb-mcp exposes a memory region as 15 MCP tools over JSON-RPC 2.0 on stdio, so Claude Desktop, an IDE, or any other MCP client can read and write it directly.
Read tools
- mem_recall
- mem_fetch
- mem_get
- mem_edges
- mem_profile
- mem_summarize
- mem_verify
Write & forget tools
- mem_remember
- mem_remember_batch
- mem_update
- mem_link
- mem_unlink
- mem_evolve
- mem_evict
- mem_forget
{
"mcpServers": {
"citadel": {
"command": "citadeldb-mcp",
"args": [
"--db", "/absolute/path/to/memory.cdl",
"--embedder", "e5-large",
"--reranker", "ms-marco-minilm"
],
"env": { "CITADEL_KEY": "your-passphrase" }
}
}
}Install with pip install citadeldb-mcp, then run citadeldb-mcp pull e5-large and citadeldb-mcp pull ms-marco-minilm. Pulls need no vault key; serving requires CITADEL_KEY, --db, and --embedder. Managed model downloads are pinned and verified. Recall hits include provenance and integrity results. See MCP setup.
59 SQL comparisons with SQLite.
Single-threaded measurements with durability off and both caches configured for 4,096 pages (about 32 MiB). Ratios are SQLite time / Citadel time: above 1 means Citadel is faster, below 1 means Citadel is slower. Execution and cached repeat reads are reported separately. Measurements from September 13, 2026 combine the complete 6b41d0c3 run with eight execution pairs from ea8827d7 and three UPDATE pairs from 0f9362cf, as listed in the methodology; they are not a full-suite timing run at one revision. 58 of 59 aggregate point ratios exceed 1; generated UPDATE is 0.960×, with run-level uncertainty retained in the data.
Execution speed
Cached repeat reads
Identical reads reuse Citadel's cached results or projected UNION ALL branches while SQLite executes again. These are not first-execution timings.
page_size=8192, journal_mode=MEMORY, synchronous=OFF, cache_size=4096. Citadel: SyncMode::Off, cache_size=4096. Reproduce: cargo bench --locked -p citadeldb-sql --bench h2h_bench -- --sample-size 30 --warm-up-time 1 --measurement-time 2 --noplot. The displayed rows use the two candidate runs from serial reference/candidate/candidate/reference cohorts; the data retains per-run intervals and drift.22 crates, one file format.
Desktop, CLI, bindings, and agent interfaces share the database, SQL, and memory APIs. Studio uses those APIs directly, without an MCP server.
Page layout is 8,208 bytes. Sixteen bytes of random IV, 8,160 bytes of ciphertext, and 32 bytes of HMAC-SHA256. Authentication is checked before decryption.
Commit protocol is shadow paging. Dirty pages go to new locations, BLAKE3 Merkle hashes climb bottom-up, the inactive 240-byte commit slot is updated, then one byte in the file header flips to publish the new root. No write-ahead log.
Memory uses the same storage engine. Vectors, atoms, and edges use authenticated pages and database transactions. citadel-membench measures recall on LoCoMo and LongMemEval.
A SQL dialect that doesn't disappoint.
FULL OUTER and LATERAL joins, recursive and DML-bearing CTEs, triggers, materialized views, window frames, JSON/JSONB with PostgreSQL operators, full-text search, and a native VECTOR type.
Statements
- CREATE / DROP / ALTER TABLE
- CREATE INDEX (partial / expr)
- CREATE VIEW / MATERIALIZED VIEW
- CREATE TRIGGER
- UPSERT (ON CONFLICT)
- RETURNING (OLD / NEW)
- TRUNCATE / SAVEPOINT
- PREPARED / $1, $2, ...
- EXPLAIN / EXPLAIN ANALYZE
Clauses & joins
- INNER / LEFT / RIGHT / CROSS
- FULL OUTER / LATERAL
- Subq.: scalar / IN / EXISTS / ANY/ALL
- Correlated subqueries
- WITH / WITH RECURSIVE
- WITH-DML (RETURNING)
- UNION / INTERSECT / EXCEPT
- GROUP BY / HAVING
- Window frames (ROWS / RANGE)
JSON / JSONB & search
- 14 PostgreSQL operators
->/->>/#>/#>>@>/<@/?/?|/?&- 16 scalar / 4 aggregate fns
- GIN indexes on JSONB
- FTS: tsvector / ts_rank / phrase
VECTOR(N)+ ANN index<->L2 /<#>inner /<=>cosine
Types / constraints / windows
- INT / REAL / TEXT / BLOB / BOOL
- DATE / TIME / TIMESTAMP(TZ)
- INTERVAL / IANA zones (jiff)
- Generated cols (STORED / VIRTUAL)
- STRICT tables
- COLLATE BINARY / NOCASE / RTRIM
- FK: CASCADE / SET NULL / DEFERRABLE
- ROW_NUMBER / RANK / LAG / LEAD
- SUM / AVG OVER / PARTITION BY
Encryption, keys, and integrity.
No plaintext on disk
Page payloads are encrypted before writing and authenticated before decryption. File headers and commit metadata are not hidden.
Keys live outside the database
Encryption material lives in {dbname}.citadel-keys. The passphrase derives a master key in memory via Argon2id and never touches disk.
Passphrase rotation
Changing a passphrase derives new wrapping material and re-wraps the root key. It does not re-encrypt pages; the work is independent of the database's page count.
Cryptographic erasure
Encrypted regions use per-atom keys. Forgetting destroys the active key copies and issues a receipt; it does not erase external backups, replicas, or physical-media remnants.
Forward-secret sync
Noise NNpsk0_25519_ChaChaPoly_BLAKE2s with a 256-bit PSK. Ephemeral Curve25519 keys per session; compromise of one doesn't leak prior traffic.
FIPS-oriented at-rest profile
A feature flag uses PBKDF2-HMAC-SHA256 (600,000+ iterations) with AES-256-CTR for database storage. This is not a claim of whole-product validation.
Audit verification detects edits and broken links in retained history. It cannot detect restoration of a complete, older authentic vault snapshot, including its keys and audit files. Freshness requires an external anchor. See the security policy.
Desktop, CLI, and language bindings.
Rust
crates.io / citadeldbThe database API and SQL frontend, with typed transactions. 16 crates published.
Python
PyPI / citadeldbEncrypted SQL, vector search, and memory with type stubs. Bring your own embedder; the standalone MCP server is a separate package.
C / C++
cbindgen / citadel.hA panic-safe C ABI with static or dynamic linkage. Drop into any toolchain that speaks C ABI.
WebAssembly
npm / @citadeldb/wasmA real encrypted database in your browser tab. Compiled with wasm-pack. Powers the playground.
CLI
crates.io / citadeldb-cliAn interactive SQL shell with tab completion, syntax highlighting, and 27 dot-commands.
From the blog.
- Introducing Citadel
An embedded database that encrypts every page, with a built-in memory engine.
Start with the browser build.
No install, no signup. Run real SQL against an encrypted database compiled to WebAssembly.