42 lines
2.8 KiB
Markdown
42 lines
2.8 KiB
Markdown
# ADR-0003: chronogrid_core – SQLCipher 4.x Database Initializer
|
||
|
||
- **Date**: 2026-08-19
|
||
- **Status**: Accepted
|
||
- **Task**: 1.3
|
||
|
||
## Context
|
||
|
||
ChronoGrid requires an encrypted local-first database for storing namespaces, projects, tasks, notes, links, and ingestion logs. The schema is defined in `docs/API_CONTRACTS/schema.sql` and mandates SQLCipher 4.x (AES-256-CBC) per `docs/ARCHITECTURE/02-security-and-crypto.md`.
|
||
|
||
## Decision
|
||
|
||
### Dependency Choice
|
||
|
||
Adopted `rusqlite` v0.31 with the `bundled-sqlcipher-vendored-openssl` feature. This bundles both the SQLCipher C library and OpenSSL, producing a hermetic build that requires no external system libraries beyond a C compiler and Perl (for the OpenSSL build step).
|
||
|
||
### Key Handling
|
||
|
||
The 32-byte master key derived by `chronogrid_crypto::kdf::derive_master_key` (Argon2id) is passed as a **raw hex key** via `PRAGMA key = "x'…'"`. This instructs SQLCipher to use the key material directly, bypassing its internal PBKDF2-HMAC-SHA512 derivation. Rationale: Argon2id is a strictly stronger KDF than PBKDF2; double-derivation would add latency without meaningful security improvement.
|
||
|
||
### PRAGMA Configuration
|
||
|
||
| PRAGMA | Value | Purpose |
|
||
|----------------------------|-------|-------------------------------------------|
|
||
| `key` | Raw hex (32 bytes) | Encrypt/decrypt the database |
|
||
| `cipher_compatibility` | `4` | Enforce SQLCipher 4.x defaults |
|
||
| `foreign_keys` | `ON` | Required by schema FK constraints |
|
||
|
||
### Schema Embedding
|
||
|
||
The canonical schema SQL is embedded at compile time via `include_str!("../../../docs/API_CONTRACTS/schema.sql")`, ensuring the crate always stays in sync with the single source of truth without runtime file I/O.
|
||
|
||
### Post-Init Validation
|
||
|
||
After running the DDL, the initializer queries `sqlite_master` to verify all 6 expected tables (`namespaces`, `projects`, `tasks`, `notes`, `task_note_links`, `ingestion_logs`) and 4 indexes exist. A quick `SELECT count(*) FROM sqlite_master` immediately after keying detects wrong-key or corrupt-file conditions early.
|
||
|
||
## Consequences
|
||
|
||
- **Positive**: Fully encrypted at-rest storage with zero cloud dependencies. Schema changes in `schema.sql` are automatically picked up on recompilation. 11 unit tests validate correctness.
|
||
- **Negative**: First build requires Perl (Strawberry Perl on Windows) for OpenSSL compilation. Build time is ~10 minutes on first compile due to OpenSSL/SQLCipher C compilation (subsequent builds are cached).
|
||
- **Trade-off**: Using raw key (bypassing SQLCipher PBKDF2) means the security of the database encryption relies entirely on the Argon2id KDF strength, which is acceptable given our pinned parameters (64 MiB, 3 iterations, 4 threads).
|