Case study Inside Espejo
I wanted a digital journal where nobody could read my entries. Not the server, not the database, not even me if I lose the password. That's how Espejo started.
Problem
Journaling apps push streaks and gamification. And none offer real privacy: your entries live in plain text on someone else's servers.
Architecture
Offline-first with optional sync. The server never sees the content.
- IndexedDB (Dexie) as source of truth, works without connection
- AES-256-GCM + PBKDF2 (310k iterations) encryption on client
- Supabase for optional cross-device sync (encrypted blobs only)
- Soft-delete + last-write-wins for conflict resolution
UX
Designed from well-being psychology.
- Emotional check-in before writing, less friction on hard days
- Contemplation mode: immersive reading with serif typography
- Semantic favorites (clarity, seed, anchor, victory, scar)
- Year in Review, Spotify Wrapped style
- Nudges with priority: care > insight > celebration
Stack
- Next.js 16 (App Router) + React 19 + TypeScript
- TipTap (rich text), Tailwind 4, shadcn/ui (Radix)
- Vitest + Testing Library (unit) / Playwright (E2E)
- Vercel (deploy) + Sentry (errors) + CSP/HSTS headers
Learnings
When people actually use it, you think about code differently.
Offline-first with E2EE is not trivial. Migrating schemas in IndexedDB, resolving sync conflicts without seeing the data, handling password loss when it's the encryption key. Every problem pushes you past the happy path.
All of the above sounds good in theory. But you don't have to take my word for it: try it yourself.