SigilSession
Single-biometric bootstrap orchestrator for sign-in flows.
The problem this solves. Post-A2, sign-in derives the sigil DID via PRF(passkey, SIGIL_SALT) and the wallet seed via PRF(passkey, SEED_SALT). Each PRF salt is a separate parameter to a separate WebAuthn assertion — two ceremonies, two biometric prompts. After tapping "sign in" the user immediately taps "read balance" and gets prompted again. That's the regression WalletSeedSource.ensureSeedReady inherits from the post-A2 design.
The fix. WebAuthn's PRF extension supports evaluating TWO salts in one assertion (eval.first + eval.second). Modern authenticators (Google Password Manager since Chrome 132, hardware tokens) return both outputs in a single response → one biometric prompt.
Flow:
Single PRF ceremony with
prfSaltFirst = SIGIL_SALTandprfSaltSecond = SEED_SALT. One biometric.Derive sigil DID via SigilIdentityProvider.deriveFromPrfOutput.
Pre-warm SeedVault via WalletSeedSource.acceptPreDerivedSeed so the wallet's first refresh hits cache instead of running its own PRF ceremony.
Persist the sigil triple in SigilStateStore.
Fallback. If the authenticator only evaluates first (older GMS Core, etc.), PasskeyManager.authenticateWithPrf returns prfOutputSecond = null. We then run a second ceremony for SEED_SALT (= two biometrics, same as the pre-SigilSession path). UX degrades gracefully; correctness is unchanged.
Lives in sdk:wallet-seed (not core:identity) because the orchestrator depends on WalletSeedSource for the SeedVault pre-warm — and sdk:wallet-seed already sits above core:identity in the dependency graph, so this is the natural seam.