--- title: How it works nav: How it works output: how-it-works/index.html description: How Varsto works: your devices and your own storages, encrypted blocks, keys, the signed ledger that tracks every copy, sync, peer-to-peer transfer, sharing, Strongroom and the recovery kit, with the honest limits. ---
The technology behind Varsto, one mechanism at a time: how a file becomes encrypted blocks, which keys exist, how the signed ledger keeps track of every copy, how devices sync and find each other, and what is not done yet.
Everything here is implemented in the source-available core (crates/varsto-core) and described with exact field layouts in the storage format. It is alpha software: formats may still change, and neither the design nor the code has been audited. The threat model and the failure model say what it does and does not defend against.

Varsto has two kinds of parts, and nothing of ours in between.
Every storage holds the same three kinds of object: encrypted blocks of file content, each device's encrypted file lists (manifests), and each device's signed ledger, plus a few small encrypted records for devices, folders and policies. Every path has exactly one writer: a device writes only under its own id, and blocks are named by the hash of their ciphertext and never overwritten. Storages therefore need no locks, and each one doubles as a mailbox: devices that are never online at the same time still converge, as long as they share one storage. Devices can also exchange blocks directly, on the LAN or across the internet.
There is no Varsto account, server or relay. If the project stopped tomorrow, your installed devices would keep syncing through your storages.

fsck --verify and automatic verification check copies they cannot read.Known trade-off: deterministic (convergent) encryption inside a folder means that two people who both hold the folder key can tell whether they have an identical file, and compressed chunk sizes are visible to the storage. Within a shared folder that stays true for one key epoch; removing a member gives the folder a new random key, so a removed member cannot recognise anything written afterwards.

Everything starts from one random 256-bit vault key, created on the first device and never derived from anything you type. It is the one secret to keep; the recovery kit is that key on paper.
keys.enc, encrypted under a key that Argon2id derives from the device passphrase. Changing the passphrase re-wraps that file and touches nothing in storage.Removing a device starts a new key epoch: a new random vault key, sealed to each remaining device with the hybrid key exchange below. New ledger batches, records, file lists and blocks use the new keys, and a chain of encrypted epoch records lets the new key open everything older. The removed device finds a signed revocation the next time it reaches a storage and stops; with a wipe order it also empties its folders and keys.
| Purpose | Algorithm | Notes |
|---|---|---|
| Content and metadata encryption | XChaCha20-Poly1305 (AEAD) | 256-bit keys, 192-bit nonces. Every ciphertext carries associated data that names what it is, so a storage cannot move an object into another role. |
| Hashing and key derivation | BLAKE3 (keyed hash and derive_key) | Chunk ids and file hashes are keyed. Every derived key carries the context e2ee-sync-format/0/<purpose>. |
| Passphrase to key | Argon2id | At least 64 MiB, 3 passes; a random salt per device. The passphrase never leaves the device. |
| Signatures (ledger, records) | Ed25519 + ML-DSA-65, hybrid | Both signatures cover the same bytes and both must verify. A key with a post-quantum part never accepts an Ed25519-only signature. |
| Key exchange (sharing, new epochs) | X25519 + ML-KEM-768, hybrid | Both shared secrets and the full transcript go into one key; a sealed secret stays private unless both algorithms are broken. |
| Security-key folders | FIDO2 hmac-secret | The security key derives a 32-byte secret from a credential and a per-folder salt after a touch. |
| Recovery kit | BIP-39 words, Shamir over GF(256) | 24 words with checksum; optional 2-of-3 shares. |

