Hi everyone
,
I’d like to share Metamorphic, a zk habit and self-improvement tracker I’ve been building with Phoenix LiveView.
The core idea: all personal data is encrypted client-side before it ever reaches the server. The server only stores opaque ciphertext blobs.
Some backstory: I’ve had the brand assets for Metamorphic ever since earlier versions of Mosslet, and I was trying to think of what my next application project could be for it. I was inspired by observing my partner’s passion for psych, behavior, and habit forming. That started to give me the idea and then it made sense to me that something as personal as your habits and goals should be private to only you, and you shouldn’t have to worry about it being otherwise (I was also motivated to apply the recent zk-messaging updates from Mosslet to an entire app).
What it does:
- Habit tracking with daily/weekly check-ins, streaks, and drag-and-drop reordering
- Self-reflections with mood tracking and daily prompts
- Goal setting with milestones, progress bars, and habit linking
- Schedule/calendar with recurring events, day planner, and printable views
- Family/group accountability with shared habits, shared goals, and a group dashboard
- Progress insights with activity heatmaps and completion stats
- Data export (JSON/CSV) — decrypted entirely client-side, server never sees plaintext
Encryption is not a premium feature. Every tier gets full E2E encryption. Paid tiers gate convenience (unlimited habits, reminders, export, groups), not privacy.
How the crypto works:
- Client-side encryption via
libsodium-wrappers-sumo(XSalsa20-Poly1305 for data, NaCl box/seal for key distribution) - Hybrid post-quantum key encapsulation (ML-KEM-768 + X25519 via
@noble/post-quantum) — same approach as Signal and Apple iMessage - Three independent encryption layers at rest: client-side E2E, Cloak AES-256-GCM in Postgres, and LUKS disk encryption on Fly.io (fly’s managed postgres)
- Zero-knowledge email: no plaintext email column in the database, only an HMAC blind index for lookups and an E2E-encrypted blob
- Password never touches sessionStorage — only the Argon2id-derived session key
- Persistent key cache using Web Crypto API (non-extractable AES-256-GCM wrapping key in IndexedDB) so browser restarts don’t require re-entering your password
- Recovery key flow for password reset without server access to private keys
Tradeoffs worth mentioning:
- LiveView and zk encryption are in tension — the server renders the page but can’t render the actual content. You end up with brief placeholder skeletons that JS hooks fill in after decryption, and a lot of
push_event/handleEventchoreography (15+ hooks)… (although not too different UX-wise from my experience with Mosslet’s mostly trust-the-server model). - No server-side search on encrypted fields. Filtering by habit name or reflection text has to happen client-side after decryption. Fine so far for now.
- Testing perhaps is harder than usual — you can’t assert on decrypted content in LiveView tests since decryption is JS-only, so tests focus on DOM structure and data attributes rather than visible text. The context-level tests (Habits, Goals, Reflections, etc.) do verify that encrypted fields are stored and retrieved correctly (
assert habit.encrypted_name != nil), so the data pipeline is tested — it’s just the decrypt-and-display pipeline that seems untestable server-side. - If a user loses their password and hasn’t set up a recovery key, their data is gone by design. Correct for our zk design, but a real UX tradeoff.
Tech stack:
- Elixir/Phoenix LiveView for the full-stack web app
- Ecto + Postgres (Fly.io Managed Postgres)
- libsodium-wrappers-sumo + @noble/post-quantum on the client
- Cloak/cloak_ecto for application-level at-rest encryption
- Oban for background jobs (reminders)
- Tailwind CSS v4 + daisyUI (theming only) for the UI
- Sortable.js for drag-and-drop, JSZip for export packaging
- Tidewave for AI-assisted development — runtime introspection, live SQL/eval, a11y diagnostics, and browser interaction from the editor
A lot was taken/shared from my work with MOSSLET - a privacy-focused alternative to the social networking landscape (built with Elixir).
Happy to answer any questions. You can also check out a more detailed overview of the encryption architecture at our encryption page. As before, 20% forever discount for using code ELIXIRFORUM20.
I’m not very good at habit/goal tracking and so I’ve been using this myself to encourage getting back into yoga, meditation, and running. So far so good. Let me know if it’s helpful for you. Cheers ![]()






















