veilpass

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:

The push / pull cycle

Device A pushes ciphertext through a two-hop safety route to a DHT record slot; device B pulls through its own safety route. device A veilpass push hop hop DHT record ciphertext at record key chunked if needed hop hop device B veilpass pull 2-hop safety route 2-hop safety route
The storage node hosting the record only ever sees the second hop as the requester. It never learns which real IP address published the vault or which one pulled it.

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.

Treat tokens as one-shot

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:

What sync does not do

Non-goals

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

← back to design overview