Design · Peer-to-peer sync
Sync without a server
Your device joins Veilid, a public peer-to-peer network. Your vault lives at a record key — a slot on Veilid's distributed hash table. Other devices that hold the record key and your passphrase can pull the vault down.
What Veilid gives us
Veilid is an open-source peer-to-peer network with onion-style routing and a built-in distributed hash table. For veilpass we use three of its features:
- A distributed hash table (DHT). A worldwide keyed store. Any node can create a record; any node with the key can fetch or update it (with the right credentials).
- Safety routes. Traffic between your device and the DHT is bounced through two intermediate Veilid nodes so the storage node hosting your record never learns your real IP address.
- Node signing. DHT writes are signed with an Ed25519 writer keypair. Only the holder of the private half can update the record.
The push / pull cycle
Push. The app takes the same ciphertext bytes that live on disk, splits them into chunks if the vault is larger than about 32 KB (Veilid's per-slot limit), and writes them to the DHT record. The write is signed with the writer keypair.
Pull. Another device with the record key fetches the chunks, reassembles them, verifies a BLAKE3 hash from the manifest, decrypts the vault, and merges the entries into its own local copy. Concurrent edits on both sides survive because merge is done per-entry with a monotonic per-device clock.
Pairing: seven words
Getting a second device onto the same vault is a matter of reading seven words from the first device to the second.
The words are drawn from the EFF long wordlist — the same list used for physical-dice passphrases. Six of the seven are random; the seventh is a checksum so a mis-heard word fails cleanly instead of silently reaching the wrong DHT slot.
example · abandon adept baker crest daub eager freckle
Six body words + one checksum word · ~77 bits of entropy · hyphenated wordlist entries are supported.
Under the hood: the six body words seed a deterministic Veilid record key + an XChaCha20-Poly1305 content key. The first device publishes a small sealed bundle (containing the real vault's record key and its writer keypair) to that derived record. The second device re-derives the same keys from the words, fetches the bundle, and now has everything it needs to sync.
The sponsor record isn't deleted after the second device uses it. Anyone else who sees those seven words while the record still exists on the DHT can re-derive it and pair themselves. Read tokens over a channel you trust and consider them spent afterward, even though they technically aren't.
Merge: last-writer-wins per entry
Two devices editing the same vault at the same time will
eventually converge. Every entry carries an
updated_at timestamp based on a monotonic
per-device clock; on merge, the newer edit wins for each
entry. Vault-level metadata (name, sync policy) uses the
same last-writer-wins rule at the vault level.
There's no CRDT and no manual conflict UI. In practice, if both devices edited the same password field within the same second, the loser's edit is lost — the same trade-off a shared spreadsheet makes. Any entry the loser added that didn't collide is preserved.
What leaks on the wire
Everything meaningful is encrypted. Veilid's onion-style routing means "who can see what" depends heavily on where on the network the observer sits.
The storage node hosting your record sees the record key when your devices probe it (record keys aren't secret — they're addresses, not credentials), the size of your vault ciphertext, how often you push, and the magic prefix bytes we use for chunk framing (which identify a payload as coming from veilpass). It never sees your real IP address — the safety route hides the sender behind intermediate hops — and it cannot link a record access back to a specific device or person.
Any other node on the network sees only encrypted route packets. Record keys, ciphertext, sizes, and the association between a key and a device are all hidden inside the route envelope; each hop only knows its immediate neighbour. A passive network observer can still do coarse traffic analysis (packet timing and volume between neighbouring nodes), which is a fundamental limitation of any low-latency onion network.
What no observer of any kind can learn:
- The vault contents. Ciphertext is AEAD-sealed.
- Your passphrase, master key, or vault key.
What sync does not do
No selective sync. A push writes the whole vault. There's no per-entry replication.
No unpublish button. There's no way to remove a record we already published; it persists on the DHT under Veilid's own TTL and re-publication policy. Because the content is AEAD-sealed, this is a metadata concern, not a confidentiality one.
No metadata anonymity from the storage node. The peer holding your record sees "someone syncs a ~N KB vault around this time of day." Passive nodes elsewhere on the network do not see this — the record key and the ciphertext size are inside the encrypted route envelope — but the storage node itself does, and veilpass does not attempt to hide it.
Where the code lives
crates/veilpass-sync/src/node.rs— Veilid node lifecycle and record operations.crates/veilpass-sync/src/chunking.rs— payload framing and manifest hashing.crates/veilpass-core/src/pairing.rs— seven-word tokens and deterministic derivation.crates/veilpass-bridge/src/api/sync.rs— the FFI surface the apps call.