Case study
MoodHaven Journal
A cross-platform, privacy-first journaling app with a built-in health tracker — built in Rust and Tauri, with AI as my pair programmer. Zero-knowledge encryption, direct device-to-device sync, and a Wear OS companion, across desktop, browser, mobile, and watch.
The problem
Why it exists
I'm not a software developer. I'm an IT person — close enough to the code to understand how systems work, far enough from it that writing Rust was never part of the plan. I started building MoodHaven for myself: I wanted a journaling tool built around my own well-being, something I'd actually use and actually trust, where the data sat on my machine and was unreadable to anyone without my password.
This wasn't my first attempt. I'd started something similar with ChatGPT and text files, then burned it down once I'd learned enough to know it needed to be rebuilt differently. That cycle — start, learn, scrap, rebuild — is a real part of building this way. When Claude Code became available, I came back to the idea, described what I wanted, and by the end of the day the skeleton existed.
The approach
AI as a pair programmer, privacy as a constraint
The way I build is with AI as a pair programmer, not an oracle. An oracle gives you answers to questions you've already formed; a pair programmer helps you figure out what to ask, mid-feature, with the actual code in view. I didn't know what a Mutex or a reentrant lock was before this project — I learned them because I needed to understand why a fix worked, not just that it did. The judgment about whether something should exist stays human: an AI will follow you enthusiastically into a bad idea as readily as a good one.
Privacy-first was the design constraint that shaped everything, and it turned out to be generative rather than restrictive. The app is local-first and zero-knowledge: the backend never sees plaintext. All encryption and decryption happen in the frontend using the WebCrypto API; the Rust backend only stores and retrieves opaque encrypted blobs. The key is derived from your password with PBKDF2-HMAC-SHA-256 at 600,000 iterations and is never stored — it lives in memory only while the app is unlocked. There are two recovery paths and no others: your password (plus optional 2FA), or erase and start fresh. No master key, no backdoor, no "forgot password" email.
What I built
The features
The core architecture held from the early prototyping months through v1.0: Tauri as the desktop shell (Rust backend, small binary, native OS access), React and TypeScript for the frontend, SQLite for local storage, and WebCrypto for all encryption.
- Encrypted journaling on a five-level mood scale, with a TipTap rich-text editor (floating toolbar, slash commands, task lists) built on the same ProseMirror engine behind Notion.
- TOTP two-factor authentication from day one, plus native FIDO2 / YubiKey support implemented in Rust via CTAP2/HID — not browser WebAuthn, because Tauri's WebView doesn't expose it.
- Local speech-to-text via whisper.cpp running as a Tauri sidecar — audio never leaves the machine — with a three-layer formatting pipeline: always-on local cleanup, optional on-device polish through Ollama, and OpenAI BYOK only with explicit consent.
- On-device mood auto-detection, an Oura Ring health integration that surfaces sleep, HRV, and readiness as qualitative descriptors only (raw biometrics never leave the device), and time capsules that seal an entry until a future date.
- Direct peer-to-peer sync over the local network — no cloud server. Each device generates a permanent Ed25519 keypair, advertises itself over mDNS/DNS-SD, pairs via an out-of-band 6-digit PIN, and exchanges AES-256-GCM-encrypted wire frames over a transport key both sides derive independently without ever transmitting it.
- A Wear OS companion that records voice memos and quick mood signals on the watch and transfers them to the paired phone over Bluetooth, where the desktop picks them up for transcription — the watch as a capture device, the desktop as where meaning is made.
- A browser/PWA port running the same zero-knowledge model on IndexedDB instead of SQLite.
By v1.0.0 (tagged May 19, 2026) there were 693 tests across 47 files, roughly 127 Tauri commands across 21 modules, and builds passing on Ubuntu, macOS (Intel and Apple Silicon), and Windows.
Security testing
Trying to break my own app — ten times
Designing something to be secure and reading your own code nodding along is not the same as attacking it. So I treated MoodHaven the way an external auditor would: I built a small attack lab — a Kali Linux attacker box and two victim machines, Windows 11 and Ubuntu, running the real installed app — and ran an authorized penetration test, with Claude Code as the orchestrator. I ran it ten times in a row, fixing what I found between rounds and attacking the fixed version again.
Across the campaign, 65+ specific attacks were attempted; 41 found a genuine vulnerability through the seventh round, and all 41 were fixed. Findings included readable database metadata and an exposed password hash (closed by encrypting the whole database with SQLCipher), a lexicographic timestamp compare in sync that could silently lose your edits, the device-discovery feature broadcasting public keys over the LAN, missing lock guards on sensitive commands, and the database key being left unwiped in a SQL string on every unlock.
The real lesson was a pattern: round after round, a previous round's fix had quietly introduced a new bug. The sharpest example came in the eighth round — the flagship "encrypted at rest" feature had never actually engaged on any build, on any OS. A raw-key vs. KDF-key mismatch meant the database was written with one key and read with a different one, the verification step failed, and the app silently fell back to a plaintext file. There was no test covering the encrypt-then-reopen round trip. It's now fixed and verified end-to-end on the installed Windows build (Linux re-validation still pending).
The ninth round turned a custom attack tool — a from-scratch reimplementation of the app's own encrypted sync protocol — on the running app and surfaced six more issues, including one that only appeared live. The tenth round red-teamed the ninth round's own fixes and found a real bug in each. The honest claim is convergence, not perfection: the externally-reachable attack surface went to zero, the remaining residuals are local-access and lockout-class, and the one invariant the app exists to protect — that no attack ever exposed journal content to anyone not meant to see it — held across all ten rounds.
The full writeup is in How I Tried to Break My Own Encrypted Journaling App — Ten Times.
Outcome
Status
MoodHaven Journal reached v1.0.0 on May 19, 2026 and is free and open source under the MIT license — no Pro tier, no subscription, no analytics, no telemetry. AI features are BYOK for OpenAI or fully offline with Ollama. FOSS wasn't a business decision; it was the only honest conclusion of a privacy-first premise. There's more to build — a somatic companion module, StillHaven, is already planned.
Getting here was a milestone for the app and for how I build: continuity across sessions, real back-and-forth mid-feature, and the discipline of shipping something real and then hardening it properly.
Stack
What it's built with
Read more
The full story
- Seven Months of Vibe Coding — building MoodHaven from scratch with an AI pair programmer.
- Ten Rounds of Breaking My Own App — the authorized penetration test, in detail.
- MoodHaven on GitHub — the source, MIT-licensed.