how sync is encrypted

this names the actual primitives rather than asserting "encrypted" and leaving it there. everything below is read from the code, not from memory, and every claim has a file you can go check.

the phrase is the root

setup generates a bip-39 recovery phrase, 24 words. that phrase, run through pbkdf2-hmac-sha512, produces a 64-byte seed. the seed feeds hkdf-sha256, four times, with four different tags, into four keys: a signing key (ed25519), a key-exchange key (x25519), a content master key, and a metadata key.

those four keys are your account. holding the phrase reconstructs all of them on a fresh install, on any device, without needing another device online. losing the phrase loses the account: nobody, including us, can recover it. that trade is deliberate, not an oversight — a recoverable-by-us key would mean a key we could also be compelled to hand over.

device keys are separate, and not recoverable from the phrase

each device also generates its own random ed25519 keypair at enrollment. this identifies the device to the relay and signs its requests. it is not derived from the phrase and cannot be reconstructed from it — losing a device means enrolling a fresh one, which gets its own new device identity, then uses the phrase to recover the account's four keys onto it.

what actually leaves the device

every change you make becomes an op. before an op reaches the relay:

  • a fresh subkey is derived per op, from the content master key and the op's own id (hkdf-sha256). the subkey is used exactly once, which is what makes a fixed all-zero nonce safe here.
  • the op's payload is sealed with that subkey using xchacha20-poly1305.
  • the ciphertext is content-addressed with blake3, which is how the relay deduplicates a retried upload without ever hashing your plaintext.
  • each op also carries which document it belongs to, but not as a readable id: it's hmac-sha256(metadata key, document uuid), so the relay can group ops into the same document without knowing what that document is.

what a relay operator, ours or your own, actually holds: sealed blobs, their sizes, their content hashes, a document grouping id that means nothing without your metadata key, and timing. no plaintext, no titles, no filenames, ever.

each op proves who wrote it, without the relay's help

every op carries its author's device signing key, a signature over the op, and a second signature chaining that device back to an authorization made under the account's current signing key. a device you've revoked cannot produce a valid chain for anything signed after the revocation — and every other device checks that chain itself, locally, using its own copy of the account keys. the relay is never asked whether an op is legitimate, because it structurally cannot answer that question.

pairing a second device

the new device generates a temporary key and shows a 6-digit number derived from it. you compare that number against the same number on the device already paired — read aloud, or by eye. if they match, the existing device seals a copy of the account's four keys to the new device's temporary key and sends it over. the six digits are what stop a network attacker from substituting their own key mid-pairing: the numbers would not match, and you would notice.

revoking a device, without re-keying everyone from scratch

revocation rotates the account's keys forward into a new epoch. the new epoch's secret is sealed individually to every device that's still entitled, and separately to the phrase, so the phrase alone can still recover any epoch even if every device is eventually lost. a revoked device never receives the new epoch's secret, so it cannot decrypt anything written after the rotation, and it cannot forge a valid authorship chain for it either.

what's still on your side of the line

the local database is encrypted at rest when the os keyring is available, and the app tells you plainly in settings if it isn't (see the keys_at_rest_unprotected flag) rather than silently degrading. a compromised, unlocked device is outside what any of this defends: sync protects data in transit and at rest on the relay, not a device someone already has open. the full picture, including what this does not cover, is on the threat model page.

verify it yourself

none of the above needs to be taken on trust. the app is on github, the relay is on github, both apache-2.0 on the client and agpl-3.0-or-later on the relay, and the modules this page describes live at src-tauri/src/sync/: keys.rs, envelope.rs, pair.rs, epoch.rs, op_auth.rs. the wire protocol itself is written down separately as the relay spec, alongside the relay source.