Security

Trust, demonstrated.

Manifa is end-to-end encrypted by construction, not by policy. Encryption happens on your machines; the server is a blind store. Here is exactly how it works, and where the honest limits are.

Zero-knowledge

What the server sees.

Everything content-bearing is ciphertext before it leaves your device. The rest is metadata; we pad chunk sizes to blur even that.

On your machine
  • DATABASE_URL=postgres://…
  • STRIPE_KEY=sk_live_…
  • app.tsx
  • schema.ts
What the server stores
  • a3f1c08e…
  • 9b27dd4a…
  • 1f0e6c52…
  • c84b91ff…
  • 70a2e3d9…
Never leaves your device
  • File contents
  • File and directory names
  • Your .env values
  • Account password or recovery code
  • Any encryption key, ever
The server does see
  • That a vault exists: the number and size of its encrypted chunks
  • When you sync, and which device authored it
  • Each device's platform, and how many you have
  • When a vault key rotates (that it happened, not why)
  • Who shares a vault with whom: the collaboration graph and emails
  • Session IP and browser (standard auth-session bookkeeping)
Key hierarchy

A root key only you hold.

Your Account Master Key is born on your device and never leaves it in plaintext. Vault keys wrap under it; chunk keys are derived, never stored. The same model behind Bitwarden and 1Password.

Account Master Key

256-bit · never leaves your device

Vault Key

one per vault · wrapped by AMK

Device wraps

AMK sealed to each device key

Chunk Key

derived, never stored

The primitives

Boring crypto, on purpose.

No home-rolled ciphers. Standard, well-reviewed primitives composed carefully.

AES-256-GCM-SIV

Authenticated content encryption, nonce-misuse-resistant, the correct choice for the deterministic nonces convergent encryption requires.

Convergent, within a vault

A chunk key is HMAC(vault key, plaintext hash), so identical content dedupes, but only inside your own vault, so the cross-user confirmation weakness never applies.

XChaCha20-Poly1305 wrapping

Key-wrapping (master-to-device, vault-to-master) uses XChaCha20-Poly1305 with random nonces. Argon2id derives the wrap key from your password.

BLAKE3 content addressing

Every chunk is addressed by BLAKE3 of its ciphertext. The store is keyed by content hash; the server sees opaque IDs and bytes, never paths.

Threat model

Who we defend against.

Honest-but-curious server or cloud provider

Learns nothing about file contents, names, or secret values. Sees only metadata it cannot decrypt.

Network attacker

TLS everywhere; payloads are already end-to-end encrypted underneath the transport.

Lost or stolen device

Revoke it at the device level. Revocation deletes its wrapped keys and rotates the affected vault keys.

What we won't claim

The honest limits.

Zero-knowledge is a strong claim. Here's exactly where it stops.

Metadata leaks

The server learns chunk sizes, sync timing, and the sharing graph. We pad chunk sizes to blur them; timing and the sharing graph we don't claim to hide. Zero-knowledge isn't zero-metadata.

Recovery means a recovery code

We hold no key. Lose every device and your recovery code, and the data is gone. We make backing it up unmissable at signup.

A malicious server is out of scope for v1

Authenticated encryption plus Merkle-root verification detect forged ciphertext, but we don't claim Byzantine resistance yet.

Encryption you can verify.

Dump our database and object store and you'll find only ciphertext.