Citadel

Memoria cifrada local-primero para agentes de IA con olvido criptográfico

Documentación

Citadel

crates.io npm PyPI citadeldb PyPI citadeldb-mcp MCP registry: dev.citadeldb/mcp
CI LoCoMo 87.2% (gpt-4o-mini, mean of 3 runs) LongMemEval-S 86.2% (gpt-4o reader) License

Resultados y configuraciones de memoria histórica

Inicio rápido

Para memoria semántica a través de MCP, instala uv y descarga el embedder local y el reranker opcional de cross-encoder:

uvx citadeldb-mcp pull e5-large
uvx citadeldb-mcp pull ms-marco-minilm

Establece CITADEL_KEY a tu frase de contraseña de la bóveda (export CITADEL_KEY="your-passphrase" en macOS/Linux o $env:CITADEL_KEY = "your-passphrase" en PowerShell), luego inicia:

uvx citadeldb-mcp --db memory.cdl --embedder e5-large --reranker ms-marco-minilm

El servidor se comunica a través de stdio. Consulta MCP para la configuración del cliente. Las descargas de modelos no necesitan una clave de bóveda; servirlos sí.

Memoria (Python)

Instala el paquete publicado con pip install citadeldb. Consulta la guía de compilación desde fuente y memoria semántica en Python. Los embedders implementan embed_with_cancel(texts, cancel_token) y verifican la cancelación entre lotes acotados. Los modelos locales de Candle requieren la característica de compilación candle-embed.

Memoria (Rust)

Usa citadeldb y citadeldb-mem con la característica candle-embed. Este ejemplo carga e5-large y un reranker local de cross-encoder. Se admiten otros ajustes preestablecidos o un Embedder personalizado.

use std::sync::Arc;
use citadel::DatabaseBuilder;
use citadel_mem::{AtomInput, CandleEmbedder, CrossEncoder, MemoryEngine, RecallQuery, RerankStrategy};

// Encrypted store (per-atom keys enable cryptographic forgetting)
let db = DatabaseBuilder::new("memory.db")
    .passphrase(b"secret")
    .enable_region_keys(true)
    .create()?;
let mem = MemoryEngine::open(Arc::new(db))?;

let embedder = Arc::new(CandleEmbedder::e5_large("/path/to/e5-large")?);
mem.create_encrypted_region("chat", embedder)?;
mem.set_reranker(
    Arc::new(CrossEncoder::ms_marco_minilm_l6("/path/to/ms-marco-minilm")?),
    RerankStrategy::default(),
);

// Remember raw turns (no LLM)
mem.remember("chat", AtomInput::new("fact", "Alice's cat is named Mochi"))?;
let berlin = mem.remember("chat", AtomInput::new("fact", "Alice lives in Berlin"))?;

// Recall by relevance
for hit in mem.recall("chat", RecallQuery::by_text("where does Alice live?", 5))? {
    println!("{:.3}  {}", hit.relevance.expect("ranked recall"), hit.text);
}

// Cryptographic forgetting: destroy the atom's key
mem.forget_atom("chat", berlin)?;

SQL y clave-valor

Usa los crates citadeldb y citadeldb-sql - o prueba SQL sin instalación en el playground en vivo.

use citadel::DatabaseBuilder;
use citadel_sql::Connection;

let db = DatabaseBuilder::new("my.db")
    .passphrase(b"secret")
    .create()?;

let conn = Connection::open(&db)?;
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);")?;
conn.execute("INSERT INTO users (id, name) VALUES (1, 'Alice');")?;
let result = conn.query("SELECT * FROM users;")?;

// Key-value API
let mut wtx = db.begin_write()?;
wtx.insert(b"key", b"value")?;
wtx.commit()?;

let mut rtx = db.begin_read();
assert_eq!(rtx.get(b"key")?.unwrap(), b"value");

// Named tables
let mut wtx = db.begin_write()?;
wtx.create_table(b"sessions")?;
wtx.table_insert(b"sessions", b"token-abc", b"user-42")?;
wtx.commit()?;

// In-memory (no file I/O - useful for testing and WASM)
let mem_db = DatabaseBuilder::new("")
    .passphrase(b"secret")
    .create_in_memory()?;

CLI

citadel --create my.db

citadel> CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
citadel> INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob');
citadel> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
|  2 | Bob   |
+----+-------+

citadel> .backup mydb.bak
citadel> .verify
citadel> .upgrade
citadel> .stats
citadel> .audit verify
citadel> .rekey
citadel> .compact clean.db
citadel> .dump users

# P2P sync
citadel> .keygen
citadel> .listen 4248 <KEY>              # Terminal A
citadel> .sync 127.0.0.1:4248 <KEY>      # Terminal B

Citadel Studio

Un cliente de escritorio nativo para Windows, macOS y Linux. Abre bóvedas cifradas, explora tablas y memoria, ejecuta SQL con EXPLAIN y ANALYZE, e inspecciona vectores y resultados de integridad.

Consulta la guía de Studio para capturas de pantalla e instrucciones de compilación. Descarga Citadel Studio para Windows, macOS o Linux.

