clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/141-demo-review-fixes
lofi / docs / spikes / identity / evidence / 2026-07-15.md
2026-07-15.md 5.2K rendered
1# Identity and recovery evidence — 2026-07-1523Issue: [#8](https://github.com/FelineStateMachine/lofi/issues/8)\4Baseline commit: `0843833`\5Exact package: `jazz-tools@2.0.0-alpha.53`67## Decision89Reject `BrowserPasskeyBackup` from the M1 product contract. Do not invoke it, present it as backup,10or run real-device ceremonies until Jazz changes the storage design or provides a security argument11that satisfies the WebAuthn user-handle model.1213This is a security kill before the device matrix, not a claim that the physical flows passed. The14stable HTTPS device site explicitly says passkey backup is blocked.1516## Source finding1718Retained installed-source hashes:1920```text21e9dcea8ed17538cc9e58f7a97d74c8ff719336e3db1f26a7f44d6ed9f25688df passkey-backup.js22c95327809b841996f5fd12b7f740ebc09b0771f4e95f9cf69dcd385dc589b747 recovery-phrase.js23```2425`BrowserPasskeyBackup.backup()` decodes the 32-byte Jazz auth secret and assigns those bytes26directly to `PublicKeyCredentialUserEntity.id`. `restore()` returns assertion `userHandle` as the27same auth secret. No encryption, wrapping key, large-blob encryption, or server-held recovery record28exists in this path.2930WebAuthn Level 3 says user handles are account identifiers, must not contain personally identifying31information, and may be revealed by authenticators without prior user verification. A full-control32Jazz auth secret has a stronger confidentiality requirement than the field WebAuthn defines. See33[§14.6.1 User Handle Contents](https://www.w3.org/TR/webauthn-3/#sctn-user-handle-privacy).3435The implementation requests a platform discoverable credential, required user verification, and the36credProtect extension, then verifies UP/UV flags on restore. Those are useful ceremony controls but37do not redefine `user.id` as secret storage. The package also:3839- ignores the credential returned by `credentials.create()` and records no credential ID;40- treats a resolved `null` create result as success;41- records no backup-eligibility or backup-state signal, so it cannot prove ecosystem sync;42- supplies no exclude list, list, has, delete, or clear-passkey operation;43- can create duplicate credentials on repeated backup;44- returns a restored secret but leaves saving it and recreating the database to the caller.4546## Accurate identity model4748| Action | Exact meaning |49| ----------------- | --------------------------------------------------------------------------------------------------------- |50| First visit | Generate a random 32-byte auth seed and keep it in namespaced browser localStorage. |51| Clear site data | Remove the local seed and OPFS state; the next boot creates a different identity. |52| “Sign out” helper | Clear and regenerate the seed, so this is identity rotation, not a logged-out state. |53| 24-word phrase | Reversible BIP39 encoding of the same seed; no password, KDF, encryption, reset, revocation, or rotation. |54| Restore identity | Save the recovered seed and recreate the database/client. It does not restore unsynced OPFS rows. |5556Both a future safe passkey mechanism and the phrase recover identity authority only. Data lost with57site storage returns only when it was separately synced and remains readable by that identity.5859## Product wording6061Use:6263- “This device has a local identity key.”64- “Passkey backup is unavailable in this alpha because it did not pass security review.”65- “The 24 words are a full-control bearer secret. Anyone with all words can act with this identity.”66- “Identity recovery is not data backup. Unsynced device data cannot be recovered.”67- “Without a verified recovery method, clearing site data can permanently lose access.”6869Do not use “passkey-only account,” “login,” “cloud backup,” “sign out,” or “data recovery” for the70current behavior.7172## First-run and recovery UX decision7374M1 creates the local identity without a network round trip and immediately states its device-local75custody. Passkey backup controls are absent. A phrase export/import UI is deferred until it can76provide deliberate reveal/confirmation, prevent logging or telemetry, and make the bearer-secret and77identity-only limits unavoidable. Until then the prototype is honest but unrecoverable after78site-data loss.7980## Deferred device matrix8182> Status as recorded at M1, scoped to the rejected alpha mechanism. The replacement recoverable83> passkey mechanism has since shipped and validated DX-AUTH-01/DX-DEVICE-01; do not cite this84> deferral as current status.8586Fresh profile, deleted passkey, same-ecosystem restore, cross-platform restore, iOS tab/Home Screen,87and Android browser/installed-app passkey cases are **not run** because the candidate mechanism88failed the pre-device security gate. Reopen the matrix only for a replacement mechanism with a89confidential secret-wrapping design and explicit RP-ID contract.9091## Contract delta9293DX-AUTH-01 is revised: M1 validates truthful local-identity wording and rejects the alpha passkey94mechanism. DX-DEVICE-01 remains required for OPFS/PWA testing, but no unsafe passkey ceremony is95performed merely to fill a matrix.