Zero Accessby Railmandocs

Background

Vocabulary, crypto primitives, and which package owns each job.

Background

Read this once before wiring Better Auth Zero Access by Railman. It is the glossary and package map for the rest of the docs.

What problem this solves

Users want encrypted data that survives a full database leak. That means:

  1. The server can store ciphertext and metadata.
  2. The server never holds the key that opens that ciphertext.
  3. Step-up for account actions stays separate from unlocking sealed data.

zero-access is a set of Better Auth plugins and small client libraries that implement those rules.

Words you will see

TermMeaning
MEKMaster encryption key. Lives in the browser after unlock. Never sent to the server.
WrapMEK encrypted under a password, passkey PRF secret, or recovery phrase. Server stores wraps only.
PRFWebAuthn extension that derives a site-bound secret from the authenticator. Used as a daily unlock factor.
KEKKey-encryption key derived client-side (from PRF, password, or BIP-39 seed) to produce a wrap.
Elevate / sudoShort-lived step-up claim on the session. Privileged product routes check it. Does not unwrap the MEK.
L0 / L1 / L2Session · elevate · vault unlock. Three independent proofs.
Blind storeServer API that accepts and returns wrap ciphertext without ever seeing MEK material.
amrAuthentication method references on an elevate claim (passkey, totp, password).

Trust boundary in one table

Lives in the browserMay cross the wireLives on the server
MEK (after unlock)Wrap ciphertext + KDF paramsWrap rows, MEK metadata
PRF outputSigned elevate claim (session)Session + HMAC-sealed claims
Vault passwordAccount-recovery public keyRate limits, associations
Recovery mnemonicSealed E2E envelopes (chat)Ciphertext only

If a value is in the left column, product code must never put it in a request body. The plugins enforce a denylist (M6) for the obvious failures.

Crypto (what we use, not a tutorial)

PrimitiveWhereRole
WebAuthn + PRFBrowserLogin and/or daily vault unlock secret
PBKDF2-SHA256BrowserPassword and recovery wraps (high iteration count)
HKDF-SHA256Browser / serverDomain-separated keys (PRF info, elevate MAC, recovery)
AES-GCM (via helpers)BrowserSeal app payloads with MEK
BIP-39BrowserRecovery phrase → seed → recovery wrap (and optional account-recovery signing key)
Ed25519Browser + server verifyAccount recovery challenge-sign (Mode B)
HMACServerElevate claims and login-factor stamps bound to BETTER_AUTH_SECRET

You do not need to call most of these directly. zero-vault and the passkey client helpers compose them.

For introductory extended reading, see WebAuthn and PRF, Crypto primitives, and Crypto and package rationale.

Packages

Install only what the product needs.

PackagePlugin / APIYou need it when…
better-authcoreAlways
@better-auth/passkeypasskey factoryPassed into enhancePasskey
@railman/auth-zero-access-passkeyenhancePasskey(passkey, …)Passkey login or PRF vault unlock
@railman/auth-login-factorloginFactors()Signed L0 stamps, requireAccess maps
@railman/auth-elevateelevate()Step-up / sudo (passkey optional)
@railman/auth-zero-accesszeroAccess()Passkey-centered blind MEK store, recovery, optional E2E relay
@railman/zero-vaultcreateVaultClient()Product create / daily unlock / reset UX
@railman/zero-e2eX3DH + Double Ratchet1:1 sealed messaging on a Vault-derived identity

Server install order when you use everything:

...enhancePasskey(...).plugins → loginFactors → elevate → zeroAccess()

PRF salt stays in the browser crypto config (defineZeroAccessPasskeyCryptoConfig), not in the server passkey plugin.

How adoption branches

See the full matrix in Use cases (A–E). Short version:

Entry pathCompositionWhere it leads
Elevate independentlyelevate (+ login-factor; passkey optional)Password/TOTP/passkey step-up at /elevate
Passkey-centered stackenhancePasskey + guarded zeroAccessVault at /vault, then Chat at /chat

Vault is the first product layer on the passkey/PRF foundation. Chat extends Vault: unlock supplies the identity seed that zero-e2e consumes. The crypto library does not run WebAuthn itself, but the Chat product path is not standalone.

Hard rules

  1. Elevate success must not call vault unlock.
  2. Vault unlock must not grant admin routes.
  3. Authorize from the server session (signed claim), never from client-supplied JSON.
  4. Recovery phrase is for reset, not everyday unlock.
  5. Chat identity must come from the unlocked Vault path; demo password bindings are not the product architecture.

Maturity

Packages are still early (0.0.x). Crypto and first Better Auth endpoints exist; treat production use as your own security review.

Next

  1. How it works — L0 · L1 · L2
  2. Elevate and sudo — with and without passkeys
  3. Quick start — compose and ship

On this page