Frameworks de agentes

Los adaptadores implementan interfaces de almacenamiento, sesión y recuperación específicas del framework. Cada uno requiere un embedder explícito. Consulta el README del paquete para la configuración, el comportamiento de búsqueda y los filtros admitidos.

FrameworkPaqueteImplementa
LangGraphcitadeldb-langgraphBaseStore
CrewAIcitadeldb-crewaiStorageBackend
OpenAI Agents SDKcitadeldb-openai-agentsSession
Google ADKcitadeldb-google-adkBaseMemoryService
LlamaIndexcitadeldb-llamaindexBasePydanticVectorStore
LangChaincitadeldb-langchainVectorStore, BaseChatMessageHistory
Haystackcitadeldb-haystackDocumentStore
Microsoft Agent Frameworkcitadeldb-ms-agent-frameworkHistoryProvider, ContextProvider
Strands Agentscitadeldb-strands-agentsSessionRepository
pip install citadeldb-langgraph

Una sola base de datos sirve a cada adaptador en el hilo que la abrió, por lo que el almacenamiento a largo plazo de un grafo y sus transcripciones de sesión pueden compartir un solo archivo cifrado. Consulta packaging/ para el README de cada paquete.

MCP

Sirve una región de memoria cifrada a Claude Desktop o a cualquier cliente MCP. citadeldb-mcp está publicado en PyPI y listado en el registro oficial de MCP como dev.citadeldb/mcp. Ejecútalo sin instalarlo a través de uvx.

Para la configuración recomendada de recuperación semántica, descarga el embedder y el reranker de cross-encoder una vez:

uvx citadeldb-mcp pull e5-large
uvx citadeldb-mcp pull ms-marco-minilm

Los comandos de descarga no necesitan una clave de bóveda. Antes de iniciar el servidor, establece CITADEL_KEY a la frase de contraseña de la bóveda: usa export CITADEL_KEY="your-passphrase" en macOS/Linux o $env:CITADEL_KEY = "your-passphrase" en PowerShell. Luego ejecuta:

uvx citadeldb-mcp --db memory.cdl --embedder e5-large --reranker ms-marco-minilm

--db, --embedder y CITADEL_KEY son obligatorios al servir. El reranker es opcional, pero e5-large con ms-marco-minilm es la configuración utilizada para los benchmarks de memoria.

Para instalar el ejecutable en su lugar, ejecuta pip install citadeldb-mcp o cargo install citadeldb-mcp. Descarga los mismos modelos con citadeldb-mcp pull e5-large y citadeldb-mcp pull ms-marco-minilm, luego agrégalo a claude_desktop_config.json:

{
  "mcpServers": {
    "citadel": {
      "command": "citadeldb-mcp",
      "args": [
        "--db", "/absolute/path/to/memory.cdl",
        "--embedder", "e5-large",
        "--reranker", "ms-marco-minilm"
      ],
      "env": { "CITADEL_KEY": "your-passphrase" }
    }
  }
}

Benchmarks de memoria histórica

Los resultados registrados de LoCoMo y LongMemEval se resumen a continuación; sus configuraciones y limitaciones preceden a los cambios actuales del motor de memoria. Las comparaciones de SQL con SQLite sin cifrar en 59 casos están en Benchmarks de velocidad.

LoCoMo - lector y juez gpt-4o-mini con los prompts del harness, media de 3 ejecuciones medidas el 18 de agosto de 2026:

MétricaPuntuación
General87.2% +/- 0.3
Contexto completo, sin recuperación (reportado en el artículo de Mem0, no reejecutado aquí)72.9%

La recuperación es idéntica en las tres ejecuciones; la variación proviene del no determinismo del lector y del juez. Una auditoría manual estima que ~6.4% de las claves de respuesta de LoCoMo son erróneas, por lo que la precisión bruta debe interpretarse teniendo en cuenta ese ruido de anotación.

La memoria se construye sin LLM - turnos brutos enriquecidos con subtítulos de fotos proporcionados y texto de búsqueda de imágenes, indexados y recuperados de forma determinista.

LongMemEval_S (arXiv 2410.10813) división de pajar completo (~40-50 sesiones/pregunta), lector gpt-4o, prompt oficial de CoT y juez gpt-4o-2024-08-06:

MétricaPuntuación
General86.2%
Promedio por tarea86.8%
Abstención80.0%

El pajar completo pone a prueba la recuperación contra distractores (no el techo del lector oráculo). Protocolo y resultados por tipo en citadel-membench.

Motor de memoria cifrada

