Back to portfolio

self.cathedral.gg

An end-to-end encrypted health platform designed so even its operator can't read your data.

Live 2026

The self.cathedral.gg health timeline after unlocking, entries shown in a clean reader view.
The health timeline a logged-in user sees. Everything on this screen was decrypted in the browser; the server only ever held ciphertext.

Context

Self is a personal health platform where I run the server and still can't read what's in it. Health data is about the worst thing to leak, and most software that holds it asks you to trust whoever runs the server — this one is built so that trust isn't required.

That one constraint drove the whole architecture. If the operator can't be trusted, the browser has to do the protecting.

The hard part

The threat model assumes the server may be hostile, so the server stores ciphertext only. That sounds simple until you notice how much of a normal web app depends on the server reading your data.

Logging in and unlocking your data are two separate steps. Better Auth handles the session, but decryption keys derive from a passphrase the server never sees, stretched through PBKDF2 at 600k iterations. Steal a session cookie and you're logged into an account whose data you still can't decrypt.

Approach

All crypto runs through the Web Crypto API in the browser. AES-256-GCM for user data. X25519 ECDH key exchange when a user shares data with a healthcare provider, so the provider can read what was shared without the server ever holding a plaintext copy. Files are encrypted before they reach Backblaze B2.

The server still has work to do. pg-boss schedules jobs and Twilio carries outbound messages, and none of it gets to read the data it acts on. Every server-side feature starts from the same question: what is the minimum the server needs to know to do this job?

Tradeoffs

End-to-end encryption costs real features. Server-side search is gone, along with any analytics that would need the server to read user data. If someone forgets their passphrase, no reset email brings the data back, so the UX has to be honest about that up front. For health data, the alternative is a database that's one breach or one subpoena away from disclosure.

It also raises the bar on testing. The suite runs 2,516 tests across unit, integration, security, and e2e. The security tests exist because a bug in encryption code doesn't look like a bug. It looks like working software that leaks.

What shipped

A 60k+ LOC SvelteKit 2 PWA, deployed and live. Postgres with Drizzle underneath, Better Auth for sessions, pg-boss running the job queue, Twilio for outbound messaging, Backblaze B2 holding the encrypted files. Provider sharing is live: a user can share selected records with their provider, and the server never sees them in the clear.

Built with

SvelteKit 2 Postgres Drizzle Web Crypto API Better Auth pg-boss