Citadel
Memória criptografada local-first para agentes de IA com esquecimento criptográfico
Documentação
Resultados e configurações de memória histórica
Início rápido
Para memória semântica via MCP, instale uv e baixe o embedder local e o reranker opcional de cross-encoder:
uvx citadeldb-mcp pull e5-large
uvx citadeldb-mcp pull ms-marco-minilm
Defina CITADEL_KEY para sua frase-senha do cofre (export CITADEL_KEY="your-passphrase"
no macOS/Linux ou $env:CITADEL_KEY = "your-passphrase" no PowerShell) e então inicie:
uvx citadeldb-mcp --db memory.cdl --embedder e5-large --reranker ms-marco-minilm
O servidor se comunica via stdio. Veja MCP para configuração do cliente. Os downloads de modelos não precisam de chave do cofre; o serviço precisa.
Memória (Python)
Instale o pacote publicado com pip install citadeldb. Veja o
guia de source-build em Python e memória semântica.
Os embedders implementam embed_with_cancel(texts, cancel_token) e verificam cancelamento
entre lotes limitados. Os modelos locais Candle exigem o recurso de build candle-embed.
Memória (Rust)
Usa citadeldb e citadeldb-mem com o recurso candle-embed. Este exemplo
carrega e5-large e um reranker local de cross-encoder. Outros presets ou um
Embedder personalizado são suportados.
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 e chave-valor
Usa os crates citadeldb e citadeldb-sql — ou experimente SQL sem instalação no playground ao 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
Um cliente desktop nativo para Windows, macOS e Linux. Abra cofres criptografados, navegue por tabelas e memória, execute SQL com EXPLAIN e ANALYZE, e inspecione vetores e resultados de integridade.
Veja o guia do Studio para capturas de tela e instruções de build. Baixe o Citadel Studio para Windows, macOS ou Linux.
Frameworks de agentes
Os adaptadores implementam interfaces de armazenamento, sessão e recuperação específicas de cada framework. Cada um exige um embedder explícito. Veja o README do pacote para configuração, comportamento de busca e filtros suportados.
| Framework | Pacote | Implementa |
|---|---|---|
| LangGraph | citadeldb-langgraph | BaseStore |
| CrewAI | citadeldb-crewai | StorageBackend |
| OpenAI Agents SDK | citadeldb-openai-agents | Session |
| Google ADK | citadeldb-google-adk | BaseMemoryService |
| LlamaIndex | citadeldb-llamaindex | BasePydanticVectorStore |
| LangChain | citadeldb-langchain | VectorStore, BaseChatMessageHistory |
| Haystack | citadeldb-haystack | DocumentStore |
| Microsoft Agent Framework | citadeldb-ms-agent-framework | HistoryProvider, ContextProvider |
| Strands Agents | citadeldb-strands-agents | SessionRepository |
pip install citadeldb-langgraph
Um único banco de dados atende a todos os adaptadores na thread que o abriu, então o armazenamento
de longo prazo de um grafo e seus transcrições de sessão podem compartilhar um único arquivo criptografado. Veja packaging/ para o README
de cada pacote.
MCP
Sirva uma região de memória criptografada para o Claude Desktop ou qualquer cliente MCP. citadeldb-mcp está
publicado no PyPI e listado no registro oficial do MCP
como dev.citadeldb/mcp. Execute-o sem instalação via uvx.
Para a configuração recomendada de recall semântico, baixe o embedder e o reranker de cross-encoder uma vez:
uvx citadeldb-mcp pull e5-large
uvx citadeldb-mcp pull ms-marco-minilm
Os comandos de pull não precisam de chave do cofre. Antes de iniciar o servidor, defina CITADEL_KEY
para a frase-senha do cofre: use export CITADEL_KEY="your-passphrase" no macOS/Linux ou
$env:CITADEL_KEY = "your-passphrase" no PowerShell. Então execute:
uvx citadeldb-mcp --db memory.cdl --embedder e5-large --reranker ms-marco-minilm
--db, --embedder e CITADEL_KEY são obrigatórios ao servir. O reranker é opcional,
mas e5-large com ms-marco-minilm é a configuração usada nos benchmarks de memória.
Para instalar o executável, execute pip install citadeldb-mcp ou
cargo install citadeldb-mcp. Baixe os mesmos modelos com citadeldb-mcp pull e5-large e
citadeldb-mcp pull ms-marco-minilm, e então adicione-o ao 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 memória histórica
Os resultados registrados de LoCoMo e LongMemEval estão resumidos abaixo; suas configurações e limitações antecedem as mudanças atuais do mecanismo de memória. Comparações de SQL com SQLite não criptografado em 59 casos estão em Benchmarks de velocidade.
LoCoMo — leitor e juiz gpt-4o-mini com os prompts do harness, média de 3 execuções medidas em 18 de agosto de 2026:
| Métrica | Pontuação |
|---|---|
| Geral | 87,2% +/- 0,3 |
| Contexto completo, sem recuperação (relatado no artigo da Mem0, não reexecutado aqui) | 72,9% |
A recuperação é idêntica nas três execuções; a variação vem do não-determinismo do leitor e do juiz. Uma auditoria manual estima que ~6,4% das chaves de resposta do LoCoMo estão incorretas, então a precisão bruta deve ser interpretada com esse ruído de anotação em mente.
A memória é construída sem LLM — turnos brutos enriquecidos com legendas de fotos fornecidas e texto de busca de imagens, indexados e recuperados deterministicamente.
LongMemEval_S (arXiv 2410.10813) divisão full-haystack (~40-50 sessões/questão), leitor gpt-4o, prompt oficial de CoT e juiz gpt-4o-2024-08-06:
| Métrica | Pontuação |
|---|---|
| Geral | 86,2% |
| Média por tarefa | 86,8% |
| Abstenção | 80,0% |
Full-haystack estressa a recuperação contra distratores (não o teto do leitor oráculo). Protocolo e resultados por tipo em citadel-membench.
Mecanismo de memória criptografada
As mesmas páginas criptografadas que armazenam tabelas SQL também armazenam memória. Três crates compõem o mecanismo de memória:
- citadeldb-vector — um tipo SQL
VECTOR(N), operadores de distância (<->L2,<#>inner,<=>cosine) e um índice ANN filtrado com suporte a PRISM que lê através do armazenamento de páginas criptografadas. - citadeldb-mem — o mecanismo de memória (regiões, átomos, arestas) com recall híbrido e esquecimento criptográfico: um átomo ou região é apagado destruindo sua chave, em granularidade de armazenamento inteiro, por região e por átomo.
- citadeldb-mcp — um servidor Model Context Protocol que expõe uma região de memória do Citadel (criptografada por padrão) a qualquer cliente MCP (Claude Desktop, IDEs) como ferramentas recall/remember/link/evolve/forget/verify.
Caminho de memória sem LLM
citadeldb-mem armazena conteúdo bruto de conversa sem um LLM de sumarização. O recall usa embeddings, correspondência de palavras-chave BM25 e um reranker opcional. Backends locais de embedding e reranking mantêm esse processamento no dispositivo; backends personalizados determinam seu próprio uso de rede e custos. Os leitores e juízes de benchmark são LLMs separados — gpt-4o-mini para LoCoMo, gpt-4o para LongMemEval. O protocolo e os resultados estão em citadel-membench.
Runtime de agente
- citadeldb-llm — a camada de cliente LLM neutra em relação ao provedor (Claude, OpenAI, Ollama, Gemini) atrás de uma única factory, com hash canônico de requisições e uma identidade de requisição de cliente não secreta.
- citadeldb-ai — um runtime de agente autônomo (ReAct + Reflexion, registro de ferramentas, limites de orçamento, backends LLM plugáveis) que usa citadeldb-mem para persistência.
Recursos
- Criptografado em repouso — AES-256-CTR + HMAC-SHA256 por página, verificado antes da descriptografia
- SQL — JOINs, subconsultas, CTEs (recursivas + WITH-DML), UNION/INTERSECT/EXCEPT, funções de janela, views, materialized views, triggers, tabelas TEMP, colunas geradas (STORED + VIRTUAL), constraints, ações completas de FK, UPSERT, RETURNING, JSON/JSONB (14 operadores Postgres + linguagem de caminho SQL/JSON), busca em texto completo, prepared statements com cache de planos e um catálogo de sistema consultável. Lista completa em SQL
- ACID — Árvore B+ Copy-on-Write, shadow paging, sem WAL. Isolamento de snapshot com leitores concorrentes
- Slots de commit autenticados — os metadados de commit (raízes de tabela, catálogo) carregam seu próprio HMAC; arquivos mais antigos migram em uma direção via
.upgrade - Sincronização P2P — diff de tabelas baseado em Merkle em canais criptografados com Noise e autenticação PSK
- CLI — shell SQL com completamento de tab, realce de sintaxe, 27 dot-commands (.backup, .verify, .upgrade, .rekey, .sync, .dump, ...)
- Citadel Studio — cliente desktop nativo para SQL, memória armazenada, inspeção de vetores e diagnóstico de cofre
- Hierarquia de chaves em 3 níveis — Frase-senha -> Argon2id -> Master Key -> AES-KW -> REK -> HKDF -> DEK + MAC
- Esquecimento criptográfico — apagamento de chave em armazenamento inteiro e por região/átomo via citadeldb-mem. Backups pré-apagamento, chaves copiadas e texto exportado estão fora desse apagamento
- Perfil em repouso orientado a FIPS — PBKDF2-HMAC-SHA256 + AES-256-CTR para armazenamento de banco de dados; não é uma alegação de validação do produto inteiro
- Log de auditoria — HMAC-SHA256 encadeado dentro de arquivos e entre gerações v2 retidas; a verificação de histórico retido detecta edições de registros e links retidos quebrados, mas não há âncora externa anti-rollback
- Backup a quente — snapshots consistentes via MVCC, sem bloqueio de escrita
- Páginas de overflow — valores grandes tratados de forma transparente, até 1 GiB por valor
- Multiplataforma — Windows, Linux, macOS. Bindings para Python, C FFI e WebAssembly
- Milhares de testes — testes unitários, de integração e de tortura em todo o workspace
Benchmarks de velocidade
Medidos em 13, 20 e 27 de setembro de 2026 (UTC) em um Intel Core i9-12900HX, Windows 11 Pro, Rust 1.98.0 e SQLite 3.51.3. As execuções usam um processador lógico fixo, com durabilidade desabilitada e ambos os caches configurados para 4.096 páginas (cerca de 32 MiB). A maioria dos casos usa 100K linhas; esquemas e operações variam conforme listado abaixo.
Cada tempo é a média aritmética de duas ou quatro medianas de amostras por execução, com 30 amostras por execução. As proporções usam tempo SQLite não arredondado / tempo Citadel: acima de 1 significa que o Citadel é mais rápido, abaixo de 1 significa que o Citadel é mais lento. Por exemplo, 0,5x significa que o Citadel leva o dobro do tempo do SQLite.
Dezesseis comparações foram atualizadas em 27 de setembro em 0dbe126c: nove casos de execução e sete leituras repetidas em cache. Cada linha atualizada usa quatro novas execuções por mecanismo na ordem Citadel/SQLite/SQLite/Citadel, depois SQLite/Citadel/Citadel/SQLite. Outras linhas mantêm suas medições de 13 ou 20 de setembro e revisões de fonte. Esta é uma captura combinada, não uma execução completa da suíte na revisão mais recente. Revisões de fonte, configurações de execução, medianas, intervalos de 95% e deriva identificam cada linha.
Velocidade de execução
37 comparações de escritas e leituras que executam cada iteração, incluindo consultas com parâmetros rotativos. Reconfigurações de fixture são excluídas, a menos que a descrição do caso diga o contrário.
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
Leituras repetidas em cache
22 comparações de leituras idênticas contra dados inalterados. O Citadel reutiliza resultados em cache; union reutiliza linhas de branch projetadas e reconstrói a saída de UNION ALL. O SQLite executa a consulta novamente. Esses tempos não representam a primeira consulta após uma escrita.
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
Somente Citadel
Nenhuma comparação com SQLite é relatada para estes sete casos. json_table executa cada iteração; os outros seis medem leituras repetidas em cache.
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 - -
Comparações de índice
A mesma consulta dentro do Citadel, com e sem seu índice. As proporções são tempo não indexado / indexado. json_gin rotaciona sondas únicas de JSON-id; fts_index repete uma consulta fixa em uma coluna TEXT. Ambos executam cada iteração.
Benchmark Without index With index Ratio
----------------------------------------------------------------------
json_gin 8.26 ms 5.77 us 1430x
fts_index 1.95 s 4.76 ms 409x
Metodologia
Consultas exatas, esquemas, tamanhos de entrada e limites de tempo estão nas implementações H2H. Configurações compartilhadas de banco de dados e coleta de resultados estão em common.rs.
- SQLite usa
page_size=8192, journal_mode=MEMORY, synchronous=OFF, cache_size=4096. Citadel usaSyncMode::Offecache_size=4096; suas páginas armazenadas de 8.208 bytes contêm um corpo descriptografado de 8.160 bytes. As contagens de entradas de cache correspondem, não o uso exato de bytes. Essas execuções não medem a latência de commit durável. - Linhas de resultado, incluindo a saída RETURNING, são totalmente coletadas. A maioria dos casos de leitura reutiliza uma instrução preparada. A criação do conjunto de dados está fora do cronômetro.
insert_selectinclui criar a tabela de destino e copiar 1.000 linhas para ela, cada uma como uma instrução autocommit separada. Descartá-la é excluído.fts_rank_first_executionusa uma instrução preparada nova a cada iteração; preparação e descarte são excluídos. Não é uma medição de I/O com disco frio.fts_rankreutiliza o resultado preparado. Citadel TS_RANK e SQLite BM25 são algoritmos de classificação diferentes.fk_cascadeinclui inserir um pai e 100 filhos, confirmar e depois excluir o pai.fk_cascade_delete_onlycronometra apenas a exclusão em cascata.savepoint_createinclui BEGIN, SAVEPOINT, RELEASE e COMMIT.savepoint_nestedcria dez savepoints aninhados com 100 inserções em cada nível, reverte para o sexto, libera os savepoints restantes e confirma.savepoint_rollbackinsere 1.000 linhas antes de um savepoint e 10.000 depois, reverte o último e confirma.- Criterion usa 30 amostras por braço. As coortes de 13 de setembro usam um aquecimento de 1 segundo e uma meta de medição de 2 segundos; as coortes de 20 e 27 de setembro usam 3 e 8 segundos. Casos lentos executam por mais tempo para completar todas as amostras. As execuções são seriais no processador lógico 0, sem builds concorrentes.
- As coortes de 27 de setembro usam um build de origem e oito jobs filtrados por mecanismo: Citadel/SQLite/SQLite/Citadel, depois SQLite/Citadel/Citadel/SQLite. Cada uma das quatro medianas por mecanismo contribui. Coortes anteriores de comparação de origem mantêm suas execuções candidatas registradas e controles SQLite correspondentes.
- Intervalos de mediana de 95% por execução e deriva cronológica são retidos nos dados. Faixa ou deriva acima de 5% é sinalizada para linhas atualizadas; execuções sinalizadas permanecem incluídas. Intervalos não são agrupados, e proporções não estabelecem um aumento de velocidade universal ou medem mudança de uma versão anterior.
Por exemplo, reexecute os casos UPDATE atualizados em sua revisão registrada
com este filtro; substitua citadel por sqlite para o outro mecanismo:
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
Preserve o executável compilado, use um CRITERION_HOME novo por job e execute a
ordem registrada de oito jobs do mecanismo com afinidade de CPU fixa. O comando acima
executa o checkout atual; reproduzir uma linha requer sua revisão de origem registrada
e coorte. IDs exatos do Criterion, hashes de executáveis, todas as medianas por execução,
intervalos e hashes de evidência estão em
sql-benchmarks.json.
SQL
Instruções - CREATE/DROP TABLE (incl. TEMP), ALTER TABLE (ADD/DROP/RENAME COLUMN, RENAME TABLE, DISABLE/ENABLE TRIGGER), CREATE/DROP INDEX (incl. WHERE parcial, chaves de expressão, CONCURRENTLY), REINDEX [DATABASE] / REINDEX [TABLE | INDEX] name, CREATE/DROP VIEW, CREATE/DROP MATERIALIZED VIEW (com 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 (incluindo subconsultas correlacionadas em SET), DELETE, TRUNCATE TABLE, RETURNING (com OLD/NEW), BEGIN [READ ONLY | READ WRITE]/COMMIT/ROLLBACK, SAVEPOINT/RELEASE/ROLLBACK TO, SET [LOCAL] TIME ZONE, EXPLAIN, REFRESH MATERIALIZED VIEW
Restrições - PRIMARY KEY, NOT NULL, UNIQUE, DEFAULT, CHECK (nível de coluna + tabela), FOREIGN KEY com ações referenciais completas (ON DELETE / ON UPDATE CASCADE / SET NULL / SET DEFAULT / RESTRICT / NO ACTION), GENERATED ALWAYS AS (...) STORED|VIRTUAL
Collations - BINARY, NOCASE (insensível a maiúsculas/minúsculas ASCII) e RTRIM (ignora espaços finais). Chaves primárias de texto usam sua collation declarada; chaves estrangeiras usam a collation das colunas referenciadas. Chaves de índice de coluna herdam a collation de sua coluna, a menos que substituídas por COLLATE. Chaves INTERVAL (primárias, únicas, estrangeiras e de índice) comparam por comprimento como = faz, então '1 month' e '30 days' são uma chave; REINDEX converte colunas de intervalo armazenadas por versões anteriores.
Tipos - INTEGER, REAL, TEXT, BLOB, BOOLEAN, DATE, TIME, TIMESTAMP (WITH TIME ZONE), INTERVAL, JSON, JSONB, TSVECTOR, TSQUERY, ARRAY
JSON / JSONB - Operadores Postgres mais funções de caminho SQL/JSON e os métodos de item SQL:2023 .bigint(), .decimal(), .integer(), .number(), .string(), .boolean(), .date(), .time(), .time_tz(), .timestamp() e .timestamp_tz(). Avaliação dependente de fuso horário usa o contexto transacional SET [LOCAL] TIME ZONE da conexão.
Cláusulas - JOINs (INNER, LEFT, RIGHT, CROSS, FULL OUTER; LATERAL com 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 + array), GROUP BY/HAVING, ORDER BY, LIMIT/OFFSET
Funções de janela - ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG, LEAD, FIRST_VALUE, LAST_VALUE, SUM/COUNT/AVG/MIN/MAX OVER com PARTITION BY, ORDER BY, quadros ROWS/RANGE. Em consultas agrupadas, funções de janela operam nos grupos restantes após GROUP BY e HAVING.
Views - CREATE/DROP VIEW, OR REPLACE, IF NOT EXISTS/IF EXISTS, aliases de coluna, views aninhadas
Materialized views - CREATE MATERIALIZED VIEW [IF NOT EXISTS] name AS SELECT ..., REFRESH MATERIALIZED VIEW [CONCURRENTLY] name (CONCURRENTLY faz um diff-merge - DELETE linhas removidas, UPDATE linhas alteradas, INSERT novas linhas - em vez de TRUNCATE+repopular), DROP MATERIALIZED VIEW [CASCADE], semântica completa de tabela de apoio (índices, joins, planejador vê uma tabela real), introspecção pg_matviews
Triggers - 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. Triggers INSTEAD OF tornam views graváveis. Tabelas de transição funcionam como tabelas virtuais em corpos de trigger. ALTER TABLE ... DISABLE/ENABLE TRIGGER [name|ALL]. Disparo em ordem de nome fiel ao PG. Introspecção via information_schema.triggers e SHOW TRIGGERS [ON table].
Tabelas TEMP - CREATE TEMP TABLE ... vive em um banco de dados em memória por conexão, descartado na desconexão. Paridade total de DDL/DML/índice/restrição/trigger com tabelas persistentes.
Funções - 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. Agregados não-janela suportam FILTER (WHERE ...).
Funções de Data/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. Suporta INTERVAL '1 year 2 months', DATE '2024-01-15', TIMESTAMP '2024-01-15 12:30:00Z', sentinelas infinity/-infinity, datas BC, análise completa de zona IANA (jiff), comparação INTERVAL normalizada por PG.
Pesquisa de texto completo - Tipos tsvector / tsquery, construtores to_tsvector / to_tsquery / plainto_tsquery / phraseto_tsquery / websearch_to_tsquery, operador de correspondência @@, classificação ts_rank / ts_rank_cd com posições ponderadas (A/B/C/D), correspondência de prefixo (term:*), distância de frase (<N>), índices invertidos via CREATE INDEX ... USING fts
Catálogo do 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 (tabelas virtuais, consultáveis). Atalhos SHOW TRIGGERS [ON table] e SHOW MATERIALIZED VIEWS para as consultas de catálogo correspondentes.
Instruções preparadas - Parâmetros posicionais $1, $2, ... com cache de instruções LRU mais cache de plano com tag de snapshot para joins e consultas compostas (cache invalida apenas no commit, nunca por chamada)
Scripts de múltiplas instruções - Connection::execute_script(sql) executa instruções separadas por ; em uma chamada, retornando resultados por instrução com sucesso parcial preservado. WASM: db.run(sql) retorna [{type, ...}, ...].
UPSERT - INSERT ... ON CONFLICT (cols) DO NOTHING / DO UPDATE SET col = excluded.col ... WHERE ... e ON CONFLICT ON CONSTRAINT idx_name. excluded.* refere-se à linha proposta; col puro refere-se à linha existente.
Segurança
Sem texto simples em disco. Cada página é criptografada antes de gravar e autenticada antes de ler.
Arquivo de chave separado. Chaves de criptografia vivem em {dbname}.citadel-keys, não dentro do banco de dados. A frase secreta deriva uma chave mestre em memória via Argon2id (ou PBKDF2 no perfil em repouso orientado a FIPS) e nunca toca o disco.
Backup de chave. Exporte um backup de chave criptografado com uma frase secreta de recuperação separada. Restaure o acesso sem re-criptografar todo o banco de dados.
Rekey instantâneo. Alterar a frase secreta re-embrulha a chave de criptografia raiz. Sem re-criptografia de página - instantâneo independentemente do tamanho do banco de dados.
Sincronização criptografada. Protocolo Noise (NNpsk0_25519_ChaChaPoly_BLAKE2s) com uma chave pré-compartilhada de 256 bits. Chaves Curve25519 efêmeras por sessão para sigilo de encaminhamento.
Arquitetura
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 chama as APIs de banco de dados e SQL diretamente e usa MemoryMaintenance para
inspeção e apagamento de memória armazenada. Não precisa de servidor MCP ou modelo de incorporação.
Layout de Página (8.208 bytes)
+----------+--------------------+----------+
| IV 16B | Ciphertext 8160B | MAC 32B |
+----------+--------------------+----------+
IV aleatório novo por página. HMAC verificado antes da descriptografia.
Protocolo de Commit
Shadow paging com um god byte - um byte seleciona o slot de commit ativo. Commits atômicos sem WAL:
- Grave páginas sujas em novos locais (CoW)
- Calcule hashes Merkle de baixo para cima
- Atualize o slot de commit inativo
- Inverta o god byte
SyncMode::Full libera as páginas e o slot de commit antes da inversão, depois libera
o seletor antes de retornar sucesso. Se essa liberação final falhar, a
durabilidade do commit é incerta e gravações adicionais retornam Error::ReopenRequired.
Feche e reabra o banco de dados antes de gravar novamente.
Limite de Integridade
O que a maquinaria de integridade em repouso garante e não garante contra um atacante com acesso a arquivos:
- HMAC por página vincula
(epoch, page_id, IV, ciphertext). Qualquer modificação dos bytes de uma página é detectada antes da descriptografia. Não vincula a geração de commit: uma imagem de página gravada validamente no passado para o mesmo(page_id, epoch)verifica para sempre. - Slots de commit têm dois formatos aceitos. Slots V1 carregam um HMAC-SHA256 truncado sobre todos os campos exceto o próprio MAC; slots legados carregam apenas uma soma de verificação sem chave sobre um prefixo. Slots legados com soma de verificação válida permanecem legíveis apenas enquanto nenhum requisito V1 for registrado. Uma vez que ambos os slots físicos são V1 válidos e o cofre registra esse requisito unidirecional, qualquer slot legado com soma de verificação válida é rejeitado como evidência de downgrade, e escritores se recusam a criar um.
- Rollback para um estado genuíno mais antigo está fora deste limite. Um slot autenticado anterior mais suas páginas correspondentes podem passar nas verificações do arquivo de dados; um snapshot internamente consistente mais antigo de todo o estado local do cofre, incluindo dados, chave e arquivos de auditoria retidos, também passa na autenticação local. Detectar frescor requer uma âncora externa - por exemplo, armazene o
txn_iddo commit mais recente e a raiz Merkle fora do alcance do atacante e compare-os após abrir.
Bindings de Linguagem
C / C++
Biblioteca estática ou dinâmica com citadel.h gerado automaticamente (cbindgen). Pontos de entrada exportados são seguros contra 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
Instale com 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();
Compile o pacote npm: bash scripts/publish-wasm.sh
Python
Uma wheel importável com o mecanismo completo (SQL, vetores, memória, runtime de agente) e stubs de tipo incluídos.
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'}]
Compilação
Rust 1.95+.
git clone https://github.com/yp3y5akh0v/citadel.git
cd citadel
cargo build --release
Flags de Recursos
| Flag | Descrição |
|---|---|
audit-log | Log de auditoria encadeado HMAC-SHA256 (padrão: ativado); sem âncora externa anti-rollback |
fips | Perfil PBKDF2 + AES-256-CTR em repouso; não é validação do produto inteiro |
io-uring | I/O assíncrono io_uring do Linux |