Learn: the parts, explained in context
Status, from /shipped/: shipped v0.1.12 Learn pages: passkeys, WebAuthn and PRF; keys from PRF to plaintext; GCP from this site's side; each term defined once in data/terms.json and linked from its first use on every content page; flow diagrams drawn by the build as inline SVG with a text twin · shipped v0.1.12 The passkey lab on /learn/passkeys/: create lab passkeys in this browser under this host's RP ID, ask for PRF bytes, derive the wrapping key, wrap a lab KEK, encrypt a lab body, unlock again with the same or a second passkey, and watch what is stored where; headless in the build against Chromium's virtual authenticator with PRF · proposed Passkey with WebAuthn PRF derives the keyring wrapping key; RP ID secrets.sgit.ai
The design pages say what this site does. These pages say what the words mean, here, for this site, and nowhere more general than that: what a passkey is when it is the thing that unlocks your keyring, what the PRF bytes are, where each key comes from and goes, and what Google Cloud sees. Every term below is linked from the first place it appears on any page of the site, and the reader's column lists the terms a page uses.
The pages
| Page | Answers | Try it |
|---|---|---|
| Passkeys, WebAuthn and PRF | What a passkey is and is not here; the two WebAuthn calls, drawn; what your authenticator, your browser, this site and Google each store; why one site, one RP ID; what happens with two passkeys | The passkey lab: create a lab passkey in this browser, ask it for PRF bytes, derive the wrapping key, wrap and unwrap a lab KEK, add a second passkey, and watch what is stored where |
| Keys: from PRF to plaintext | Is there a passphrase (no); the PRF salt; HKDF; the wrapping keys; the KEK; AES-256-GCM; the recovery code and why it is long; what is in memory, when | The lab's steps 3 and 4 run this chain on real WebCrypto, with the bytes shown |
| GCP, from this site's side | What the project is for; Identity Platform and the uid; the bucket and the Security Rules; Terraform and Workload Identity Federation; what Google sees and cannot see | The admin checklist on /admin/, once a project exists (needs a human; see needs.md) |
The glossary
One entry per term, in this site's context. The data is data/terms.json; the build links the first occurrence of each term on every content page and sets the definition as the link's hover text, so a definition is written once.
- AES-256-GCM also: AES-GCM
- The authenticated cipher the browser provides; every wrap and the body use it, so a wrong key or a tampered byte fails loudly instead of decrypting to garbage.
- authenticator also: authenticators, platform authenticator
- Whatever holds the passkey's private key and PRF secret: Google Password Manager, iCloud Keychain, Windows Hello or a hardware key; it never hands the secret out, only results computed with it.
- bucket also: Cloud Storage, Cloud Storage for Firebase
- The Cloud Storage bucket where users/{uid}/keyring.json and meta.json live, versioned, reachable from the browser directly with the login's token.
- credential id also: credentialId, allowCredentials
- The public identifier of one passkey, stored in meta.json so the page can list your devices and ask the authenticator for that passkey by name.
- Firebase uid also: users/{uid}
- The login's stable id for a user; it names the user's prefix in the bucket and appears in the rules, and it is not a secret.
- GCP also: Google Cloud, GCP project
- The Google Cloud project that holds the login and the bucket for one environment; it sees who signed in and stores ciphertext, and can open none of it.
- gesture also: passkey gesture
- The touch, face, PIN or tap your authenticator asks for before it computes anything; one gesture per unlock, and nothing happens without it.
- HKDF also: HKDF-SHA256
- The standard key-derivation function the browser provides; turns 32 PRF bytes (or the recovery code) plus a salt and a label into a wrapping key, deterministically.
- Identity Platform also: Firebase Auth
- Google's login service, used through the vendored Firebase Auth SDK; the login decides which bucket paths you may touch and nothing else.
- KEK also: keyring key
- The keyring's one real key: 32 random bytes that encrypt the body, themselves stored only wrapped, once per unlock method.
- keyring also: keyring.json
- The one encrypted file per user in the bucket: the PRF salt, the wrapped copies of the KEK, and the encrypted body with the entries; version 1 is specified on its own page.
- meta.json
- The small public file next to the keyring in the bucket: the user handle, each passkey's credential id, public key and name, and the keyring revision; nothing secret.
- passkey also: passkeys
- A WebAuthn credential your authenticator keeps and syncs; here it is the thing that unlocks your keyring, not a login.
- passphrase also: master password
- There is none. Nothing you type unlocks the keyring; the passkey's PRF output and the machine-made recovery code are the only unlock methods, by design.
- PRF also: PRF extension, hmac-secret
- The WebAuthn extension that makes the authenticator return 32 secret bytes for a given input, the same bytes every time for the same passkey, origin and input; those bytes are what derives the wrapping key.
- PRF salt also: prfSalt, prf.eval
- 32 random public bytes fixed for the keyring's life, given to every passkey as the PRF input; stored in keyring.json in the clear, because it is an input, not a secret.
- recovery code
- 26 characters of base32, 128 random bits, shown once at first run: the one unlock method that lives in your head or on paper, and the only way back in when every passkey is gone.
- RP ID also: relying-party identifier, rpId
- The host a passkey is scoped to; here exactly secrets.sgit.ai (localhost when testing), never sgit.ai, so no sibling site can ask for the gesture.
- Security Rules also: storage.rules
- The rules file Google enforces in front of the bucket: a signed-in user may read and write only their own prefix, with size limits; the whole access control of the MVP.
- Terraform
- The infrastructure description in infra/terraform/ that creates each environment's project, login, bucket and rules, applied by GitHub Actions; it can delete and reconfigure, and cannot read.
- user handle also: user.id
- The 32 random bytes a passkey is registered under; not your email and not the Firebase uid, so the authenticator learns nothing about the account.
- WebAuthn
- The browser API (navigator.credentials) that creates a passkey and later asks your authenticator to prove it still has it; the only API this site uses to talk to the authenticator.
- Workload Identity Federation also: WIF
- How GitHub Actions gets a GCP identity without a stored key: the pipeline presents its own token and GCP trusts it for this one repository and branch.
- wrapping key also: WK, WK_passkey, WK_recovery
- An AES key derived from one unlock method (a passkey's PRF output, or the recovery code) whose only job is to encrypt the KEK; a different one per passkey.
How these pages are made
The diagrams are drawn by the build from data/diagrams/ into the page as inline SVG that uses the theme's colours, with the same diagram as text underneath for copying and for the markdown twin; no library, no script. The lab is a web component in the house shape, components/passkey-lab/, and it talks to nothing but your authenticator: no Google, no bucket, no network. What it keeps in this browser is listed on its page, and the gate checks that list on every release.