citadeldb 0.2.0

Citadel: encrypted-first embedded database engine
Documentation
<p align="center">
  <img src="https://raw.githubusercontent.com/yp3y5akh0v/citadel/master/.github/banner.png" alt="Citadel" width="600">
</p>

<h1 align="center">Citadel</h1>

<p align="center">An encrypted-first embedded database engine written in Rust.<br>Every page is encrypted at rest — encryption is not an afterthought, it's the foundation.</p>

## Features


- **Encrypted at rest** — AES-256-CTR or ChaCha20, HMAC-SHA256 per page, verified before decryption
- **Full SQL engine** — CREATE/DROP TABLE, SELECT with JOINs, subqueries, aggregates, indexes
- **ACID transactions** — Copy-on-Write B+ tree with shadow paging, no WAL. Snapshot isolation with concurrent readers
- **P2P sync** — Merkle tree diffing over Noise-encrypted transport with PSK authentication
- **Interactive CLI** — SQL shell with tab completion, syntax highlighting, 27 dot-commands
- **Key hierarchy** — Passphrase → Argon2id → Master Key → AES-KW → REK → HKDF → DEK + MAC
- **FIPS mode** — PBKDF2-HMAC-SHA256 + AES-256-CTR for compliance environments
- **Audit logging** — HMAC-SHA256 chained tamper-evident log
- **Hot backup** — Consistent snapshots via MVCC without blocking writes
- **Large values** — Overflow pages handle values up to any size transparently
- **Cross-platform** — Windows, Linux, macOS, and more. C FFI (37 functions) and WebAssembly bindings included
- **2,100+ tests** — Unit, integration, and torture tests across all crates

## Quick Start


### As a Library


```rust
use citadel::{DatabaseBuilder, Argon2Profile};
use citadel_sql::Connection;

// Create a new encrypted database
let db = DatabaseBuilder::new("my.db")
    .passphrase(b"secret")
    .argon2_profile(Argon2Profile::Interactive)
    .create()?;

// SQL
let mut 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 (default table)
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 database (no file I/O, useful for testing and WASM)
let mem_db = DatabaseBuilder::new("")
    .passphrase(b"secret")
    .create_in_memory()?;
```

### CLI


```bash
# Create and open a database

citadel --create my.db

# Run SQL interactively

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   │
└────┴───────┘

# Database operations

citadel> .backup mydb.bak          # Hot backup (non-blocking)
citadel> .verify                   # Integrity check
citadel> .stats                    # Tree depth, pages, Merkle root
citadel> .audit verify             # Verify HMAC chain
citadel> .rekey                    # Change passphrase
citadel> .compact clean.db         # Compact to new file
citadel> .dump users               # Export as SQL statements

# P2P encrypted sync

citadel> .keygen                   # Generate a 256-bit sync key
citadel> .listen 4248 <KEY>        # Terminal A: wait for peer
citadel> .sync 127.0.0.1:4248 <KEY> # Terminal B: push tables
```

## SQL Support


**Statements**: CREATE/DROP TABLE, CREATE/DROP INDEX, INSERT, SELECT, UPDATE, DELETE, BEGIN/COMMIT/ROLLBACK, EXPLAIN

**Types**: INTEGER, REAL, TEXT, BLOB, BOOLEAN

**Expressions**: JOINs (INNER, LEFT, RIGHT, CROSS), subqueries (scalar, IN, EXISTS), CASE, BETWEEN, LIKE, DISTINCT, GROUP BY/HAVING, ORDER BY, LIMIT/OFFSET

**Functions**: COUNT, SUM, AVG, MIN, MAX, LENGTH, UPPER, LOWER, SUBSTR, ABS, ROUND, COALESCE, NULLIF, CAST

**Parameters**: Positional binding with `$1, $2, ...` and LRU statement cache

## Security


**No plaintext on disk.** Every page is encrypted before writing and authenticated before reading. There is no "unencrypted mode" — the database file is always opaque.

**Separate key file.** Encryption keys live in `{dbname}.citadel-keys`, not inside the database. The passphrase never touches disk — it derives a master key in memory via Argon2id (or PBKDF2 in FIPS mode).

**Key backup.** Export an encrypted key backup with a separate recovery passphrase. If the original passphrase is lost, restore access without re-encrypting the database.

**Passphrase change.** Re-wraps the root encryption key with the new passphrase. No page re-encryption needed — instant regardless of database size.

**Encrypted sync.** P2P sync uses the Noise protocol (`NNpsk0_25519_ChaChaPoly_BLAKE2s`). Both peers must share a 256-bit pre-shared key. Each session generates ephemeral Curve25519 keys for forward secrecy. No data is sent in plaintext.

## Architecture


```
┌─────────────┬───────────────┬───────────────┐
│ citadel-cli  │  citadel-ffi  │ citadel-wasm  │  CLI, C FFI, WebAssembly
├─────────────┴───────────────┴───────────────┤
│                 citadel-sql                  │  SQL parser, planner, executor
├─────────────────────────────────────────────┤
│                  citadel                     │  Database API, builder, sync
├──────────────┬──────────────┬───────────────┤
│  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, constants
└─────────────────────────────────────────────┘
```

### Page Layout (8208 bytes on disk)


```
┌──────────┬────────────────────┬──────────┐
│  IV 16B  │  Ciphertext 8160B  │  MAC 32B │
└──────────┴────────────────────┴──────────┘
```

Every page gets a fresh random IV. HMAC is verified before decryption to prevent ciphertext manipulation.

### Commit Protocol


Shadow paging with a god byte — a single byte controls which of two commit slots is active. Commits are atomic without a write-ahead log:

1. Write dirty pages to new locations (CoW)
2. Compute Merkle hashes bottom-up
3. Update inactive commit slot
4. Flip god byte (single-byte atomic write)

## Language Bindings


### C / C++


The `citadel-ffi` crate builds as a static or dynamic library with an auto-generated `citadel.h` header (cbindgen). All functions are panic-safe.

```c
#include "citadel.h"


CitadelDb *db = NULL;
citadel_create("my.db", "secret", 6, &db);

// Key-value
CitadelWriteTxn *wtx = NULL;
citadel_write_begin(db, &wtx);
citadel_write_put(wtx, (const uint8_t*)"key", 3, (const uint8_t*)"val", 3);
citadel_write_commit(wtx);

// SQL
CitadelSqlConn *conn = NULL;
citadel_sql_open(db, &conn);
CitadelSqlResult *result = NULL;
citadel_sql_execute(conn, "SELECT * FROM users;", &result);

citadel_close(db);
```

### WebAssembly


The `citadel-wasm` crate provides a JavaScript API for browser and Node.js environments.

```js
import { CitadelDb } from "citadel-wasm";

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]));
```

Build with `wasm-pack build crates/citadel-wasm --target web`.

## Building


Requires Rust 1.75 or later.

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

### Feature Flags


| Flag | Description |
|------|-------------|
| `audit-log` | Tamper-evident HMAC-chained audit log (default: on) |
| `fips` | FIPS 140-3 mode: PBKDF2 + AES-256-CTR only |
| `io-uring` | Linux async I/O via io_uring |

## License


MIT OR Apache-2.0