Skip to main content

Derivation chain

                    PERSONAL VAULT
                    ==============

Master Password (never stored)
      |
      v
Argon2id KDF (64 MiB / 3 iter)
+ per-user salt (vault_salts table)
      |
      v
Vault Key (AES-256-GCM)
      |
      +---> encrypts each password record
      |     encrypted_data: { iv, ciphertext }
      |
      +---> wraps Family Vault Key (AES-KW)
      |
      +---> encrypts Shamir recovery shares
      |
      |           FAMILY VAULT
      |           ============
      |
      +--[AES-KW unwrapKey]--> Family Vault Key (FVK)
                                  |
                                  +---> encrypts family passwords
                                  |     (AES-256-GCM, same pattern)
                                  |
                                  +---> encrypts Family Recovery Secret
                                        stored in family_recovery_config
                                            |
                                            v
                                  FAMILY RECOVERY (Shamir)
                                  =========================
                                  FRS (32 bytes random)
                                      |
                                      v
                                  Shamir split (GF(256))
                                  k-of-n shares
                                      |
                                      v
                                  Each share encrypted with
                                  guardian's Vault Key (AES-256-GCM)
                                  stored in family_recovery_shares


      SERVER-SIDE (separate domain)
      ==============================

SERVER_ENCRYPTION_KEY (env var)
      |
      v
AES-256-GCM --- encrypts TOTP/2FA secrets only
                (NOT used for vault data)

Key types

KeyAlgorithmDerivationStored
Vault keyAES-256-GCMArgon2id from master password + saltIn-memory only
Family vault keyAES-256-GCMRandom generationWrapped with each member’s vault key (AES-KW)
Item keyAES-256-GCMRandom per-itemWrapped with owner/recipient RSA public key
RSA keypairRSA-OAEP 2048-bitGenerated client-side during vault setupEncrypted private key stored server-side
Family recovery secretRaw 32 bytesRandom generationSplit into Shamir shares, each encrypted
Server encryption keyAES-256-GCMEnvironment variableServer-side only, for legacy TOTP

Sharing key flow

When sharing a credential:
1

Generate item key

A random 256-bit AES key is generated for the credential.
2

Encrypt credential

The credential is encrypted with the item key using AES-256-GCM.
3

Wrap for recipient

The item key is wrapped (encrypted) with the recipient’s RSA-OAEP public key.
4

Store

The encrypted credential and wrapped item key are stored. Each recipient gets their own wrapped copy.
5

Decrypt

The recipient unwraps the item key with their RSA private key, then decrypts the credential.

Recovery

If a user loses their master password, recovery is possible through Shamir secret sharing:
  1. The Family Recovery Secret (FRS) is split into k-of-n Shamir shares
  2. Each share is encrypted with a guardian’s vault key
  3. When k guardians provide their shares, the FRS is reconstructed
  4. The FRS decrypts the family recovery configuration
  5. The user can re-derive their vault key with a new master password