Zero-Knowledge Architecture

    Security That's Open to Scrutiny

    Every credential is encrypted in your browser before it ever leaves your device. Credential fields and key blobs are stored encrypted. Protecting them depends on the whole system: encryption, key exchange, authentication and session controls — not just the choice of cipher.

    Why We Are Different

    The Dual-Gate architecture separates identity from authorization, keeping account sign-in distinct from the keys used to decrypt credentials.

    GATE 1

    Identity Layer

    Your email and password authenticate you to our server. This proves who you are, but gives you access to nothing but encrypted blobs.

    Handled server-side - proves identity only
    GATE 2

    Authorization Layer

    Your 3-word login phrase + your email + a global pepper are fed into Argon2id to derive a key that decrypts your Master Key - entirely in your browser. The Master Key exists only in volatile RAM for the duration of your session.

    Processed entirely in your browser - never sent to our servers

    We Can't See Your Data

    The server stores only the encrypted master_key_blob, a salt, and your public key. It never sees your 3 words, the Derived Key, or the Master Key.

    RAM-Only Master Key

    On every login, your browser re-derives the key from your 3 words + email + salt, decrypts the master_key_blob, and holds the Master Key only in volatile browser memory.

    Per-Project Encryption

    Each project has its own Project Key, wrapped (encrypted) by your Master Key using XChaCha20-Poly1305. Credentials are encrypted field-by-field with unique random nonces.

    For the Security-Minded

    Technical Deep-Dive

    Battle-tested cryptographic primitives with parameters designed to withstand modern attack vectors.

    Argon2id Key Derivation

    Your 3-word login phrase is transformed into a Derived Key using Argon2id, the winner of the Password Hashing Competition. This Derived Key then decrypts your Master Key (stored as an encrypted blob on the server).

    • Memory-hard: Requires 64MB of RAM per derivation, making GPU attacks impractical
    • Side-channel resistant: Protected against timing attacks via Argon2id hybrid mode
    • Input binding: Your 3 words + email + a server-side pepper are combined (the pepper itself is not publicly disclosed)
    • Separate Master Key: A random 32-byte Master Key is generated by libsodium's CSPRNG, then wrapped by the Derived Key
    // Key Derivation Parameters
    algorithm: Argon2id
    memory: 64 MiB
    iterations: 3
    parallelism: 4
    keyLength: 256 bits (32 bytes)
    input: 3words + email + pepper
    salt: random key_salt (stored on server)
    // Encryption Chain
    derivedKey = argon2id(words + email + pepper, salt)
    masterKey = decrypt(master_key_blob, derivedKey)
    // Encryption Chain
    3 Words + Email + Salt
    ↓ Argon2id (64MB, 3 iterations)
    Derived Key
    ↓ XChaCha20-Poly1305 decrypt
    Master Key
    ↓ XChaCha20-Poly1305 decrypt
    Project Key
    ↓ XChaCha20-Poly1305 encrypt/decrypt
    Credential Fields
    Each field encrypted with unique 24-byte nonce

    How Credentials Are Encrypted

    Each credential (name, username, password, email, URL, notes) is encrypted field-by-field using XChaCha20-Poly1305 with a Project Key (or the Master Key for personal vault items). Each encryption operation uses a unique random 24-byte nonce prepended to the ciphertext.

    • All encryption/decryption happens client-side in your browser
    • Keys are generated using libsodium's cryptographically secure RNG
    • XChaCha20-Poly1305 provides authenticated encryption - tamper-proof
    • Unique nonces per field prevent pattern analysis across entries

    How Sharing Works

    Invited users never need the owner's 3-word login phrase. The Project Key is sealed to the invitee's public key; opening that sealed copy requires the corresponding private key. After acceptance, each member keeps a copy wrapped with their own Master Key.

    Updated following FIND-PW01

    The previous invite-code flow stored a plaintext code on the server. That contradicted our stated separation of key-exchange material. We removed the invite_code column and replaced code-derived wrapping with recipient public-key sealing.

    Owner Invites

    1.

    Owner's browser already has the Project Key in memory (unwrapped from their Master Key)

    2.

    The Project Key is sealed to the invitee's public key

    3.

    The sealed Project Key can be stored for the recipient without storing a plaintext invite code

    Invitee Accepts

    1.

    The invitee uses the private key corresponding to the recipient public key to open the sealed Project Key locally

    2.

    The unsealed Project Key exists in browser memory

    3.

    The Project Key is immediately re-wrapped with the invitee's own Master Key and stored

    4.

    Subsequent access uses the member's own wrapped copy of the Project Key

    Recipient Public Key

    The Project Key is sealed for the invitee rather than wrapped with a code-derived key.

    No Plaintext Invite Code

    The server-side invite_code column has been removed.

    Independent Copies

    After acceptance, each user unlocks via their own 3 words → Master Key. No ongoing dependency.

    XChaCha20-Poly1305
    Authenticated symmetric encryption - used for credential fields and member key wrapping
    Argon2id
    Memory-hard key derivation - 64MB RAM, 3 iterations
    libsodium CSPRNG
    Cryptographically secure random number generator for all key generation
    24-byte Random Nonces
    Unique per encryption operation - prepended to each ciphertext

    Libsodium Cryptography

    All cryptographic operations happen in your browser using libsodium-js, a JavaScript implementation of the industry-standard NaCl library. This means:

    • All encryption/decryption happens client-side - nothing leaves your browser unencrypted
    • Master Key and Project Keys are generated using libsodium's CSPRNG, not on our servers
    • Constant-time operations prevent timing attacks
    • Audited, battle-tested cryptographic primitives
    Worst-Case Scenario

    What If We Are Hacked?

    Let's walk through exactly what an attacker would find with full database access.

    01

    Attacker gains full database access

    Through SQL injection, insider threat, or server compromise

    What they find:
    • Email addresses
    • Encrypted master_key_blob per user
    • Encrypted wrapped_project_key per project member
    • Encrypted credential fields (ciphertext blobs)
    • Random salts and nonces
    What they can't access:
    • Your 3-word login phrase (never stored)
    • Derived Keys (computed in browser, never sent)
    • Master Keys (exist only in volatile RAM)
    • Project Keys (wrapped, unreadable without Master Key)
    • Any plaintext credential data
    02

    Attacker attempts brute force

    Trying to crack the encryption without the 3-word phrase

    What they find:
    • XChaCha20-Poly1305 encrypted blobs (256-bit keys)
    • Argon2id-protected derivation requiring 64MB RAM per attempt
    • Per-user unique salts preventing rainbow tables
    What they can't access:
    • Any computational shortcut past Argon2id
    • Patterns in encrypted data (each field has unique nonce)
    • Weak key derivation to exploit
    03

    Attack becomes economically infeasible

    The math makes decryption impossible without the 3-word phrase

    What they find:
    • Useless encrypted blobs - indistinguishable from random noise
    • No metadata to correlate (names, URLs all encrypted)
    • No keys to derive without the mnemonic + email + pepper
    What they can't access:
    • Your credentials - ever
    • Project information - ever
    • Anything valuable - computationally indecipherable

    The Bottom Line

    Encryption is an important boundary, not a promise that every implementation is flawless. Credential fields and key blobs are encrypted, but key exchange and access controls must uphold the same model. The October 2026 reports exposed gaps in those boundaries; the production fixes and lessons are documented openly below.

    Security improves when assumptions get challenged

    Thank you to independent researcher squ11z1 for reporting FIND-PW01, FIND-PW11 and FIND-PW12 through coordinated disclosure in October 2026. All three issues were confirmed fixed in production in our response. This acknowledgement is not a full independent audit or a claim that no other vulnerabilities exist.

    • Project invitations: removed plaintext invite codes and sealed Project Keys to the invitee's public key.
    • Second-factor enforcement: hardened server-side checks; vault tables and KDF material refuse AAL1 sessions through Data API and row-level policies, addressing the reported MFA and TOTP unenrollment issue.
    • Passkey lifecycle: registration requires a live session no older than 10 minutes and a completed second factor. Password resets invalidate associated passkeys.

    Security documentation updated:

    Live Database Preview

    See What We Actually Store

    This is a realistic preview of what our production database actually stores. Even with full access, every field is an indecipherable encrypted blob.
    We literally cannot read your data.

    pwdly_production -
    SELECT * FROM credentials LIMIT 6
    Waiting...

    What you see

    Random-looking base64 strings - encrypted blobs that are computationally indistinguishable from random noise.

    What's hidden

    Everything - site names, usernames, passwords, emails, URLs & notes. Completely unrecoverable without your 3-word login phrase.

    What this means

    Even if an attacker steals our entire database, they get nothing usable. Every single field is end-to-end encrypted with XChaCha20-Poly1305.

    Pwdly vs Traditional Managers

    See how our zero-knowledge architecture compares to standard master password systems.

    Feature
    Pwdly
    Traditional
    Key Derivation
    Argon2id (64MB, 3 iterations, memory-hard)
    PBKDF2 / bcrypt
    Authentication Model
    Dual-Gate (Identity + 3-Word Authorization)
    Single Master Password
    Encryption Algorithm
    XChaCha20-Poly1305 (authenticated, unique nonces)
    AES-256-CBC / GCM
    Key Storage
    RAM only (volatile browser memory)
    LocalStorage / Cookies
    Server Breach Impact
    Encrypted fields and wrapped/sealed keys; impact depends on the compromise
    Potential vault exposure
    Sharing Model
    Recipient public-key sealing → independent key copy
    Shared vault key or server-mediated
    Per-Project Isolation
    Unique Project Keys, wrapped by Master Key
    Shared vault key
    GPU Attack Resistance
    High (64MB memory-hard per attempt)
    Low to Medium
    Recovery Option
    3-word login phrase (offline backup)
    Email-based recovery

    Ready for Real Security?

    Join teams who've upgraded from legacy password managers to true zero-knowledge architecture.

    No cookies. No tracking. No banners (almost).

    We use privacy-friendly, cookieless analytics (Umami) to count page views — no personal data, no profiling, no third-party scripts. Read more.