Skip to main content

Architecture

The zero-trust vault ensures the server never has access to your plaintext data. All encryption and decryption happens in your browser using the Web Crypto API.
Master Password (never stored or transmitted)
      |
      v
Argon2id KDF (64 MiB / 3 iterations / 4 lanes)
+ per-user salt (stored on server)
      |
      v
Vault Key (AES-256-GCM, via WebCrypto importKey)
      |
      +---> encrypts each password record
      |     { iv, ciphertext }
      |
      +---> wraps Family Vault Key (AES-KW)
      |
      +---> encrypts Shamir recovery shares

Key derivation

ParameterValue
AlgorithmArgon2id
Memory64 MiB (fallback: 32 MiB)
Iterations3 (fallback: 4)
Parallelism4 lanes
Output256-bit AES key
SaltPer-user random, stored in vault_salts table
The low-memory fallback (32 MiB, 4 iterations) activates automatically on devices with constrained memory. It maintains equivalent security with a slightly different parameter set.

Salt management

  • Generated client-side using crypto.getRandomValues()
  • Stored server-side in the vault_user_salt table — one salt per user
  • Created once during vault setup (POST /api/vault/salt)
  • Public by design — the salt ensures different users derive different keys from the same password, but is not secret

Encryption flow

1

Derive key

The vault key is derived from your master password + salt using Argon2id. The result is imported as a CryptoKey via crypto.subtle.importKey().
2

Generate IV

A random 12-byte initialization vector is generated for each encryption operation using crypto.getRandomValues().
3

Encrypt

The plaintext is encrypted using crypto.subtle.encrypt() with AES-256-GCM, producing ciphertext and an authentication tag.
4

Store

The { iv, ciphertext } pair is sent to the server. The master password and vault key are never transmitted.

TOTP secrets

TOTP secrets use the same zero-trust model:
  • Encrypted with the vault key before storage
  • Stored as zerotrust:{ iv, ciphertext } in the database
  • TOTP codes are generated client-side using HMAC-SHA1
  • The server returns 410 Gone if you request a code for a zero-trust entry

Security properties

  • Forward secrecy: changing your master password re-encrypts all vault data with a new key
  • No key escrow: PassAgent cannot decrypt your vault under any circumstances
  • Memory safety: vault key is held only in JavaScript memory (React state) and wiped on lock/logout
  • Brute-force resistance: Argon2id’s memory-hard properties make offline attacks expensive