The ledger is Varsto's bookkeeping. Every device keeps its own append-only log of facts about what it did, and no device ever writes into another one's log. The facts are small and concrete: "I wrote block 7f3a to the storage I call box", "I downloaded 7f3a from box and its hash matched", "I published version 12 of this folder's file list".
Every device reads every other device's log through the storages and replays all of them into the same picture: which blocks exist, where every copy is, who wrote it, who has checked it and when. Nothing in it is trusted for where it came from: each batch is signed by the device that made it, and a copy counts as verified only when a different device has checked it.
Without the ledger, a sync tool can only hope its copies exist. With it, Varsto can say how many copies of every block there are, in which places, how recently each was checked, and which ones nobody has checked yet.
| Event | Meaning |
|---|---|
| Device enrolled | This device joined the vault, under this name. |
| Folder added | This device created a folder. |
| Block stored | This device wrote an encrypted block to a storage: folder, chunk id, object name, size and the storage. A claim, attested only by the writer. |
| Block verified | This device downloaded the block from a storage and its hash matched its name. Recorded by downloads, fsck --verify, automatic verification and full disk checks. |
| Block on device | This device holds the block's plaintext, as part of a file. |
| File list published | This device published version n of a folder's file list, with its hash and file count. |
| Storage retired | A storage was removed from the vault: claims on it made before this event no longer count as copies. |
No file names and no content: blocks are identified by keyed chunk ids and ciphertext hashes, and the whole batch is encrypted anyway.

