Guide
How to sync SSH keys and hosts without trusting a cloud
Last updated October 21, 2026
I built ZestSSH, so read this with that in mind. The design below is how ZestSSH syncs, and every number comes from its published security whitepaper.
Encrypt everything on your own device, with a key derived from a password only you know, before any of it leaves. Then the server, or the storage you bring, only ever holds an encrypted file it can't open. That's end-to-end encrypted sync. ZestSSH does this for hosts, keys, snippets and settings, and lets you keep the encrypted file on your own WebDAV server, S3-compatible bucket or Google Drive for free, or on its hosted cloud.
Why is syncing SSH keys risky?
Your SSH keys open your servers. A sync service that keeps them in a form it can read becomes a single place where every one of those keys can leak: through a breach, a misconfigured backup, an insider, or a legal demand. Encrypting data "at rest" doesn't help if the provider also holds the decryption keys.
So the useful question to ask any SSH client that syncs is short: who holds the key that decrypts my vault? If the answer is "the service", you're trusting the service.
What does end-to-end encrypted sync mean?
The keys that encrypt your data are created and used only on your devices. The server stores and returns an opaque blob and helps devices agree on which version is newest, but it never has anything that could decrypt it. A full copy of the server's database is useless to whoever has it.
The price is real: if you lose every way of unlocking the vault, the provider can't reset it for you. ZestSSH handles that with a recovery key, covered below.
How does ZestSSH encrypt the vault?
- A random vault key. Your device generates a random 256-bit data encryption key (DEK). It encrypts everything.
- Encrypting the data. The vault is serialized to JSON, compressed with zlib, and encrypted with AES-256-GCM under the DEK, using a fresh random 12-byte nonce every time. GCM's 16-byte authentication tag means any tampering is detected instead of decrypted into garbage.
- Locking the key with your password. Your sync password goes through Argon2id with 128 MiB of memory, 3 passes and 4 lanes, using a random 32-byte salt. The result is a master encryption key (MEK) that wraps the DEK with AES-256-GCM. Argon2id is memory-hard, so every guess at your password costs an attacker real memory and time.
- A second lock for recovery. A recovery key, generated on your device and shown to you once, derives a second key (KEK) the same way with its own salt, and wraps the same DEK. Either your password or your recovery key can open the vault.
- Proving you have the password without sending it. The server stores a verification value derived from the MEK with HKDF-SHA256 and then hashed. That lets a device show it has the right password without the password, or the MEK, ever being sent.
The Argon2id settings travel with the vault, so they can be raised later without breaking older data. To stop a hostile server from quietly weakening them, the app refuses anything below 64 MiB, 3 passes and 4 lanes. It also refuses anything above 1 GiB, 32 passes and 16 lanes, so a server can't use absurd settings to freeze your device.
What's inside the encrypted file?
- Connection profiles: host, port, user and auth preferences
- SSH identities, including private keys, and the passwords stored with them
- Groups and tags, snippets, and port-forwarding rules
- Networks: the per-Wi-Fi or per-subnet address, user and jump-host overrides
- Known hosts, applied conservatively when restored, so a synced record never silently replaces a key a device already trusts
- Command history, only if you turn recording on (it's off by default)
- A short allowlist of settings, such as terminal appearance and connection defaults
What can the server see?
| Item | Stored? |
|---|---|
| The encrypted vault, its salts, Argon2id settings, wrapped keys, verification values and version number | Yes |
| Your sync password or recovery key | Never, in any form |
| The DEK, MEK or KEK | Never; they exist only on your devices |
| Any plaintext from your vault | Never |
| Hosted ZestSSH Cloud only: account email, sign-in name and photo, file size and timestamps, and your device list (platform and sign-in time) | Yes, to run the account |
| An activity log or your IP addresses | No |
A store you bring holds a single file: the encrypted vault with the fields in the first row, the time of the last push and an id for the device that made it.
Where can the encrypted file live?
Anywhere your devices can reach. Sync to your own storage is free on every ZestSSH tier, with the same encryption as the hosted service:
- WebDAV, for example a Nextcloud server you already run.
- An S3-compatible bucket: Amazon S3, Backblaze B2, Cloudflare R2, MinIO and others.
- Google Drive, using only Drive's hidden app-data folder, so ZestSSH can't see your other files.
- Your own server, if you implement ZestSSH's sync protocol. There's no packaged server to download; this option is for people who write their own.
- ZestSSH Cloud, the hosted option, if you'd rather not run anything (Juiced, $24.99 once).
What are the trade-offs?
- Lose both the password and the recovery key, and the synced vault is gone. Your devices keep their local copies, but nobody can decrypt the synced file.
- Metadata isn't hidden. Whoever stores the file can see its size and when it changes.
- Compression happens before encryption, which in theory leaks a little about content through size. The whitepaper explains why that trade-off is accepted for this kind of data.
- Use HTTPS for your store. ZestSSH allows plain HTTP for a WebDAV server on your own network and for S3 endpoints. The vault is still encrypted, but TLS also hides the traffic around it.
How can you check this yourself?
Read the ZestSSH security whitepaper (also available as a PDF). It documents the key hierarchy, every parameter above, exactly what the server stores, and how conflicts and host-key trust are handled.
The SSH protocol library ZestSSH is built on is open source as zest_ssh_core. The sync code is part of the app and isn't open source; the whitepaper is how it's documented.
FAQ
Can ZestSSH read my synced SSH keys?
No. The vault is encrypted on your device with a key derived from your sync password, and only the encrypted file ever leaves it. The hosted service and any storage you bring hold ciphertext they cannot decrypt.
What happens if I forget my sync password?
Use your recovery key, which ZestSSH shows once when you set up sync. It unlocks a second copy of the vault key. If you lose both the password and the recovery key, nobody can recover the data, including us; your devices still keep their local copies.
Is sync free?
Yes, on every tier, as long as the encrypted file lives on storage you run: a WebDAV server, an S3-compatible bucket or Google Drive. Juiced ($24.99, once) is only for using the hosted ZestSSH Cloud store instead. The encryption is identical either way.
Does ZestSSH still work without sync or an account?
Yes. ZestSSH works fully offline with no account. Sync is optional and only adds a copy of your encrypted vault somewhere your other devices can reach.