Case Study · Cross-domain · Desktop App

Kaizei

Personal Finance OS — TW/US stocks · smart accounting · double-entry ledger

Role: Solo developer
Timeline: 2024–2026
Status: Live (v2.4.17)
Monthly cost: $0 (Cloudflare + Supabase free tier)
v2.4.17Current version
AES-256GCM zero-knowledge
310kPBKDF2 iterations
0TypeScript strict errors
Kaizei official site hero — zero-knowledge encrypted ledger
Kaizei official site (kaizei.pages.dev) — zero-knowledge encryption is the core promise, not an add-on.

Pain points

Two structural problems with personal-finance software: (1) multi-device sync inevitably needs cloud, but handing API keys and transaction records to a third-party server is handing over the keys; (2) mainstream Electron apps treat security carelessly — missing CSP, Node.js integration enabled, API keys stored in plaintext config files.

Kaizei's design goal: deliver everything (TW/US stock auto-update, double-entry ledger, reports, Gemini analysis), but make it so the server is physically incapable of reading user data, and apply OS- and sandbox-level defenses on the desktop side.

System architecture

Three-layer separation: desktop + edge + object storage. The server only sees ciphertext; auth and decryption happen on the user's device.

  1. 1 · User master password

    Entered locally, never leaves the device. Web Crypto derives two independent keys.

  2. 2 · Dual-key derivation (PBKDF2 310,000 iterations)

    authKey (sent to server for identity verification) and encKey (stays on device, encrypts/decrypts the vault). Both derived from the same password but with different salts — neither can be derived from the other.

  3. 3 · Local AES-256-GCM encryption

    Vault data (API keys, settings, sensitive fields) is encrypted with encKey into ciphertext before being sent to the cloud. The server receives an encrypted binary blob.

  4. 4 · Cloudflare Worker (Hono + custom JWT)

    The Worker compares identity via SHA-256(authKey + server pepper) and issues a 30-day HS256 JWT. Even if the JWT is stolen, the attacker still gets ciphertext that the server itself can't decrypt.

  5. 5 · Supabase (PostgreSQL + RLS)

    RLS is set to no public policies — only the Worker's service_role can write. GitHub Actions pings daily at 02:00 UTC to prevent free-tier pause.

Kaizei desktop app main view
Kaizei desktop app main view. TW/US stock real-time quotes, last 6 months income/expense trend, expense categories, and recent transactions in one dashboard. Everything shown is locally AES-256-GCM-decrypted before render; the cloud only stores ciphertext.

Design highlight 1 — Zero-knowledge vault

The most critical design: the server is physically incapable of decrypting user data. Even if the server is fully breached, the DB is dumped, and the attacker gets admin access, what they pull out is still AES-GCM ciphertext — without the user's master password, there's no path back to plaintext.

User Device Cloudflare Worker · Supabase Master Password never leaves device PBKDF2 (310,000) → authKey + encKey AES-256-GCM encrypts vault locally encKey stays on device authKey + ciphertext Worker (Hono + JWT) SHA-256(authKey + pepper) Supabase (PG + RLS) stores: email · authHash · salt + AES-GCM ciphertext only server CANNOT decrypt (no encKey on server)
Zero-knowledge encryption flow. authKey is for identity verification; encKey never leaves the user's device. Even if the server is fully breached, the attacker only sees AES-GCM ciphertext.

Concrete implementation details:

  • PBKDF2 310,000 iterations (OWASP 2023 recommended floor) derives authKey + encKey from the master password + two independent salts
  • Server pepper is added when hashing authKey, so even if DB salts leak, offline brute-forcing is infeasible
  • JWT 30-day expiry; even a leaked JWT only sees ciphertext
  • Web Crypto API is used end-to-end via browser/Electron native crypto — no third-party crypto library trust issues

Trade-off

  • Lost master password = unrecoverable data. No backdoor = no "forgot password" option. User education must be strong
  • Each unlock takes ~ 200-400ms (PBKDF2 by design). Acceptable cost for security
  • Sync conflict resolution is limited: the server can't see the contents, so no field-level merge — only whole-payload overwrite (last-write-wins)

Design highlight 2 — Desktop-side defense in depth

Zero-knowledge protects the "server can't see" line. The Electron desktop side needs a separate defense in depth against local attacks:

  1. OS keychain integration

    Windows DPAPI / macOS Keychain provide a second-layer OS-level encryption for locally stored API keys (even though they're already part of the vault), bound to the user account / hardware.

  2. Electron sandbox fully on

    sandbox: true + nodeIntegration: false + contextIsolation: true. The renderer has no direct Node.js access; the main process exposes a strict IPC whitelist.

  3. Enforced CSP + DOMPurify

    Production builds automatically strip 'unsafe-inline'. All potentially HTML-bearing content (changelog from GitHub Releases, user-input notes) is forced through DOMPurify.

Kaizei stocks tracking view
Stocks module. TW/US stock real-time quotes; all API keys protected by OS-keychain encryption.
Kaizei ledger view
Double-entry ledger module. Each transaction is recorded locally then encrypted-synced to the cloud vault.

Design highlight 3 — Data pipeline & supply-chain defense

  1. Excel-import ReDoS protection

    Hard 20 MB file-size limit (blocks ReDoS attack vector) + auto-snapshot before import (up to 5 local backups) + one-click restore without password.

  2. TypeScript strict mode, zero tolerance

    v2.4.17 fully enables strict: true; fixed 32 pre-existing type errors to reach a 0-errors baseline. Type safety = compile-time elimination of an entire bug class.

  3. Dependabot weekly scans

    npm package vulnerabilities scanned weekly with auto-PRs. Secret Scanning + Push Protection prevent accidental secret pushes.

  4. Public release mirror (auditable binaries)

    The app's main repo is private, but releases sync to the public yu8812/kaizei-releases, where users can verify binary hashes and release notes.

Kaizei official site changelog
Official site "Changelog" section. Pulls version history from the public release mirror via the GitHub Releases API; users can see security updates per version at a glance.

Tech stack

ElectronReactViteTypeScript strict Cloudflare WorkersHono v4 Supabase (PostgreSQL + RLS) Web Crypto · AES-256-GCMPBKDF2 HS256 JWT (custom) Tailwind CSS GitHub Actions CI/CD
Kaizei official site core features
Official site "Core features" section. Visual presentation of three pillars (privacy / settlement / smart analysis) — corresponds to the design highlights of this case study.

Reflections / Future directions

  1. The tension between no-backdoor design and "forgot password" — losing the master password = unrecoverable data is the necessary cost of zero-knowledge architecture. Optional middle-ground solutions (Shamir Secret Sharing 2-of-3 recovery, social recovery) increase implementation complexity and trust assumptions. A worthwhile design problem.
  2. Real attack-surface evaluation for Electron desktop apps — multi-layer defense (sandbox + CSP + OS keychain + AES) is in place, but whether the corresponding threat model is over- or under-engineered requires empirical attack testing (e.g., sandbox escape research).
  3. Cross-machine vault sync conflict resolution — currently last-write-wins. Can field-level CRDT merge be done without breaking zero-knowledge? An interesting cryptography + distributed-systems crossover.

Further reading