Skip to content

Encryption

Trove encrypts everything that leaves your device. This page describes the primitives, where keys live, and what a relay can observe on the wire. The full implementation contract is in docs/CRYPTO_SPEC.md in the source.

Anything that travels between devices or members of a writing group:

  • Device-to-device sync of Manuscripts, Codexes, drafts, and Tome notes.
  • Excerpts, comments, and chat shared inside a Circle (writing group).
  • The MLS group state that maintains both of those.

On your disk, your writing sits as plain Markdown and TOML by default — so you can back it up, open it in other tools, or hand it to a co-writer. If you’d rather lock it all behind a passphrase, you can turn on at-rest encryption: it stays off until you switch it on, and when on it encrypts everything you write on that device. Full-disk encryption (FileVault, BitLocker, LUKS) is still worth using for everything else on the machine.

Group encryption uses Messaging Layer Security (MLS), RFC 9420 — the IETF standard for end-to-end encrypted group messaging. The same standard underpins Apple iMessage and Cisco Webex.

Trove locks a single ciphersuite:

MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519

Concretely:

Primitive Algorithm
KEM DHKEM X25519
AEAD AES-128-GCM
Hash SHA-256
Signature Ed25519

MLS gives us forward secrecy (compromising today’s keys does not reveal yesterday’s traffic) and post-compromise security (after a member’s device is compromised and the group commits, future traffic is private again).

The MLS implementation is openmls with the RustCrypto provider. No hand-rolled cryptography.

Each account has a single 32-byte identity_seed, generated by the OS CSPRNG on first launch. Every long-lived keypair derives from it through HKDF-SHA-256 with a domain-separating label. That means a fresh device — once it knows the seed — recovers the same keys and rejoins your DeviceGroup.

Per-device leaf private keys live in the OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service). They never leave the device.

Trove runs Cloudflare Workers as federation relays. They perform no decryption. Their only crypto operation is verifying inbound ActivityPub HTTP Signatures so they know which Actor sent what.

A relay can see:

  • That an Actor with handle @you@trove.ink exists.
  • That some Actor sent a signed envelope to another Actor.
  • The byte length of the ciphertext and the time it arrived.

A relay cannot see:

  • Any plaintext you wrote — manuscripts, comments, chat, titles, anything.
  • Your MLS group keys.
  • Your identity_seed or any key derived from it.

When you choose to sync, Trove wraps your identity_seed under a passphrase you pick, so a fresh laptop can recover the account.

  • KDF: Argon2id with parameters published in docs/CRYPTO_SPEC.md in the source.
  • AEAD for the wrapped seed: XChaCha20-Poly1305 (24-byte nonce, 16-byte tag).
  • Verification is fail-closed — a wrong passphrase produces an AEAD tag mismatch and the client surfaces a generic error. The relay cannot distinguish “wrong passphrase” from “no such account”.

A 24-word Backup phrase (BIP39, English wordlist) encodes the identity_seed directly. If you lose both the passphrase and the phrase, the account is unrecoverable. Trove cannot reset it; we have no copy of the keys.

Your library is plain Markdown and TOML in a folder you own. The app keeps writing, searching, and compiling offline forever — no relay required. Sync, Circles, and DMs need our hosted relay, but those are the only surfaces tied to it.

  • Threat model — what Trove protects against and where the limits are.
  • docs/CRYPTO_SPEC.md in the source — implementation-level contract.
  • docs/FEDERATION.md in the source — the federation stack.