Las mismas páginas cifradas que contienen tablas SQL también contienen memoria. Tres crates componen el motor de memoria:

  • citadeldb-vector - un tipo SQL VECTOR(N), operadores de distancia (<-> L2, <#> producto interno, <=> coseno), y un índice ANN filtrado respaldado por PRISM que lee a través del almacén de páginas cifradas.
  • citadeldb-mem - el motor de memoria (regiones, átomos, aristas) con recuperación híbrida y olvido criptográfico: un átomo o región se borra destruyendo su clave, a granularidad de todo el almacén, por región y por átomo.
  • citadeldb-mcp - un servidor de Protocolo de Contexto de Modelo que expone una región de memoria de Citadel (cifrada por defecto) a cualquier cliente MCP (Claude Desktop, IDEs) como herramientas de recordar/recordar/enlazar/evolucionar/olvidar/verificar.

Ruta de memoria sin LLM

citadeldb-mem almacena contenido de conversación bruto sin un LLM resumidor. La recuperación usa embeddings, coincidencia de palabras clave BM25 y un reranker opcional. Los backends locales de embedding y re-ranqueo mantienen este procesamiento en el dispositivo; los backends personalizados determinan su propio uso de red y costos. Los lectores y jueces de benchmark son LLMs separados - gpt-4o-mini para LoCoMo, gpt-4o para LongMemEval. El protocolo y los resultados están en citadel-membench.

Runtime de agente

  • citadeldb-llm - la capa de cliente LLM neutral al proveedor (Claude, OpenAI, Ollama, Gemini) detrás de una sola fábrica, con hash canónico de solicitudes y una identidad de solicitud de cliente no secreta.
  • citadeldb-ai - un runtime de agente autónomo (ReAct + Reflexion, registro de herramientas, límites de presupuesto, backends LLM conectables) que usa citadeldb-mem para la persistencia.

Características

  • Cifrado en reposo - AES-256-CTR + HMAC-SHA256 por página, verificado antes del descifrado
  • SQL - JOINs, subconsultas, CTEs (recursivas + WITH-DML), UNION/INTERSECT/EXCEPT, funciones de ventana, vistas, vistas materializadas, triggers, tablas TEMP, columnas generadas (STORED + VIRTUAL), restricciones, acciones FK completas, UPSERT, RETURNING, JSON/JSONB (14 operadores de Postgres + lenguaje de ruta SQL/JSON), búsqueda de texto completo, sentencias preparadas con caché de planes y un catálogo de sistema consultable. Lista completa en SQL
  • ACID - Árbol B+ de copia en escritura, paginación de sombra, sin WAL. Aislamiento de instantáneas con lectores concurrentes
  • Ranuras de commit autenticadas - los metadatos de commit (raíces de tabla, catálogo) llevan su propio HMAC; los archivos más antiguos migran en una sola dirección a través de .upgrade
  • Sincronización P2P - Diferenciación de tablas basada en Merkle sobre canales cifrados con Noise y autenticación PSK
  • CLI - Shell SQL con completado de pestañas, resaltado de sintaxis, 27 comandos de punto (.backup, .verify, .upgrade, .rekey, .sync, .dump, ...)
  • Citadel Studio - Cliente de escritorio nativo para SQL, memoria almacenada, inspección de vectores y diagnóstico de bóveda
  • Jerarquía de claves de 3 niveles - Frase de contraseña -> Argon2id -> Clave maestra -> AES-KW -> REK -> HKDF -> DEK + MAC
  • Olvido criptográfico - Borrado de claves de todo el almacén y por región / por átomo a través de citadeldb-mem. Las copias de seguridad previas al borrado, las claves copiadas y el texto plano exportado quedan fuera de ese borrado
  • Perfil en reposo orientado a FIPS - PBKDF2-HMAC-SHA256 + AES-256-CTR para almacenamiento de bases de datos; no es una afirmación de validación de todo el producto
  • Registro de auditoría - HMAC-SHA256 encadenado dentro de archivos y entre generaciones v2 retenidas; la verificación del historial retenido detecta ediciones de registros y enlaces retenidos rotos, pero no hay un ancla externa contra la reversión
  • Copia de seguridad en caliente - Instantáneas consistentes a través de MVCC, sin bloqueo de escritura
  • Páginas de desbordamiento - Valores grandes manejados de forma transparente, hasta 1 GiB por valor
  • Multiplataforma - Windows, Linux, macOS. Enlaces de Python, C FFI y WebAssembly
  • Miles de pruebas - Pruebas unitarias, de integración y de tortura en todo el workspace

Benchmarks de velocidad

Medidos el 13, 20 y 27 de septiembre de 2026 (UTC) en un Intel Core i9-12900HX, Windows 11 Pro, Rust 1.98.0 y SQLite 3.51.3. Las ejecuciones usan un procesador lógico fijo, con durabilidad deshabilitada y ambas cachés configuradas para 4,096 páginas (aproximadamente 32 MiB). La mayoría de los casos usan 100K filas; los esquemas y operaciones varían como se indica a continuación.

Cada tiempo es la media aritmética de dos o cuatro medianas de muestra por ejecución, con 30 muestras por ejecución. Las proporciones usan tiempo de SQLite sin redondear / tiempo de Citadel: por encima de 1 significa que Citadel es más rápido, por debajo de 1 significa que Citadel es más lento. Por ejemplo, 0.5x significa que Citadel tarda el doble que SQLite.

Dieciséis comparaciones se actualizaron el 27 de septiembre en 0dbe126c: nueve casos de ejecución y siete lecturas repetidas en caché. Cada fila actualizada usa cuatro nuevas ejecuciones por motor en el orden Citadel/SQLite/SQLite/Citadel, luego SQLite/Citadel/Citadel/SQLite. Otras filas conservan sus mediciones del 13 o 20 de septiembre y las revisiones de fuente. Esta es una instantánea combinada, no una ejecución completa del conjunto en la revisión más reciente. Revisiones de fuente, configuraciones de ejecución, medianas, intervalos del 95% y deriva identifican cada fila.

Velocidad de ejecución

37 comparaciones de escrituras y lecturas que ejecutan cada iteración, incluyendo consultas con parámetros rotativos. Los reinicios de fixtures se excluyen a menos que la descripción del caso indique lo contrario.

Benchmark                     Citadel        SQLite         Ratio
----------------------------------------------------------------------
join_param                    2.6 us         51.2 us        19.7x
fts_rank_first_execution      6.86 ms        64.5 ms        9.4x
insert_returning              80.8 us        351 us         4.34x
update_returning              65.7 us        232 us         3.53x
window_agg                    33.6 ms        108 ms         3.2x
upsert_returning              124 us         363 us         2.94x
sort_paginate_pk              9.13 us        26.1 us        2.86x
delete_returning              97.2 us        269 us         2.76x
window_rank                   69.2 ms        182 ms         2.63x
fts_phrase                    5.66 ms        14.6 ms        2.58x
fts_match                     4.94 ms        12.1 ms        2.44x
json_extract                  22.6 ms        49 ms          2.17x
scan                          6.31 ms        13.2 ms        2.09x
insert                        25.5 us        51.8 us        2.03x
insert_gen_virtual            36.4 us        66.4 us        1.82x
wide_proj_full                6.83 ms        12.1 ms        1.77x
insert_gen_stored             37.3 us        65.7 us        1.76x
upsert_all_new                36.2 us        63.5 us        1.76x
wide_proj_pk                  416 us         728 us         1.75x
truncate                      57.6 us        101 us         1.75x
upsert_dedup                  31.9 us        53.7 us        1.68x
savepoint_rollback            2.08 ms        3.22 ms        1.55x
delete                        75.1 us        116 us         1.54x
wide_proj_2col                651 us         998 us         1.53x
covered_count                 377 us         561 us         1.49x
wide_proj_3col                1.28 ms        1.89 ms        1.47x
savepoint_nested              232 us         322 us         1.39x
update                        36.1 us        45.8 us        1.27x
insert_select                 171 us         214 us         1.25x
with_dml                      122 us         147 us         1.21x
fk_cascade_delete_only        52.4 us        63.2 us        1.2x
fk_cascade                    122 us         144 us         1.18x
savepoint_create              916 ns         1.07 us        1.16x
upsert_mixed                  56.1 us        64.7 us        1.15x
covered_range                 105 us         119 us         1.14x
update_gen_propagate          65.7 us        70.8 us        1.08x
upsert_counter                79.1 us        82.7 us        1.05x

Lecturas repetidas en caché

22 comparaciones de lecturas idénticas contra datos sin cambios. Citadel reutiliza resultados en caché; union reutiliza filas de rama proyectadas y reconstruye la salida de UNION ALL. SQLite ejecuta la consulta nuevamente. Estos tiempos no representan la primera consulta después de una escritura.

Benchmark                     Citadel        SQLite         Ratio
----------------------------------------------------------------------
correlated_in                 242 ns         2.78 s         11500000x
fts_rank                      534 ns         63.8 ms        120000x
correlated_exists             239 ns         9.61 ms        40200x
jsonb_contains                1.81 us        40.5 ms        22400x
sort_nocase                   446 ns         4.71 ms        10600x
cte                           1.46 us        9.29 ms        6350x
sort                          674 ns         4.03 ms        5990x
group_by                      2.47 us        14.5 ms        5860x
sum                           529 ns         2.74 ms        5180x
distinct                      1.82 us        5.94 ms        3260x
full_outer_join               25.4 us        31.7 ms        1250x
correlated_scalar             23.6 us        28.1 ms        1190x
recursive_cte                 267 ns         175 us         654x
partial_index_point           269 ns         22.6 us        84.1x
view_point                    300 ns         22.8 us        75.9x
point                         302 ns         22.6 us        74.9x
filter                        38.6 us        2.74 ms        70.9x
view_filter                   38.6 us        2.65 ms        68.8x
count                         855 ns         37.4 us        43.7x
select_gen_virtual            2.21 us        34.5 us        15.6x
join                          24.7 us        147 us         5.94x
union                         50.6 us        230 us         4.54x

Solo Citadel

No se reporta comparación con SQLite para estos siete casos. json_table ejecuta cada iteración; los otros seis miden lecturas repetidas en caché.

Benchmark                     Citadel        SQLite         Ratio
----------------------------------------------------------------------
json_table                    7.42 ms        -              -
lateral                       2.63 us        -              -
date_sort                     1.81 us        -              -
date_extract                  848 ns         -              -
date_groupby                  576 ns         -              -
date_arith                    260 ns         -              -
date_range_scan               257 ns         -              -

Comparaciones de índices

La misma consulta dentro de Citadel, con y sin su índice. Las proporciones son tiempo sin índice / con índice. json_gin rota sondas únicas de id JSON; fts_index repite una consulta fija en una columna TEXT. Ambos ejecutan cada iteración.

Benchmark                     Without index  With index     Ratio
----------------------------------------------------------------------
json_gin                      8.26 ms        5.77 us        1430x
fts_index                     1.95 s         4.76 ms        409x
Metodología

Las consultas exactas, esquemas, tamaños de entrada y límites de tiempo están en las implementaciones H2H. La configuración compartida de la base de datos y la recopilación de resultados están en common.rs.

  • SQLite usa page_size=8192, journal_mode=MEMORY, synchronous=OFF, cache_size=4096. Citadel usa SyncMode::Off y cache_size=4096; sus páginas almacenadas de 8,208 bytes contienen un cuerpo descifrado de 8,160 bytes. Los recuentos de entradas de caché coinciden, no el uso exacto de bytes. Estas ejecuciones no miden la latencia de confirmación duradera.
  • Las filas de resultados, incluida la salida RETURNING, se recopilan por completo. La mayoría de los casos de lectura reutilizan una declaración preparada. La creación del conjunto de datos está fuera del temporizador.
  • insert_select incluye crear la tabla de destino y copiar 1K filas en ella, cada una como una declaración de autocommit separada. Eliminarla está excluido.
  • fts_rank_first_execution usa una declaración preparada nueva en cada iteración; la preparación y la eliminación están excluidas. No es una medición de E/S en frío de disco. fts_rank reutiliza el resultado preparado. Citadel TS_RANK y SQLite BM25 son algoritmos de clasificación diferentes.
  • fk_cascade incluye insertar un padre y 100 hijos, confirmar y luego eliminar el padre. fk_cascade_delete_only mide solo la eliminación en cascada.
  • savepoint_create incluye BEGIN, SAVEPOINT, RELEASE y COMMIT. savepoint_nested crea diez savepoints anidados con 100 inserciones en cada nivel, revierte al sexto, libera los savepoints restantes y confirma. savepoint_rollback inserta 1K filas antes de un savepoint y 10K después, revierte estas últimas y confirma.
  • Criterion usa 30 muestras por brazo. Las cohortes del 13 de septiembre usan un calentamiento de 1 segundo y un objetivo de medición de 2 segundos; las cohortes del 20 y 27 de septiembre usan 3 y 8 segundos. Los casos lentos se ejecutan más tiempo para completar todas las muestras. Las ejecuciones son seriales en el procesador lógico 0, sin compilaciones concurrentes.
  • Las cohortes del 27 de septiembre usan una compilación fuente y ocho trabajos filtrados por motor: Citadel/SQLite/SQLite/Citadel, luego SQLite/Citadel/Citadel/SQLite. Cada una de las cuatro medianas por motor contribuye. Las cohortes anteriores de comparación de fuentes conservan sus ejecuciones candidatas registradas y los controles SQLite correspondientes.
  • Los intervalos de mediana del 95% por ejecución y la deriva cronológica se conservan en los datos. El rango o la deriva por encima del 5% se marcan para filas actualizadas; las ejecuciones marcadas permanecen incluidas. Los intervalos no se agrupan y las proporciones no establecen una aceleración universal ni miden el cambio desde una versión anterior.

Por ejemplo, vuelva a ejecutar los casos UPDATE actualizados en su revisión registrada con este filtro; reemplace citadel con sqlite para el otro motor:

cargo bench --locked -p citadeldb-sql --bench h2h_bench -- \
  '^(update|update_gen_propagate|update_returning)/citadel/$' \
  --sample-size 30 --warm-up-time 3 --measurement-time 8 --noplot

Conserve el ejecutable compilado, use un CRITERION_HOME nuevo por trabajo y ejecute el orden de ocho trabajos de motor registrado con afinidad de CPU fija. El comando anterior ejecuta el checkout actual; reproducir una fila requiere su revisión fuente registrada y cohorte. Los IDs exactos de Criterion, hashes de ejecutables, todas las medianas por ejecución, intervalos y hashes de evidencia están en sql-benchmarks.json.

SQL

Sentencias - CREATE/DROP TABLE (incl. TEMP), ALTER TABLE (ADD/DROP/RENAME COLUMN, RENAME TABLE, DISABLE/ENABLE TRIGGER), CREATE/DROP INDEX (incl. parcial WHERE, claves de expresión, CONCURRENTLY), REINDEX [DATABASE] / REINDEX [TABLE | INDEX] name, CREATE/DROP VIEW, CREATE/DROP MATERIALIZED VIEW (con REFRESH [CONCURRENTLY]), CREATE/DROP TRIGGER (BEFORE/AFTER/INSTEAD OF, FOR EACH ROW/STATEMENT, REFERENCING NEW/OLD TABLE, WHEN, UPDATE OF cols), INSERT (VALUES, SELECT, ON CONFLICT DO NOTHING/DO UPDATE, ON CONSTRAINT), SELECT, UPDATE (incluyendo subconsultas correlacionadas en SET), DELETE, TRUNCATE TABLE, RETURNING (con OLD/NEW), BEGIN [READ ONLY | READ WRITE]/COMMIT/ROLLBACK, SAVEPOINT/RELEASE/ROLLBACK TO, SET [LOCAL] TIME ZONE, EXPLAIN, REFRESH MATERIALIZED VIEW

Restricciones - PRIMARY KEY, NOT NULL, UNIQUE, DEFAULT, CHECK (nivel de columna + tabla), FOREIGN KEY con acciones referenciales completas (ON DELETE / ON UPDATE CASCADE / SET NULL / SET DEFAULT / RESTRICT / NO ACTION), GENERATED ALWAYS AS (...) STORED|VIRTUAL

Cotejamientos - BINARY, NOCASE (insensible a mayúsculas ASCII) y RTRIM (ignora espacios finales). Las claves primarias de texto usan su cotejamiento declarado; las claves foráneas usan el cotejamiento de las columnas referenciadas. Las claves de índice de columna heredan el cotejamiento de su columna a menos que se anulen con COLLATE. Las claves INTERVAL (primarias, únicas, foráneas e índice) comparan por longitud como lo hace =, por lo que '1 month' y '30 days' son una clave; REINDEX convierte columnas de intervalo almacenadas por versiones anteriores.

Tipos - INTEGER, REAL, TEXT, BLOB, BOOLEAN, DATE, TIME, TIMESTAMP (WITH TIME ZONE), INTERVAL, JSON, JSONB, TSVECTOR, TSQUERY, ARRAY

JSON / JSONB - Operadores de Postgres más funciones de ruta SQL/JSON y los métodos de elemento SQL:2023 .bigint(), .decimal(), .integer(), .number(), .string(), .boolean(), .date(), .time(), .time_tz(), .timestamp() y .timestamp_tz(). La evaluación dependiente de la zona horaria usa el contexto transaccional SET [LOCAL] TIME ZONE de la conexión.

Cláusulas - JOINs (INNER, LEFT, RIGHT, CROSS, FULL OUTER; LATERAL con CROSS/INNER/LEFT), subconsultas (escalares, IN, EXISTS, correlacionadas), CTEs (WITH / WITH RECURSIVE / WITH-DML: WITH x AS (INSERT/UPDATE/DELETE ... [RETURNING *]) SELECT ...), UNION/INTERSECT/EXCEPT [ALL], CASE, BETWEEN, LIKE, DISTINCT, ANY / ALL (formas de subconsulta + matriz), GROUP BY/HAVING, ORDER BY, LIMIT/OFFSET

Funciones de ventana - ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG, LEAD, FIRST_VALUE, LAST_VALUE, SUM/COUNT/AVG/MIN/MAX OVER con PARTITION BY, ORDER BY, marcos ROWS/RANGE. En consultas agrupadas, las funciones de ventana operan en los grupos restantes después de GROUP BY y HAVING.

Vistas - CREATE/DROP VIEW, OR REPLACE, IF NOT EXISTS/IF EXISTS, alias de columna, vistas anidadas

Vistas materializadas - CREATE MATERIALIZED VIEW [IF NOT EXISTS] name AS SELECT ..., REFRESH MATERIALIZED VIEW [CONCURRENTLY] name (CONCURRENTLY hace una fusión diferencial - DELETE filas eliminadas, UPDATE filas cambiadas, INSERT filas nuevas - en lugar de TRUNCATE+repoblar), DROP MATERIALIZED VIEW [CASCADE], semántica completa de tabla subyacente (índices, uniones, el planificador ve una tabla real), introspección pg_matviews

Disparadores - CREATE TRIGGER name {BEFORE|AFTER|INSTEAD OF} {INSERT|UPDATE [OF cols]|DELETE} ON table FOR EACH {ROW|STATEMENT} [REFERENCING NEW TABLE AS new_t OLD TABLE AS old_t] [WHEN (expr)] BEGIN ... END. Los disparadores INSTEAD OF hacen que las vistas sean escribibles. Las tablas de transición funcionan como tablas virtuales en los cuerpos de los disparadores. ALTER TABLE ... DISABLE/ENABLE TRIGGER [name|ALL]. Disparo en orden de nombre fiel a PG. Introspección a través de information_schema.triggers y SHOW TRIGGERS [ON table].

Tablas TEMP - CREATE TEMP TABLE ... vive en una base de datos en memoria por conexión, eliminada al desconectarse. Paridad completa de DDL/DML/índice/restricción/disparador con tablas persistentes.

Funciones - COUNT, SUM, AVG, MIN, MAX, LENGTH, UPPER, LOWER, SUBSTR/SUBSTRING, TRIM/LTRIM/RTRIM, REPLACE, INSTR, CONCAT, HEX, ABS, ROUND, CEIL/CEILING, FLOOR, SIGN, SQRT, RANDOM, COALESCE, NULLIF, GREATEST, LEAST, CAST, TYPEOF, IIF. Los agregados no de ventana admiten FILTER (WHERE ...).

Funciones de fecha/hora - NOW, CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME, LOCALTIMESTAMP, LOCALTIME, CLOCK_TIMESTAMP, EXTRACT, DATE_PART, DATE_TRUNC, DATE_BIN, AGE, MAKE_DATE, MAKE_TIME, MAKE_TIMESTAMP, MAKE_INTERVAL, JUSTIFY_DAYS, JUSTIFY_HOURS, JUSTIFY_INTERVAL, ISFINITE, DATE, TIME, DATETIME, STRFTIME, JULIANDAY, UNIXEPOCH, TIMEDIFF, AT TIME ZONE. Admite INTERVAL '1 year 2 months', DATE '2024-01-15', TIMESTAMP '2024-01-15 12:30:00Z', centinelas infinity/-infinity, fechas BC, análisis completo de zona IANA (jiff), comparación de INTERVAL normalizada por PG.

Búsqueda de texto completo - Tipos tsvector / tsquery, constructores to_tsvector / to_tsquery / plainto_tsquery / phraseto_tsquery / websearch_to_tsquery, operador de coincidencia @@, clasificación ts_rank / ts_rank_cd con posiciones ponderadas (A/B/C/D), coincidencia de prefijo (term:*), distancia de frase (<N>), índices invertidos a través de CREATE INDEX ... USING fts

Catálogo del sistema - information_schema.tables, information_schema.columns, information_schema.key_column_usage, information_schema.table_constraints, information_schema.triggers, pg_timezone_names, pg_timezone_abbrevs, pg_matviews (tablas virtuales, consultables). Abreviaturas SHOW TRIGGERS [ON table] y SHOW MATERIALIZED VIEWS para las consultas de catálogo correspondientes.

Declaraciones preparadas - Parámetros posicionales $1, $2, ... con caché de declaraciones LRU más caché de planes etiquetados con instantánea para uniones y consultas compuestas (la caché se invalida solo en confirmación, nunca por llamada)

Scripts de múltiples declaraciones - Connection::execute_script(sql) ejecuta declaraciones separadas por ; en una llamada, devolviendo resultados por declaración con éxito parcial preservado. WASM: db.run(sql) devuelve [{type, ...}, ...].

UPSERT - INSERT ... ON CONFLICT (cols) DO NOTHING / DO UPDATE SET col = excluded.col ... WHERE ... y ON CONFLICT ON CONSTRAINT idx_name. excluded.* se refiere a la fila propuesta; col desnudo se refiere a la fila existente.

Seguridad

Sin texto plano en disco. Cada página se cifra antes de escribir y se autentica antes de leer.

Archivo de clave separado. Las claves de cifrado viven en {dbname}.citadel-keys, no dentro de la base de datos. La frase de contraseña deriva una clave maestra en memoria a través de Argon2id (o PBKDF2 en el perfil en reposo orientado a FIPS) y nunca toca el disco.

Copia de seguridad de clave. Exporte una copia de seguridad de clave cifrada con una frase de contraseña de recuperación separada. Restaure el acceso sin volver a cifrar toda la base de datos.

Reclave instantáneo. Cambiar la frase de contraseña reenvuelve la clave de cifrado raíz. Sin re-cifrado de páginas - instantáneo independientemente del tamaño de la base de datos.

Sincronización cifrada. Protocolo Noise (NNpsk0_25519_ChaChaPoly_BLAKE2s) con una clave precompartida de 256 bits. Claves Curve25519 efímeras por sesión para secreto hacia adelante.

Arquitectura

Clients and bindings:
+---------------------------------------------+
|               citadel-studio                |  Memory, SQL, and vault client
+----------------------+----------------------+
|     citadel-cli      |    citadel-python    |  CLI, Python wheel
+----------------------+----------------------+
|     citadel-ffi      |     citadel-wasm     |  C FFI, WebAssembly
+----------------------+----------------------+

Agent layer:
+---------------------------------------------+
|                 citadel-ai                  |  Agent runtime (ReAct + Reflexion)
+---------------------------------------------+
|                 citadel-llm                 |  LLM clients: Claude, OpenAI, Ollama, Gemini
+---------------------------------------------+

Memory layer:
+---------------------------------------------+
|                 citadel-mcp                 |  MCP server for memory tools
+---------------------------------------------+
|                 citadel-mem                 |  Regions, atoms, recall, erasure
+---------------------------------------------+
|                citadel-vector               |  VECTOR(N) type + PRISM filtered ANN
+---------------------------------------------+

Encrypted database engine:
+----------------------+----------------------+
|     citadel-sql      |    sql-json-path     |  SQL frontend, SQL/JSON paths
+----------------------+----------------------+
|                   citadel                   |  Database API, builder, vault lifecycle
+-------------+--------------+----------------+
| citadel-txn | citadel-sync | citadel-crypto |  Transactions, replication, keys
+-------------+--------------+----------------+
|       citadel-buffer       |  citadel-page  |  Buffer pool (SIEVE), page codec
+----------------------------+----------------+
|                 citadel-io                  |  File I/O, fsync, io_uring
+---------------------------------------------+
|                citadel-core                 |  Types, errors, cancellation
+---------------------------------------------+

Evaluation harnesses:
+----------------------+----------------------+
|   citadel-membench   |     citadel-swe      |  Memory and agent benchmarks
+----------------------+----------------------+

Studio llama a las API de base de datos y SQL directamente y usa MemoryMaintenance para inspección y borrado de memoria almacenada. No necesita servidor MCP ni modelo de incrustación.

Diseño de página (8,208 bytes)

+----------+--------------------+----------+
|  IV 16B  |  Ciphertext 8160B  |  MAC 32B |
+----------+--------------------+----------+

IV aleatorio nuevo por página. HMAC verificado antes del descifrado.

Protocolo de confirmación

Paginación de sombra con un byte dios - un byte selecciona la ranura de confirmación activa. Confirmaciones atómicas sin WAL:

  1. Escribir páginas sucias en nuevas ubicaciones (CoW)
  2. Calcular hashes Merkle de abajo hacia arriba
  3. Actualizar la ranura de confirmación inactiva
  4. Cambiar el byte dios

SyncMode::Full vacía las páginas y la ranura de confirmación antes del cambio, luego vacía el selector antes de devolver éxito. Si ese vaciado final falla, la durabilidad de la confirmación es incierta y las escrituras posteriores devuelven Error::ReopenRequired. Cierre y reabra la base de datos antes de escribir nuevamente.

Límite de integridad

Lo que la maquinaria de integridad en reposo garantiza y no garantiza contra un atacante con acceso a archivos:

  • HMAC por página vincula (epoch, page_id, IV, ciphertext). Cualquier modificación de los bytes de una página se detecta antes del descifrado. No vincula la generación de confirmación: una imagen de página escrita válidamente en el pasado para el mismo (page_id, epoch) se verifica para siempre.
  • Ranuras de confirmación tienen dos formatos aceptados. Las ranuras V1 llevan un HMAC-SHA256 truncado sobre cada campo excepto el propio MAC; las ranuras heredadas llevan solo una suma de verificación sin clave sobre un prefijo. Las ranuras heredadas con suma de verificación válida permanecen legibles solo mientras no se registre un requisito V1. Una vez que ambas ranuras físicas son V1 válidas y la bóveda registra ese requisito unidireccional, cualquier ranura heredada con suma de verificación válida se rechaza como evidencia de degradación, y los escritores se niegan a crear una.
  • Reversión a un estado genuino anterior está fuera de este límite. Una ranura autenticada anterior más sus páginas coincidentes pueden pasar las verificaciones del archivo de datos; una instantánea internamente consistente anterior de todo el estado local de la bóveda, incluidos los datos, la clave y los archivos de auditoría retenidos, también pasa la autenticación local. Detectar frescura requiere un ancla externa - por ejemplo, almacene el txn_id de la última confirmación y la raíz Merkle fuera del alcance del atacante y compárelos después de abrir.

Enlaces de lenguaje

C / C++

Biblioteca estática o dinámica con citadel.h generado automáticamente (cbindgen). Los puntos de entrada exportados son a prueba de pánico.

#include "citadel.h"

int main(void) {
    struct CitadelDb *db = NULL;
    struct CitadelSqlConn *conn = NULL;
    struct CitadelSqlResult *result = NULL;
    citadel_error_t status = citadel_create(
        "my.db", (const uint8_t *)"secret", 6, NULL, &db);
    if (status != CITADEL_ERROR_T_OK) goto cleanup;

    status = citadel_sql_open(db, &conn);
    if (status != CITADEL_ERROR_T_OK) goto cleanup;
    status = citadel_sql_execute(conn, "SELECT 1 + 1 AS value;", &result);

cleanup:
    citadel_sql_result_free(result);
    citadel_sql_close(conn);
    citadel_close(db);
    return status == CITADEL_ERROR_T_OK ? 0 : 1;
}

WebAssembly

Instalar con npm install @citadeldb/wasm.

import init, { CitadelDb } from "@citadeldb/wasm";

await init();

const db = new CitadelDb("secret");
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);");
db.execute("INSERT INTO t (id, name) VALUES (1, 'Alice');");

const result = db.query("SELECT * FROM t;");
// { columns: ["id", "name"], rows: [[1, "Alice"]] }

db.put(new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]));
db.free();

Compilar el paquete npm: bash scripts/publish-wasm.sh

Python

Una rueda importable con el motor completo (SQL, vectores, memoria, tiempo de ejecución del agente) y stubs de tipos incluidos.

pip install citadeldb
import citadeldb

db = citadeldb.connect("my.db", key="secret", create=True)
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)")
db.execute("INSERT INTO t VALUES (1, 'Alice')")
db.query("SELECT * FROM t").to_dicts()
# [{'id': 1, 'name': 'Alice'}]

Compilación

Rust 1.95+.

git clone https://github.com/yp3y5akh0v/citadel.git
cd citadel
cargo build --release

Indicadores de características

IndicadorDescripción
audit-logRegistro de auditoría encadenado HMAC-SHA256 (predeterminado: activado); sin ancla externa contra reversión
fipsPerfil PBKDF2 + AES-256-CTR en reposo; no es validación de todo el producto
io-uringE/S asíncrona io_uring de Linux

Licencia

Apache-2.0