Building a Desktop App with Tauri v2 — A 3MB Secret Manager
Why we chose Tauri v2 over Electron, and how we implemented AES-256 encryption with a Rust backend.
Why Tauri
Electron is great, but it was overkill for a secret manager. Bundling the Chromium runtime pushes installer size past 150 MB. Asking users to download 150 MB just to store a few API keys doesn’t make sense.
Tauri v2 uses the system WebView. As a result, the KeyBox installer comes in at 3 MB.
Comparing Candidates
Three options were on the table.
| Candidate | Installer | Pros | Cons |
|---|---|---|---|
| Electron | ~150 MB | Massive ecosystem, battle-tested | Heavy |
| Tauri v2 | ~3 MB | Lightweight, safer, Rust backend | Smaller plugin ecosystem |
| Native (C# WPF) | ~50 MB | Deep OS integration | Windows-only, steep learning curve |
For an offline security tool, installer size acts as a trust signal. When a user clicks download, would they feel safer with 3 MB or 150 MB?
Stack
- Frontend: React + TypeScript — a familiar stack to ship UI quickly
- Backend: Rust — for a security-critical app, memory safety isn’t optional
- Crypto: AES-256-GCM — keys are derived from a master password (PBKDF2, 100,000 iterations)
- Storage: SQLite — local-only, no network needed
What’s New in Tauri v2
The biggest change moving from v1 to v2 is the plugin system. Previously, you defined Rust commands directly. In v2, you can structure them as plugins.
// Tauri v2 plugin pattern
#[tauri::command]
fn encrypt(data: &str, key: &str) -> Result<String, String> {
// AES-256-GCM encryption
crypto::encrypt_aes256(data, key)
.map_err(|e| e.to_string())
}
IPC also got better. invoke calls are now type-safe, with autocomplete from the
frontend when calling Rust functions.
Key Derivation — Why PBKDF2
You can’t use a master password directly as a key. It’s too short, with too little entropy. A key derivation function (KDF) is required, and the candidates were PBKDF2 / scrypt / Argon2.
| KDF | Memory Use | GPU Attack Resistance | Rust Library Maturity |
|---|---|---|---|
| PBKDF2 | Low | Medium | Very stable |
| scrypt | Medium | High | Stable |
| Argon2 | High | Highest | Relatively new |
KeyBox is a desktop app and key derivation runs only once per session at startup, so memory usage didn’t matter much. PBKDF2 with 100,000 iterations provides plenty of resistance to brute-force attacks, and prioritizing a mature library felt right.
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
fn derive_key(password: &str, salt: &[u8]) -> [u8; 32] {
let mut key = [0u8; 32];
pbkdf2_hmac::<Sha256>(password.as_bytes(), salt, 100_000, &mut key);
key
}
Bundle Size Comparison
| Framework | Installer | Memory | Cold Start |
|---|---|---|---|
| Electron | ~150 MB | ~200 MB | 1.2s |
| Tauri v2 | ~3 MB | ~30 MB | 0.4s |
A 50× difference. For an offline-only tool, that gap directly affects user experience.
What’s Missing — Tauri’s Limits
It isn’t all upside. Things hit during the build:
- System WebView dependency — Windows 11 ships Edge WebView2, but older Windows versions may need a separate install.
- Plugin ecosystem — Smaller than Electron’s. Tray icons, auto-update, and system notifications are covered by official Tauri plugins, but anything specialized has to be built yourself.
- Debugging — Not as smooth as Electron’s Chrome DevTools integration. Rust-side debugging needs separate IDE setup.
For KeyBox specifically, none of these were deal-breakers. Auto-update is unnecessary in an offline tool, and tray + notifications were covered by official plugins.
Wrap-up
Tauri v2 delivers on the “web tech for desktop apps” promise far more efficiently than Electron. If you’re building a small, security-sensitive tool, it’s worth a serious look. For a large app that depends on a rich plugin ecosystem, Electron is still the safer pick.
KeyBox source is open on GitHub — security tools should be auditable.