<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Thayer</title>
    <description>The latest articles on DEV Community by Mark Thayer (@f0rest8).</description>
    <link>https://dev.to/f0rest8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3180631%2F268029bc-d75c-42e6-9467-b9ecb04995bb.jpeg</url>
      <title>DEV Community: Mark Thayer</title>
      <link>https://dev.to/f0rest8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/f0rest8"/>
    <language>en</language>
    <item>
      <title>I Built a Zero-Knowledge Encrypted Habit Tracker with Elixir &amp; Phoenix LiveView</title>
      <dc:creator>Mark Thayer</dc:creator>
      <pubDate>Thu, 16 Apr 2026 23:16:47 +0000</pubDate>
      <link>https://dev.to/f0rest8/i-built-a-zero-knowledge-encrypted-habit-tracker-with-elixir-phoenix-liveview-3jc9</link>
      <guid>https://dev.to/f0rest8/i-built-a-zero-knowledge-encrypted-habit-tracker-with-elixir-phoenix-liveview-3jc9</guid>
      <description>&lt;p&gt;I'm the solo dev at &lt;a href="https://mosspiglet.dev" rel="noopener noreferrer"&gt;Moss Piglet&lt;/a&gt;, a bootstrapped public benefit company. I've been building &lt;a href="https://metamorphic.app" rel="noopener noreferrer"&gt;Metamorphic&lt;/a&gt; — a habit and self-improvement tracker where all personal data is encrypted client-side before it ever reaches the server. The server only stores opaque cipher-text blobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why encrypt a habit tracker?
&lt;/h2&gt;

&lt;p&gt;I was inspired by my partner's background in psych and behavior science. It clicked that something as personal as your habits, goals, and self-reflections — basically a map of what you're trying to change about yourself — should be private to only you, and you shouldn't have to worry about it being otherwise.&lt;br&gt;
Every other habit tracker I looked at stores your data in plaintext. Metamorphic doesn’t.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Habit tracking&lt;/strong&gt; — daily/weekly check-ins, streaks, drag-and-drop reordering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Self-reflections&lt;/strong&gt; — mood tracking and daily prompts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Goal setting&lt;/strong&gt; — milestones, progress bars, habit linking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schedule/calendar&lt;/strong&gt; — recurring events, day planner, printable views&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Family/group accountability&lt;/strong&gt; — shared habits, shared goals, group dashboard&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Progress insights&lt;/strong&gt; — activity heatmaps, completion stats&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data export (JSON/CSV)&lt;/strong&gt; — decrypted entirely client-side, server never sees plaintext&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Encryption is not a premium feature.&lt;/strong&gt; Every tier gets full E2E encryption. Paid tiers gate convenience (unlimited habits, reminders, export, groups), not privacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the crypto works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Client-side encryption via &lt;code&gt;libsodium-wrappers-sumo&lt;/code&gt; — XSalsa20-Poly1305 for data, NaCl box/seal for key distribution&lt;/li&gt;
&lt;li&gt;Hybrid post-quantum key encapsulation (ML-KEM-768 + X25519 via &lt;code&gt;@noble/post-quantum&lt;/code&gt;) — the same approach as Signal and Apple iMessage&lt;/li&gt;
&lt;li&gt;Three independent encryption layers at rest: client-side E2E, Cloak AES-256-GCM in Postgres, and LUKS disk encryption on &lt;a href="https://fly.io" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Zero-knowledge email: no plaintext email column in the database — only an HMAC blind index for lookups and an E2E-encrypted blob&lt;/li&gt;
&lt;li&gt;Password never touches sessionStorage — only the Argon2id-derived session key&lt;/li&gt;
&lt;li&gt;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&lt;/li&gt;
&lt;li&gt;Recovery key flow for password reset without server access to private keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More detail on the architecture: &lt;a href="https://metamorphic.app/encryption" rel="noopener noreferrer"&gt;metamorphic.app/encryption&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The interesting tradeoff: LiveView + zero-knowledge
&lt;/h3&gt;

&lt;p&gt;This is the part I think other Elixir/Phoenix devs will find most relevant.&lt;/p&gt;

&lt;p&gt;LiveView is server-rendered by design. Zero-knowledge encryption means the server can't see the content it's rendering. These are fundamentally in tension.&lt;/p&gt;

&lt;p&gt;The result: brief placeholder skeletons that JS hooks fill in after client-side decryption, and a lot of &lt;code&gt;push_event/handleEvent&lt;/code&gt; choreography (15+ hooks). It's not too different UX-wise from a trust-the-server model — the skeletons flash in briefly — but architecturally it's a very different beast.&lt;/p&gt;

&lt;p&gt;Other tradeoffs:&lt;/p&gt;

&lt;p&gt;No server-side search on encrypted fields. Filtering by habit name or reflection text happens client-side after decryption. Fine at current scale.&lt;/p&gt;

&lt;p&gt;Testing is harder. You can't assert on decrypted content in LiveView tests since decryption is JS-only. Tests focus on DOM structure and data attributes. Context-level tests verify encrypted fields are stored and retrieved correctly — the decrypt-and-display pipeline is the gap.&lt;/p&gt;

&lt;p&gt;If you lose your password and haven't set up a recovery key, your data is gone. By design. Real UX tradeoff, but correct for zero-knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elixir/Phoenix LiveView&lt;/strong&gt; — full-stack web app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecto + Postgres&lt;/strong&gt; (&lt;a href="https://fly.io" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt; Managed Postgres)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;libsodium-wrappers-sumo + &lt;a href="https://github.com/paulmillr/noble-post-quantum" rel="noopener noreferrer"&gt;@noble/post-quantum&lt;/a&gt;&lt;/strong&gt; — client-side crypto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloak/cloak_ecto&lt;/strong&gt; — application-level at-rest encryption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oban&lt;/strong&gt; — background jobs (reminders)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS v4 + daisyUI&lt;/strong&gt; — UI/theming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sortable.js&lt;/strong&gt; — drag-and-drop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSZip&lt;/strong&gt; — export packaging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tidewave&lt;/strong&gt; — AI-assisted development (runtime introspection, live SQL/eval, a11y diagnostics)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot was shared from my work on &lt;a href="https://mosslet.com" rel="noopener noreferrer"&gt;MOSSLET&lt;/a&gt;, a privacy-first social platform with private journal and Bluesky interop, also built with Elixir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it
&lt;/h3&gt;

&lt;p&gt;I'm not very good at habit tracking myself, so I've been using Metamorphic to get back into yoga, meditation, and running. So far so good.&lt;/p&gt;

&lt;p&gt;Check it out at &lt;a href="https://metamorphic.app" rel="noopener noreferrer"&gt;metamorphic.app&lt;/a&gt;. Happy to answer questions about the architecture, the zero-knowledge approach, bootstrapping a public benefit company, or anything Elixir-related.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>showdev</category>
      <category>security</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
