---
last_verified: 2026-06-01
verified_version: 0.1.0
owner: infra
freshness_days: 30
---

# Technology Stack — pvtcoms

> Version-pinned v1 stack. Versions verified against crates.io / official release pages on **2026-05-30** (Codex + Gemini +
> web research; Claude final call). Pre-code — these are the **target pins** for the first `Cargo.toml`. Re-check before the
> first commit and run `dependency-governance` once `Cargo.toml` exists. Rationale: [`DESIGN.md`](./DESIGN.md) §14.

## Toolchain (pin exactly)
| Tool | Version | Note |
|---|---|---|
| Rust (rustc/cargo) | **1.96.0** | `rust-toolchain.toml`; arti + ML-KEM are MSRV-sensitive |
| Kotlin | **2.3.20** | NOT 2.4.0 (still RC) — must match the Compose Compiler it ships with |
| Jetpack Compose BOM | **2026.05.00** | → Compose 1.11.1; BOM-pin all Compose artifacts |
| Android Gradle Plugin | **9.2.0** | requires JDK 17+; AGP 10 lands mid-2026 (plan migration) |
| Android NDK | **r27 (LTS)** | conservative over r29 (non-LTS) for a long-lived crypto core |
| JDK | **21 (LTS)**; 17 min | 17 is AGP-9 minimum; 21 for build perf |
| cargo-ndk | **4.1.2** | Rust→Android ABI cross-build |
| Tauri | **2.11.2** | desktop; lock core+CLI+plugins as one set |
| Nix (flakes) | **2.31.3** | reproducible dev/CI/build env (document the `nix-command flakes` flag) |

## Crypto crates
| Crate | Version | Decision |
|---|---|---|
| **libcrux-ml-kem** (Cryspen) | **0.0.9** | ✅ **PRIMARY ML-KEM-768 — WIRED 2026-06-01** behind `core::pqkem` (`default-features=false`, `mlkem768` only). Formally verified (F*/hax: panic-free, correct, secret-independent), KyberSlash-clean. Pre-0.1 → **pinned exact**, expect API churn |
| ml-kem (RustCrypto) | 0.3.2 | **dev-dependency only** — the differential **FIPS-203 KAT oracle** in `pqkem.rs` (must agree byte-for-byte with libcrux); not in the shipped artifact |
| ml-dsa (RustCrypto) | 0.1.0 | **NOT in v1** — RUSTSEC-2025-0144 fixed in ≥0.1.0-rc.3, but barely-1.0/unaudited; use Ed25519 for identity, add PQ signatures later if needed |
| x25519-dalek | 2.0.1 | classical half of hybrid KEM (avoid 3.0.0-rc) |
| ed25519-dalek | 2.2.0 | identity/prekey signatures (avoid 3.0.0-rc) |
| curve25519-dalek | **≥4.1.3** | **never <4.1.3** (RUSTSEC-2024-0344 timing fix); avoid 5.0.0-rc |
| chacha20poly1305 | 0.10.1 | AEAD (mobile-friendly, no AES-HW dependency); avoid 0.11.0-rc |
| hkdf / sha2 / hmac | 0.13.0 / 0.11.0 / 0.13.0 | **keep this cohort together** (same RustCrypto trait generation) |
| blake3 | 1.8.5 | optional — mailbox-token hashing/content addressing |
| zeroize / secrecy / subtle | 1.8.2 / 0.10.3 / 2.6.1 | **mandatory hygiene** — wipe keys, wrap long-lived secrets (+ SQLCipher passphrase), constant-time tag/MAC compares (never `==`) |
| rand / rand_core | 0.10.1 | ⚠️ rand_core 0.10 changed the API — **confirm dalek/libcrux accept it**; a rand_core mismatch is the likeliest build break (may stay a generation back) |

**Hybrid KEM combiner:** `K = HKDF-Extract(0, ss_x25519 ‖ ss_mlkem768)`, then context-separated expand (`info = "pvtcoms/v1/…"`). Require **both** secrets; policy-gated degrade only for migration/testing.

