Building Prism: A Zero-Knowledge, End-to-End Encrypted Personal Finance Tracker
Building Prism: A Zero-Knowledge, End-to-End Encrypted Personal Finance Tracker
Prism Personal Finance TrackerPersonal finance applications are highly personal. Unfortunately, in the age of data brokers and cloud breaches, storing your net worth, income streams, and debt balances on a remote database can feel like a massive security liability. Most commercial applications sell your data or use it for targeted advertising.
I wanted a premium personal finance app that gave me the convenience of cloud sync across my phone and laptop without compromising my privacy.
This led to the design and development of Prism Personal Finance: a privacy-first wealth architecture platform featuring client-side Zero-Knowledge End-to-End Encryption (E2EE) powered by Cloudflare Workers, a D1 database, and browser-native Web Crypto APIs.
1. Shifting the Focus: High-Level Wealth Architecture
Most finance trackers focus on granular expense logging—requiring you to log every individual coffee purchase. This leads to friction and eventually abandonment.
Prism shifts focus to high-level wealth architecture. Instead of micro-managing receipts, you manage your overall balance sheet:
- Income Streams: Tracking multiple salary, dividend, or freelance streams.
- Category Budgets: Setting high-level monthly targets (Groceries, Rent, Utilities, etc.) and tracking overall spends.
- Debt Payoff Planner: Visualizing Snowball vs. Avalanche strategies.
- Compounding Savings Goals: Tracking long-term wealth growth.
2. Under the Hood: Zero-Knowledge E2EE
To achieve absolute privacy, Prism uses a Zero-Knowledge model. Your encryption keys are derived client-side and never leave your browser. The Cloudflare Workers backend only stores and syncs opaque ciphertext blobs.
Key Derivation via PBKDF2
When you log in with your email and PIN, Prism derives a 256-bit AES key using browser-native PBKDF2 (Password-Based Key Derivation Function 2) with 100,000 iterations:const deriveKey = async (email, pin) => {
const encoder = new TextEncoder();
const baseKey = await window.crypto.subtle.importKey(
"raw",
encoder.encode(pin),
"PBKDF2",
false,
["deriveKey"]
);
return window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: encoder.encode(email + "prismsalt_constant"),
iterations: 100000,
hash: "SHA-256"
},
baseKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
};AES-GCM 256-bit Encryption
Before syncing to the cloud, the entire client database model is stringified and encrypted using AES-GCM (Galois/Counter Mode). This generates a unique 12-byte Initialization Vector (IV) for each upload, ensuring ciphertexts are completely randomized even if the underlying data remains identical:const encryptData = async (plainTextJson, cryptoKey) => {
const encoder = new TextEncoder();
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
cryptoKey,
encoder.encode(plainTextJson)
);
return {
ciphertext: btoa(String.fromCharCode(...new Uint8Array(encrypted))),
iv: btoa(String.fromCharCode(...iv))
};
};3. The Offline-First Storage Layer
To provide a fast, offline-capable mobile experience, Prism acts as a Progressive Web App (PWA).
We chose browser-native IndexedDB for database storage. Unlike LocalStorage, IndexedDB handles large datasets asynchronously, preventing thread-blocking UI lag.
Why IndexedDB + LocalStorage?
| Storage Engine | Data Stored | Access Style |
|---|---|---|
| IndexedDB | Budgets, Spends, Debts, Goals, Subscriptions | Asynchronous, Transactional |
| LocalStorage | Theme (Dark/Light), Cloud Sync Preferences, Email | Synchronous Key-Value |
sw.js) to handle background sync.4. Serverless Cloud Sync with Cloudflare Workers & D1
On the server, we use a lightweight serverless API built on Cloudflare Workers and Cloudflare D1 (SQLite database).
Because the backend doesn't know (and cannot decrypt) your data, the schema is extremely simple:
CREATE TABLE IF NOT EXISTS user_sync (
email_hash TEXT PRIMARY KEY,
ciphertext TEXT NOT NULL,
iv TEXT NOT NULL,
updated_at INTEGER NOT NULL
);When you trigger a sync, the Worker:
- Hashes your email using SHA-256 (so the database doesn't even store plaintext emails).
- Verifies the request authentication token (JWT session cache in Cloudflare KV).
- Overwrites the ciphertext and IV in the D1 SQL database.
- Returns the updated timestamp.
Since the server receives only base64 ciphertexts, the host has zero visibility into your assets.
5. Simulating Wealth: Interactive Sandboxes
Aside from security, Prism provides rich interactive simulation tools using Chart.js:
- Snowball vs. Avalanche Payoff: Compares paying off debts by interest rate priority (Avalanche) vs. lowest balance (Snowball) to show precise payoff curves and total interest saved.
- Compounding Wealth Sandbox: Interactive sliders for monthly deposits, APR, and years show compounding timelines over 40 years.
Interactive Compounding Curve ChartConclusion
Privacy shouldn't mean sacrificing modern conveniences like cloud synchronization. By moving encryption to the edge (browser client-side Web Crypto API) and keeping the server stateless and blind (Cloudflare Workers & D1), Prism sets a new standard for self-hosted, personal productivity tools.