Encryption Architecture

How Metamorphic protects your data with zero-knowledge, end-to-end encryption and post-quantum key distribution.

Zero-Knowledge Guarantee

All user content is encrypted and decrypted client-side in your browser using libsodium and ML-KEM-768 (a NIST-standardized post-quantum algorithm). The server never sees your plaintext data.

Even a full database breach reveals nothing about your habits, goals, reflections, or streaks. The server stores only opaque ciphertext blobs and HMAC blind indexes.

What that means in practice:

  • We cannot read your habits, reflections, goals, or any personal data
  • Your plaintext email is never stored — only a one-way hash and an encrypted copy
  • Your password never touches our servers in a form we can use — only a derived key
  • Encryption is free on every plan — privacy is not a paid upgrade

Cryptographic Primitives

We use standard, proven algorithms — no custom or proprietary cryptography.

Operation Algorithm
Symmetric encrypt/decrypt XSalsa20-Poly1305
Hybrid seal/open PQ ML-KEM-768 + X25519
Legacy seal/open X25519 + XSalsa20
Key derivation Argon2id
Blind index HMAC-SHA512
At-rest encryption AES-256-GCM

Quantum Readiness

Metamorphic uses a hybrid post-quantum KEM (Key Encapsulation Mechanism) that combines classical X25519 with ML-KEM-768 (NIST FIPS 203). This follows the same approach used by Signal, Apple iMessage, and Chrome/TLS.

Both algorithms must be broken simultaneously to compromise a sealed key — if either one holds, your data remains protected.