## Runtime / serialization / storage / FFI
| Component | Version | Decision |
|---|---|---|
| tokio | 1.52.3 | async runtime |
| arti-client (Tor) | **0.42.0** | ⚠️ **riskiest dep** — exact-pin, isolate behind the `AnonTransport` trait, heavily integration-test the adapter |
| serde | 1.0.228 | safe itself (risk is in the format crate, not serde) |
| **Wire format (untrusted bytes)** | — | ✅ **hand-written, strictly length/depth-bounded, versioned TLS-style parser** — heavily fuzzed. (Gemini: don't run serde-derive on hostile network bytes.) |
| postcard | 1.1.3 | only for **trusted local/internal** structs, not the wire |
| ~~bincode~~ | — | ❌ **AVOID** — 3.0.0 is an abandoned tombstone (compile-error stub) |
| rusqlite | 0.40.0 | feature `bundled-sqlcipher-vendored-openssl` (self-contained SQLCipher). **No standalone `sqlcipher` crate exists.** Lock PRAGMAs (KDF iter, page size, cipher) + migration tests |
| sqlx | 0.9.0 | not for v1 (SQLCipher key handling awkward) |
| uniffi (Mozilla) | 0.31.1 | Rust↔Kotlin/Swift FFI (proc-macro mode); pre-1.0 → keep FFI surface thin + versioned |
| thiserror / anyhow | 2.0.18 / 1.0.102 | thiserror in libs, anyhow at the app edge only |
| tracing | 0.1.44 | **redaction layer + secret denylist** (message, ratchet_state, keys, tokens, IPs); gate spans behind a debug flag |
| qrcodegen | 1.8.0 | Nayuki's audited QR encoder (pick one encoder) |
| tauri-plugin-deep-link | 2.4.9 | desktop invite links; version-locked with tauri 2.11.2 |

## UI
- **Android (flagship): Jetpack Compose** — fully native, no webview. This is where high-threat users live, so the webview question below doesn't touch them.
- **Desktop v1: Tauri 2, HARDENED** — render all message content as **plain text only** (never HTML/`innerHTML`), **strict CSP** (local assets only; no remote loads, no `eval`/inline), no network in the webview; the Rust core holds all secrets/keys. *Gemini's dissent recorded:* a system webview is a larger attack surface than a native toolkit. **Fallback evaluated at M4: Slint** (Rust-native, desktop-production-ready, AccessKit a11y, GPL-compatible). Re-evaluate at the external audit; this may become an ADR.

## Test / build tooling
proptest **1.11.0** · cargo-fuzz **0.13.1** (nightly) · cargo-nextest **0.9.137** · cargo-mutants **27.0.0** · cargo-deny
**0.19.8** (policy gate) · cargo-audit **0.22.1** (advisory scan) · miri (nightly) · loom **0.7.2** (scope to lock-free
mailbox/atomics only — slow/under-maintained) · cargo-llvm-cov **0.8.7**. cargo-vet **0.10.2** — **defer** (0.x, high audit-set
maintenance); rely on cargo-deny + cargo-audit + vendoring first.

## Versioning / supply-chain policy
- **Exact-pin** (`=x.y.z`) for `arti*`, the PQ crates, and the DB bindings; minor-pin stable ecosystem crates.
- **Commit `Cargo.lock`**; bumps only via reviewed PR that re-runs `cargo audit` + Tor integration + replay tests.
- **`cargo vendor`** for hermetic, offline, auditable builds; review every `build.rs`/proc-macro dep (the xz-style vector).
- `cargo-deny` license allowlist (MIT/Apache-2.0/BSD/ISC/Zlib/Unicode/MPL-2.0; deny GPLv2-only/proprietary — AGPL inbound-compatible).
- **Single riskiest dependency: `arti-client`** (security-critical + pre-1.0 API churn + network complexity). **Most important control: cargo-vet/vendoring + reproducible builds** so the binary is provably the audited source.

## Upgrade policy
Security patches immediately; minor monthly; major with planning + crypto/audit re-check. Crypto-primitive or transport
changes require an ADR. Watch: dalek 3.x / rand 0.10 realignment, arti onion-service API, AGP 10, Compose/compileSdk bumps.
