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.
Journaling apps push streaks and gamification. And none offer real privacy: your entries live in plain text on someone else's servers.
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
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
- 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
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.