Events are collected into batches, typically one per sync rather than one per file. A batch carries the device id, a sequence number (1, 2, 3, ...), the hash of the device's previous batch, a logical clock, its creation time and the events.
ledger/<device>/<sequence>, never overwritten.A reader accepts a batch only when both signatures verify against the device's public key in the device registry and its previous-hash matches. The post-quantum signature costs about 3.3 KB, which is why events travel in batches: the ledger grows by a few kilobytes per sync, not per file.
Every storage is a mailbox. After a device commits a batch it uploads every batch of its own that a storage lacks; on every pull it lists the ledger on each hot storage, takes the batches it has not seen, checks them, decrypts them and keeps a local copy. Because each storage carries the whole ledger, a laptop that syncs at night and a phone that syncs in the morning see the same history without ever meeting. Nobody orders anything centrally: a logical (Lamport) clock orders changes, not the wall clock. Untrusted replicas and the people you share a folder with write ledgers too, sealed under keys that open only their part: a replica's claims name objects, not files, and a member's claims cover only the shared folder.
Replaying all batches gives each device the same view: for every block of every folder, its object name and size, the storages that claim a copy and which device claimed it, which devices verified it there and when, and which devices hold the plaintext. It also knows the newest file list of each device for each folder, the devices and folders that exist, and which storages are retired. Storage names are local to each device ("box" here, "primary" there), so every storage carries a random identity, and devices match each other's claims by it.
fsck. Compares the ledger with what the storages actually list: claims without an object, blocks with no copy anywhere, blocks verified elsewhere.Because each device numbers and chains its own batches, history cannot be rewritten quietly. Two different batches with the same device and sequence number, or a batch whose previous-hash does not match, are a fork: every device that sees one marks that device as forked, and fsck reports it. The usual cause is a device restored from an old backup or a cloned disk, which would carry on from a sequence number its newer self has already used. When a device finds batches under its own identity that it never wrote, it fences itself: it stops signing and pushing until it is enrolled again as a new device, so two diverging copies of one identity never both write history.
The ledger says where blocks are; manifests say what the files are. After a change a device publishes its full view of a folder as a new manifest: every path with its size, modification time, content hash, block list and a version vector (for each device, the clock of its last change). It is encrypted under the folder's metadata key and announced in the ledger. Other devices fetch the manifests they have not seen and merge path by path:
name.conflict-<device>-<clock>.ext, so both devices converge without another round.Deleted files go to a local trash, never removed outright. The background service watches your folders and syncs within seconds of a change. Selective folders show every file but keep placeholders for those you have not fetched, and fetch a file when you open it. Manifests are full views today; very large folders will need incremental ones.
Devices also exchange blocks directly. Because encryption is deterministic within a folder, a device that holds a file can serve exactly the object the storage holds, and the receiver checks it against its name like any storage copy. A device that needs a block asks its peers first and the storages second, and spreads consecutive blocks over the peers that answer.
Tested on localhost, on the maintainer's own networks and on a three-device rig across NATs. Two symmetric NATs cannot be punched and fall back to a relay, which needs one reachable device in the vault (a home server, a VPS, a forwarded port); sockets are IPv4 only.
How fast. Benchmarks measures downloads between Paris, Amsterdam and Warsaw, peer to peer against an S3 bucket, with the method, the numbers and what is still slow.
Transport and the local interface. Objects are ciphertext before they leave the device, so transport adds no second layer for its own sake: S3 is spoken over HTTPS with Signature Version 4, rclone remotes use rclone's own configuration, and the S3 secret is kept in an encrypted file. The desktop interface is served only on 127.0.0.1, every request carries a per-session token, the Host header is checked, and the page runs under a strict Content Security Policy.
| Party | Sees | Cannot see |
|---|---|---|
| A storage provider (bucket, NAS, disk, rclone remote) | Object names (hashes of ciphertext), object sizes, how often you write and read, your device count from the number of ledger directories. | File names, contents, folder names, which blocks belong to which file, the hash of your plaintext. |
| An untrusted replica (a friend's NAS) | The same as a storage provider, plus that objects verify against their names. It signs its own claims under a replica-only key. | Any content, name or file list; it never receives a folder key or the vault key. |
| A peer on the LAN or the internet | That some device requests objects by name, after proving it holds the vault's peer authentication key. | Anything without that key: a stranger gets no object, and the LAN beacon does not name the vault. |
| Someone you share a folder with | That folder: every file in it, and your devices' signed claims about it. | Any other folder, the vault key, your device registry. |
| An AI assistant through the MCP server | The folders you granted, read-only unless you said otherwise. | Folders you did not grant; Strongroom folders, ever. |
| A thief with your unlocked laptop | Everything a normal folder holds while the device is unlocked. | Strongroom folders without your security key; nothing written after you remove the device from another one, except in folders shared with other people. |

The recipient's device creates a hybrid key pair (X25519 + ML-KEM-768) and prints a request code with the public half. Your device encapsulates to both halves, derives one key from both shared secrets and the transcript, and encrypts the folder key with it. The resulting token can travel over any channel: only the requesting device can open it, and it opens one folder.
Before sealing, both of you compare a fingerprint: six words derived from the request code and the name in it. You read them to each other over a call or in person; a code swapped on its way shows other words, and the recipient's device refuses a token whose confirmed words are not its own.
The recipient's device then writes its own ledger for that folder, sealed under a key derived from the folder key: both sides see each other's claims for that folder and nothing else. Removing a member, or revoking a token nobody accepted yet, gives the folder a new random key: your devices and the members who stay receive it (sealed to each member's own key), everything written from then on is under it, and the removed member's later changes are ignored. What they already received stays readable to them; a key cannot be recalled.

A Strongroom folder's key is never at rest. At creation the security key makes a credential, then produces a 32-byte secret from it and a random per-folder salt (FIDO2 hmac-secret). Varsto wraps the folder key under that secret and stores only the credential id, the salt and the wrapped key. Unlocking repeats the touch and keeps the key in memory for a window (15 minutes by default), then forgets it. There is no path to the folder key without the physical key: not through the passphrase, the vault key or a stolen backup. Backup security keys get their own wrap of the same folder key. Converting an existing folder gives it a new id and key, re-encrypts it and deletes the old ciphertext; re-keying a Strongroom does the same with a new key wrapped for every enrolled security key, for when a key may have been kept by someone.

The device passphrase goes through Argon2id to unlock keys.enc; the background app can keep it in the operating system's keychain so the service unlocks itself after login. Losing a passphrase loses that device's copy of the keys, not the vault: another device, or the recovery kit, enrols a new one.
The recovery kit prints the vault key as 24 BIP-39 words (a miscopied word is caught by the checksum) and, if you ask, three Shamir shares of which any two rebuild the key while one alone reveals nothing. There is no account, no escrow and no reset: the kit is how you carry the responsibility. Print a new kit after removing a device, because the old one holds the old key.
Field-level details, byte layouts and version rules: storage format, key hierarchy, ledger signing notes, security principles, failure model.