How the XYZ Passwords vault is encrypted: Argon2id key derivation, XChaCha20-Poly1305 authenticated encryption, the on-disk vault format, storage backends, cross-device sync, and the threat model.
This page documents the cryptographic design of XYZ Passwords for users and security researchers who want the details. The design goal is zero-knowledge: your master password is processed only on your device, the encryption key is never stored or transmitted, and xyz.am only ever holds an encrypted blob it cannot read.
Every implementation — the web app, the browser extension, and the mobile app — produces a byte-identical vault format, so the same vault can be opened and synced across all of them.
Your master password is stretched into an encryption key with a memory-hard function (Argon2id). That key seals the vault with an authenticated cipher (XChaCha20-Poly1305), which both encrypts and tamper-proofs the data.
| Parameter | Value |
|---|---|
| Key derivation | Argon2id (RFC 9106), libsodium ALG_ARGON2ID13 (v1.3) |
| Memory cost | 65,536 KiB (64 MiB) |
| Time cost | 3 iterations |
| Parallelism | 1 |
| Salt | 16 bytes, randomly generated per vault |
| Derived key | 32 bytes (256-bit) |
| Cipher | XChaCha20-Poly1305-IETF (AEAD) |
| Nonce | 24 bytes, randomly generated for every encryption |
| Authentication tag | 16 bytes (Poly1305) |
| Additional data (AAD) | xyz-passwords-vault-v1 |
| Encoding | Base64 (standard alphabet, padded) |
| Randomness | Operating-system CSPRNG |
A fresh random nonce is used for every save, and the AAD binds each ciphertext to this vault format so a blob can't be replayed into a different context.
The vault is a small JSON document. Everything except the sealed ciphertext is
public metadata needed to re-derive the key and decrypt — it contains no secrets:
{
"vault_version": 1,
"kdf": {
"algorithm": "argon2id",
"salt": "<base64, 16 bytes>",
"memory": 65536,
"iterations": 3,
"parallelism": 1
},
"encryption": {
"algorithm": "xchacha20-poly1305",
"nonce": "<base64, 24 bytes>"
},
"ciphertext": "<base64: XChaCha20-Poly1305 sealed JSON>"
}
Decrypting the ciphertext yields the plaintext vault — a list of entries:
{
"entries": [
{
"id": "<uuid>",
"title": "Example",
"website_url": "https://example.com",
"username": "you@example.com",
"password": "<secret>",
"notes": "",
"tags": [],
"created_at": "<ISO-8601>",
"updated_at": "<ISO-8601>"
}
]
}
The plaintext JSON exists only in memory while the vault is unlocked. Only the sealed blob above is ever written to storage or transmitted.
Keep unlocked (optional). If you turn it on, the derived key is cached locally with an expiry you choose so you don't re-enter your master password constantly. It is stored on-device only (in the platform's secure storage on mobile) and is never sent to any server. Turn it off and the key is removed.
vault.xyz.am) and cannot decrypt it.In every mode, only the encrypted blob is stored or transmitted — never your passwords, never your keys.
Sync operates entirely on encrypted blobs. When a device pulls a remote vault it decrypts it
locally, then performs a merge: entries are unioned by id, and where
the same id exists on both sides the one with the newer updated_at wins.
The merge never silently drops an entry. The re-sealed blob is then pushed back.
For XYZ Encrypted Sync, the vault.xyz.am worker authenticates your
account token before serving the blob. Reading your own blob is always allowed; writing requires an
active Premium plan. If Premium lapses, your blob stays downloadable but stops syncing
until renewed.
What an attacker who obtains the stored blob sees: the vault version, KDF parameters, the salt, the nonce, and the ciphertext. None of these reveal your passwords. Recovering the contents requires guessing your master password and paying the full Argon2id cost for every guess.
What xyz.am can see: that an encrypted blob exists for your account (for the sync backend), its size, and when it was last updated. What xyz.am cannot see: your master password, your derived key, or any entry in your vault.
Out of scope: a compromised device. If malware controls the machine while the vault is unlocked, it can read what you can read. Keep your devices trusted and patched, and use keep unlocked conservatively on shared machines.
Your safety net is an encrypted export you control: from Settings you can save an encrypted copy of the vault and import it on any device. Keep that export plus your master password somewhere safe.
All three use the same libsodium primitives and the same parameters above, which is what keeps the vault format identical across platforms.