What's already quantum-resistant:

  • All symmetric encryption — XSalsa20-Poly1305 and AES-256-GCM at 256-bit keys (~128-bit effective security via Grover's algorithm)
  • All key distribution — sealed under ML-KEM-768 + X25519, resistant to both classical and quantum attacks
  • Password hashing — Argon2id is not meaningfully threatened by known quantum algorithms
  • Blind indexes — HMAC-SHA512 with ~256-bit effective security post-quantum

Hybrid Scheme

ML-KEM-768 + X25519 via SHA3-256 key combiner (IETF hybrid KEM draft)

Classical fallback

If ML-KEM-768 is broken, X25519 still protects your keys

Quantum fallback

If X25519 is broken by a quantum computer, ML-KEM-768 still protects your keys

Hybrid key combiner

Both shared secrets are concatenated with both ciphertexts and public keys, then hashed through SHA3-256 to derive the final shared secret — providing domain separation and proper key mixing per the IETF hybrid KEM draft

ML-KEM internals (FIPS 203)

Inside the ML-KEM algorithm itself: SHA3-256 for public key hashing, SHA3-512 for seed expansion, SHAKE128/SHAKE256 for key derivation & sampling — all Keccak-based, unrelated to SHA-2

Version-Tagged Ciphertext

Auto-detects format for seamless backward compatibility

v1 Legacy

Classical X25519 + XSalsa20 (pre-migration data)

v2 Hybrid

ML-KEM-768 + X25519 post-quantum KEM (new default)

Progressive migration for existing users:

  1. 1 On next login, we detect you don't have post-quantum keys yet
  2. 2 Your browser generates a hybrid ML-KEM-768 + X25519 keypair
  3. 3 Your user key is re-sealed under the new hybrid scheme
  4. 4 All existing per-context keys (habits, goals, reflections, events, group memberships) are re-sealed under the hybrid scheme in the background
  5. 5 All future seal operations use post-quantum protection automatically

New registrations generate both classical and hybrid keypairs upfront. After migration, no v1-sealed keys remain — every key is quantum-resistant.

Key Hierarchy

Your password is the root of trust. Everything flows from it through a layered key hierarchy — each layer protects the next.

Password

Never stored, never sent to server

Session Key

Derived via Argon2id. Lives only in your browser session. Encrypts your private key.

Classical Keypair X25519

Generated at registration, random NaCl keypair

Public key

Stored on server. Used for classical key exchange and legacy compatibility.

Private key

Encrypted with your session key. Only you can unlock it.

Hybrid Keypair PQ

ML-KEM-768 + X25519, generated at registration or on first post-migration login

PQ public key

Stored on server. Used for post-quantum key encapsulation.

PQ private key

Encrypted with your session key. Backed up via recovery key.

Per-Context Keys

Each habit, goal, reflection, and group gets its own key

User key

Encrypts personal data (email, profile). Sealed via hybrid KEM when PQ keys are available.

Habit / goal / reflection keys

Random 32-byte symmetric keys. Each sealed via hybrid KEM to your public keys.

Group keys

Shared with members via hybrid KEM. Rotated when members are removed.

Three Layers of Encryption at Rest

Your data passes through three independent encryption layers. A breach at any single layer reveals nothing without the keys from the other layers.

1

Client-side E2E encryption

NaCl XSalsa20-Poly1305, encrypted in your browser before transmission

2

Application at-rest encryption

AES-256-GCM via Cloak, wrapping the already-encrypted blobs in Postgres

3

Infrastructure disk encryption

LUKS full-disk encryption on all database storage volumes, managed by our hosting provider

In transit:

  • Browser to server — TLS encrypted
  • Server to database — private WireGuard network (not accessible over the public internet)

What's Encrypted

A small amount of structural metadata is stored in plaintext to support core functionality. Everything personal is encrypted.

Encrypted (E2E)

  • Habit names & descriptions
  • Check-in details & notes
  • Reflections & journal entries
  • Goal titles & descriptions
  • Milestone content
  • Event titles & descriptions
  • Group names & member nicknames
  • Email address
  • Category labels

Structural Metadata (plaintext)

  • Check-in dates (for streak math)
  • Habit frequency (daily/weekly)
  • Display color & sort order
  • Archive status
  • Event times & recurrence rules
  • Goal type & progress numbers

This metadata does not reveal the content of your habits or personal data.

How Login Works

When you log in, your password never leaves the browser in a usable form. Here's the sequence:

  1. 1 Your browser fetches your unique salt from the server (no password sent yet)
  2. 2 Argon2id derives a session key from your password + salt, entirely in the browser
  3. 3 The session key unlocks your encrypted private keys (classical X25519 and hybrid PQ)
  4. 4 Your keys unseal each per-context key — auto-detecting whether it was sealed with hybrid PQ or classical encryption
  5. 5 Data decrypts in the browser — the server only ever sees ciphertext

Account Recovery

Since we can't read your data, we also can't reset your password for you. Instead, you can generate a recovery key in Settings — a human-readable code that acts as a backup decryption path.

If you forget your password, the recovery key lets you re-derive your classical and post-quantum private keys and set a new password. The recovery key is shown once, never stored by us (only an Argon2 hash for verification), and consumed on use — you must generate a new one afterward.

Important:

  • Without your password or recovery key, your data is permanently unrecoverable — by design
  • We strongly recommend setting up a recovery key and storing it somewhere safe

Group & Family Encryption

Groups use the same per-context key pattern. When you create a group, a random symmetric key is generated and sealed to each member via hybrid KEM. Only group members can decrypt shared habits, goals, and accountability data.

When a member is removed, the group key is rotated — a new key is generated and re-encrypted for remaining members. The removed member cannot decrypt future data.

Data Export

You can export all of your data at any time. The export decrypts everything client-side in your browser and downloads it as JSON or CSV. Your plaintext data never passes through our servers, even during export.

Verify It Yourself

Our encryption core is open source. You can inspect, audit, and verify the implementation yourself:

  • metamorphic-crypto — our open-source Rust crypto library (#![forbid(unsafe_code)])
  • Network tab — no plaintext user data ever leaves your browser
  • Source tab — the WASM crypto module is loaded at runtime, inspectable in DevTools
  • sessionStorage — your derived keys live only in your browser session

The crypto core is written in Rust, compiled to WebAssembly, and built on RustCrypto primitives. The hybrid post-quantum construction matches noble-post-quantum's ML-KEM-768+X25519 combiner. No custom or proprietary cryptography.

Have questions about our encryption?

We're happy to discuss the technical details. Reach out at support@metamorphic.app