# ADR-003: Concrete stack & key dependency decisions

## Status

Accepted

## Date

2026-05-30

## Context

Locked the concrete, version-pinned v1 stack (Codex + Gemini + web research, versions verified on crates.io 2026-05-30;
Claude final call). Full pins in [`STACK.md`](../../STACK.md). Three choices were contested and are recorded here.

## Decisions

1. **PQ KEM = `libcrux-ml-kem` (Cryspen, formally verified) as primary** for ML-KEM-768, RustCrypto `ml-kem` behind a feature
   flag for cross-check. *Why:* high-threat threat model prioritizes constant-time + auditability; libcrux is F*/hax-verified
   (panic-free, correct, secret-independent), KyberSlash-clean. *Cost:* pre-0.1 API churn → pin exact + vendor. ML-DSA (PQ
   signatures) **not in v1** (use Ed25519; RUSTSEC-2025-0144 is fixed but the crate is barely-1.0/unaudited).
   > **Implemented 2026-06-01:** libcrux-ml-kem 0.0.9 is wired behind the `core::pqkem` facade (replacing RustCrypto ml-kem,
   > which is now a **dev-dependency**). The "cross-check" is a **differential FIPS-203 KAT** (`pqkem::tests`): for the same
   > public key + deterministic coins, libcrux and RustCrypto must produce **byte-identical** ciphertext + shared secret —
   > verified passing. The handshake wire format is unchanged (identical FIPS-203 encodings), so interop is preserved.
2. **Wire format = hand-written, strictly length/depth-bounded, versioned TLS-style parser** for untrusted bytes (heavily
   fuzzed); `postcard` only for trusted local data. *Why:* serde-derive + `bincode`/`postcard` on hostile network bytes risks
   DoS/OOM/stack-overflow (Gemini). `bincode` 3.0 is also an abandoned tombstone.
3. **Desktop UI = Tauri 2, hardened** (plain-text-only rendering, strict CSP, deny-by-default IPC, no remote/SVG, secrets in
   Rust only) for v1, **with Slint (Rust-native) evaluated as the fallback at M4.** *Why:* both advisors + Bishop Fox's
   "XSS→RCE in Tauri" research rank the webview the #1 attack surface. Android (the flagship for high-threat users) is native
   Compose with **no webview**, which lowers the stakes; desktop is post-Android. May become its own ADR after the audit.

Other locked pins (see STACK.md): rustc 1.96, tokio 1.52, arti-client 0.42 (riskiest dep — isolate behind `AnonTransport`),
x25519/ed25519-dalek 2.x, curve25519-dalek ≥4.1.3, chacha20poly1305 0.10.1, hkdf/sha2/hmac (0.13/0.11/0.13 cohort),
zeroize/secrecy/subtle, rusqlite 0.40 + bundled SQLCipher, uniffi 0.31, Compose BOM 2026.05.00 / Kotlin 2.3.20 / AGP 9.2 /
NDK r27 / JDK 21, Nix flakes. Pinning: exact for arti*/PQ/DB, `Cargo.lock` committed, `cargo vendor` + `cargo-deny`/`cargo-audit`.

## Consequences

### Positive
- Strongest available crypto assurance (verified KEM); smaller wire-parser attack surface; webview risk contained + a native exit.

### Negative
- libcrux pre-0.1 churn; a hand-written parser is more code to get right (mitigated by fuzzing); Tauri hardening is mandatory work.

### Neutral
- The dalek 3.x / rand 0.10 ecosystem realignment and a possible desktop switch to Slint are tracked watch-items.
