Home / Replicate / The passkey lab
A lab: the passkey opens the vault
The passkey lab
Each step of the design, as a thing you can press in your own browser: a passkey, its PRF bytes, the door derived from them, a recovery code, and the door created on a vault server.
The design is in The passkey opens the vault: a passkey, through the WebAuthn PRF extension, gives the browser 32 bytes that only your authenticator can produce for this site; from those bytes the browser computes the key and the address of a small door vault, so nothing has to be stored to be found; the door holds the master vault’s key. Each card below is one step of that, as a thing you can press.
0. What this browser can do
WebAuthn, the platform authenticator, and whether the browser says it supports the PRF extension. Nothing to press: it is read when the page opens.
1. Make a passkey for this site
navigator.credentials.create() with rp.id set to this host, a resident key, user verification required, and the prf extension. The passkey is stored by your provider (Google Password Manager, iCloud Keychain, Windows Hello, a hardware key, or the browser’s own store); the page learns a credential id, a public key and whether PRF is on. It never learns who you are.
- Sign in to a passkey provider (Google Password Manager in Chrome, iCloud Keychain in Safari), so the passkey syncs and PRF is available.
- Use your phone by QR: a passkey on the phone, one scan at a time.
- Plug in a hardware key.
- Stay on this device: the browser’s own copy of the reading account keeps working, and the recovery code (4) is the way back.
2. Touch it: the PRF bytes
navigator.credentials.get() with the fixed input, the SHA-256 of a public string. The authenticator returns 32 bytes computed from a secret it keeps for this site and this passkey. The same passkey gives the same bytes on every device it syncs to, through the same kind of path; that is the whole trick. Through a different path (a phone over QR, instead of the provider on the device) the same passkey can give different bytes, which a test here found on 12 October 2026; the result shows which path answered, and the design gives each path its own door: one door per passkey path.
3. From the bytes, the door
The article’s derivation, made exact: HKDF to a seed, a passphrase and an 8-character vault id; then sgit’s own derivation of the read key and the write key, PBKDF2-HMAC-SHA256 at 600,000 iterations each, timed on this device. The door’s key is not stored anywhere: it comes out the same every time.
4. A recovery code: the second door
26 characters of base32, 128 bits, made by the page and in the real flow shown once. In place of the PRF bytes it derives a second door to the same master, for the day every passkey is gone.
5. Test vectors: the browser and sgit agree
The same derivation from a fixed, fake PRF output, compared value by value with what sgit’s own Python produced (vectors.json, from admin/build/passkey_vectors.py). Runs when the page opens. If any line is wrong, the browser and the command line would open different vaults.
6. The door on the vault server
With the door from 3 (or the test vector): check whether a vault exists at the derived address, create it by writing its one file (the master vault’s key, made at random and encrypted in this browser under the door’s read key), open it again as a new device would, and delete the file. Writing needs an access key for the server, which is the thing a reader will buy; reading needs nothing.
The vault id is never destroyed: a destroyed id can never be used again, and this one is derived from your passkey. The master vault itself, and moving the reading account into it, are the next steps and are not built.
What this lab is, and is not
- The relying party id is this host. On newsroom.sgit.ai a passkey made here is for newsroom.sgit.ai exactly, as the article’s trust boundary asks; it does not work on sgit.ai or on any other site. The derivation’s constants name newsroom.sgit.ai whichever host runs the page, so the test vectors are the same everywhere.
- Where the passkey lives decides what you get. Card 1 shows whether the passkey is backup-eligible, which is the browser’s word for synced. A clean browser with no provider may make a passkey that signs and returns no PRF bytes; card 2 says so if it happens, and card 1 shows the ladder.
- A door per path, not per passkey. On 12 October 2026 one Google Password Manager passkey gave one set of bytes through the provider on a Mac and on an iPhone, and a different set, consistently, when a phone answered a QR code from a Mac or an iPad: same credential id, same user handle, same input. Apple’s developer forums carry the same symptom for iCloud Keychain passkeys since late 2024, with a fix announced and a tester reporting it persisting. A second passkey in iCloud Keychain gave the same bytes on a Mac, a current iPad and a current iPhone, in Safari and in Chrome, which is the common case; an older iPad that could not use it locally offered only the QR path. The design therefore treats every distinct set of bytes as its own door to the same master, adds a door while unlocked on a device where both paths run (a Mac with Chrome runs both in one session), and uses the recovery code to bring a new path in where no door opens. The finding, the bug and the revised design are written out in one door per passkey path.
- Not built here: the master vault, the newsroom vault, moving the reading account into it, adding a second path or passkey as a door, retiring a door, and anything that signs the reader in. The brief’s phase five is where those land, after the move.
- The door record this lab writes is one sgit object (
obj-cas-imm-…, content-addressed), encrypted the way sgit encrypts, so sgit’s own code can decrypt it; it is not yet a commit in a tree, sosgit cloneof the door would show an empty history. The real door will be a vault with one committed file. - Tests:
admin/build/test_passkey.mjsdrives the page in Chromium with a virtual authenticator that has PRF, checks the derivation against the vectors, and runs the server flow against a stand-in server; the real server is tried by a person with an access key.
For an agent
A lab page for the design in https://newsroom.sgit.ai/articles/the-passkey-opens-the-vault.html: in a browser, make a passkey with the PRF extension, get the PRF bytes, derive a door vault's passphrase and id (HKDF) and sgit's read and write keys (PBKDF2, 600,000 iterations), make a recovery code, compare with sgit's Python at https://newsroom.sgit.ai/passkey/vectors.json, and with an access key create the door on a vault server and open it. It stores nothing but a passkey id; it does not touch the reading account. The derivation is written out at the top of https://newsroom.sgit.ai/passkey/lab.js.