tftsr-devops_investigation/docs/architecture/adrs/ADR-002-sqlcipher-encrypted-database.md
Shaun Arman fdb4fc03b9 docs(architecture): add C4 diagrams, ADRs, and architecture overview
Comprehensive architecture documentation covering:

- docs/architecture/README.md: Full C4 model diagrams (system context,
  container, component), data flow sequences, security architecture,
  AI provider class diagram, CI/CD pipeline, and deployment diagrams.
  All diagrams use Mermaid for version-controlled diagram-as-code.

- docs/architecture/adrs/ADR-001: Tauri vs Electron decision rationale
- docs/architecture/adrs/ADR-002: SQLCipher encryption choices and
  cipher_page_size=16384 rationale for Apple Silicon
- docs/architecture/adrs/ADR-003: Provider trait + factory pattern
- docs/architecture/adrs/ADR-004: Regex + Aho-Corasick PII detection
- docs/architecture/adrs/ADR-005: Auto-generate encryption keys at
  runtime (documents the fix from PR #24)
- docs/architecture/adrs/ADR-006: Zustand state management rationale

- docs/wiki/Architecture.md: Updated module table (14 migrations, not
  10), corrected integrations description, updated startup sequence to
  reflect key auto-generation, added links to new ADR docs.

- README.md: Fixed stale database paths (tftsr → trcaa) and updated
  env var descriptions to reflect auto-generation behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 09:35:35 -05:00

2.8 KiB

ADR-002: SQLCipher for Encrypted Storage

Status: Accepted Date: 2025-Q3 Deciders: sarman


Context

All incident data (titles, descriptions, log contents, AI conversations, resolution steps, RCA documents) must be stored locally and at rest must be encrypted. The application cannot rely on OS-level full-disk encryption being enabled.

Requirements:

  • AES-256 encryption of the full database file
  • Key derivation suitable for per-installation keys (not user passwords)
  • No plaintext data accessible if the .db file is copied off-machine
  • Rust-compatible SQLite bindings

Decision

Use SQLCipher via rusqlite with the bundled-sqlcipher-vendored-openssl feature flag.


Rationale

Alternatives considered:

Option Pros Cons
SQLCipher (chosen) Transparent full-DB encryption, AES-256, PBKDF2 key derivation, vendored so no system dep Larger binary; not standard SQLite
Plain SQLite Simple, well-known No encryption — ruled out
SQLite + file-level encryption Flexible No atomicity; complex implementation
LevelDB / RocksDB Fast, encrypted options exist No SQL, harder migration
sled (Rust-native) Modern, async-friendly No SQL, immature for complex schemas

SQLCipher specifics chosen:

PRAGMA cipher_page_size = 16384;     -- Matches 16KB kernel page (Apple Silicon)
PRAGMA kdf_iter = 256000;            -- 256k PBKDF2 iterations
PRAGMA cipher_hmac_algorithm = HMAC_SHA512;
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;

The cipher_page_size = 16384 is specifically tuned for Apple Silicon (M-series) which uses 16KB kernel pages — using 4096 (SQLCipher default) causes page boundary issues.


Key Management

Per ADR-005, encryption keys are auto-generated at runtime:

  • Release builds: Random 256-bit key generated at first launch, stored in .dbkey (mode 0600)
  • Debug builds: Hardcoded dev key (dev-key-change-in-prod)
  • Override: TFTSR_DB_KEY environment variable

Consequences

Positive:

  • Full database encryption transparent to all SQL queries
  • Vendored OpenSSL means no system library dependency (important for portable AppImage/DMG)
  • SHA-512 HMAC provides authenticated encryption (tampering detected)

Negative:

  • bundled-sqlcipher-vendored-openssl significantly increases compile time and binary size
  • Cannot use standard SQLite tooling to inspect database files (must use sqlcipher CLI)
  • cipher_page_size mismatch between debug/release would corrupt databases — mitigated by auto-migration (ADR-005)

Migration Handling: If a plain SQLite database is detected in a release build (e.g., developer switched from debug), migrate_plain_to_encrypted() automatically migrates using ATTACH DATABASE + sqlcipher_export. A .db.plain-backup file is created before migration.