{"version":3,"sources":["../src/types.ts","../src/constants.ts","../src/browser.ts","../src/addresses.ts","../src/derive.ts","../src/rootWrap.ts","../src/base64url.ts","../src/ceremony.ts","../src/SoftAuthenticator.ts","../src/vault.ts","../src/pop.ts","../src/CrossTabSync.ts","../src/IdentityImpl.ts"],"names":["concatBytes","blake2b","bs58","keccak_256","hkdf","sha256","ed25519","secp256k1","WalletError","xsalsa20poly1305","observable","runInAction"],"mappings":"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","file":"index.cjs","sourcesContent":["/** Spec §3.1. Public contract for the identity layer. */\nexport interface Addresses {\n  cere: string;\n  solana: string;\n  evm: string;\n}\n\nexport type Chain = 'cere' | 'solana' | 'evm';\n\n/**\n * The S0 account this session belongs to, as the server reports it on every\n * session response (register-finish, login-finish, session refresh).\n *\n * An account is the durable identity; a credential (passkey) is one way into\n * it. `email`/`displayName` are nullable because accounts created before S0 have\n * neither — the migration does not invent them, and the Settings nudge asks for\n * an email without ever gating on one.\n */\nexport interface AccountInfo {\n  accountId: string;\n  email: string | null;\n  displayName: string | null;\n}\n\n/*\n * S1 PR2 deleted `IdentitySession`, `SessionHandoff` and\n * `supportsSessionHandoff`.\n *\n * `IdentitySession` was the wire shape of a session moving between two\n * wallet-origin surfaces: raw `edSeed` and `secpKey` bytes, plus the bearer\n * token. `SessionHandoff` was the pair of methods that produced and consumed\n * it. Together they were the ONLY API by which key material could leave an\n * `Identity` in usable form — `exportKeystore` hands out ciphertext the caller\n * must supply a passphrase to open, which is a different thing.\n *\n * Removing them is what makes \"keys never cross a surface boundary\" structural\n * instead of a convention: the registering popup no longer exports anything,\n * because the frame re-derives its own key from PRF. `no-key-export.test.ts`\n * is the standing guard against the seam being reintroduced.\n */\n\n/**\n * Bearer-only session resume capability (Task 2/3). Kept OFF the core\n * `Identity` interface deliberately: it is an\n * optional, refresh-flow-only concern, and forcing every `Identity`\n * implementation (including thin test doubles used across the SPA's test\n * suite) to provide it would widen the interface for no benefit to callers\n * that never boot the embed route. Concrete `IdentityImpl` implements it;\n * SPA wiring feature-detects via `supportsSessionResume()`.\n */\nexport interface SessionResume {\n  /**\n   * Adopt a session from `apiClient.session.refresh()` WITHOUT running a\n   * passkey ceremony and WITHOUT deriving signing keys — the \"bearer-active\"\n   * state. See `IdentityImpl.resumeSession` for the full contract.\n   */\n  resumeSession(session: {\n    token: string;\n    addresses: Addresses;\n    credentialId: string;\n    /**\n     * The account fields off the same `SessionResponse` the token came from, so\n     * a bearer-resumed tab knows its account without a ceremony. Optional so a\n     * caller that spreads a whole `SessionResponse` gets them for free while an\n     * older, narrower call site still compiles; when `accountId` is absent the\n     * previously-known account is left alone rather than blanked.\n     */\n    accountId?: string;\n    email?: string | null;\n    displayName?: string | null;\n  }): void;\n  /**\n   * `true` once real signing keys exist (register/login/adoptSession); `false`\n   * for a bearer-active session with a token but no keys.\n   */\n  hasKeys(): boolean;\n}\n\n/**\n * Narrow an `Identity` to one that supports bearer-only session resume.\n * `IdentityImpl` returns true; thin test doubles return false and are simply\n * skipped by boot-time resume wiring (e.g. the embed route falls back to\n * \"stay logged out\" rather than crashing on a missing method).\n */\nexport function supportsSessionResume(identity: Identity): identity is Identity & SessionResume {\n  return (\n    typeof (identity as Partial<SessionResume>).resumeSession === 'function' &&\n    typeof (identity as Partial<SessionResume>).hasKeys === 'function'\n  );\n}\n\n/**\n * SPA-injected keystore encoder. Lives OUTSIDE the published identity package\n * so the heavy keystore libraries (`@polkadot/keyring`, `ethers`) stay in the\n * SPA bundle only. Given raw key bytes it returns a password-encrypted keystore\n * JSON string. Called synchronously inside `IdentityImpl`'s `sessionKeys` seam\n * (see `WalletExport`); it MUST be pure and retain nothing.\n *\n * - `chain: 'cere'` → keyBytes is the 32-byte ed25519 seed → Polkadot ed25519\n *   keystore JSON (SS58 prefix 54).\n * - `chain: 'evm'`  → keyBytes is the 32-byte secp256k1 key → Ethereum V3 keystore JSON.\n */\nexport type KeystoreEncryptor = (\n  chain: 'cere' | 'evm',\n  keyBytes: Uint8Array,\n  passphrase: string,\n  address: string,\n) => Promise<string>;\n\n/**\n * Wallet-export capability. Kept OFF the core `Identity` interface for the same\n * reason as `SessionResume`: it is an optional,\n * standalone-SPA-only concern. Concrete `IdentityImpl` implements it; UI wiring\n * feature-detects via `supportsWalletExport()`.\n *\n * SECURITY: `exportKeystore` returns only ciphertext — raw seeds never leave the\n * vault seam. `revealSolanaSecret` is the sole raw-key path (Phantom cannot\n * import an encrypted file); it is deliberately a separate, explicitly-named\n * method so the raw path cannot be taken by accident.\n */\nexport interface WalletExport {\n  /** Password-encrypted keystore JSON string for the given chain. */\n  exportKeystore(chain: 'cere' | 'evm', passphrase: string): Promise<string>;\n  /** base58-encoded 64-byte Solana secret key (raw — Phantom import). */\n  revealSolanaSecret(): Promise<string>;\n}\n\n/**\n * Narrow an `Identity` to one that supports wallet export. `IdentityImpl`\n * returns true; thin test doubles return false and hide the export UI.\n */\nexport function supportsWalletExport(identity: Identity): identity is Identity & WalletExport {\n  return (\n    typeof (identity as Partial<WalletExport>).exportKeystore === 'function' &&\n    typeof (identity as Partial<WalletExport>).revealSolanaSecret === 'function'\n  );\n}\n\nexport interface Identity {\n  readonly isAuthenticated: boolean;\n  readonly addresses: Addresses | null;\n  readonly credentialId: string | null;\n  /**\n   * The account behind the current session, or `null` when signed out. Set by\n   * register / login / `resumeSession` from the server's session response — a\n   * pure read, never a ceremony trigger.\n   *\n   * Optional on the interface (not on `IdentityImpl`, which always defines it)\n   * so the SPA's thin `Identity` test doubles are not all forced to carry an\n   * account they do not model.\n   */\n  readonly account?: AccountInfo | null;\n\n  /**\n   * Replace the account fields for the current session (see\n   * `IdentityImpl.setAccount`). The only caller is a successful\n   * `PATCH /v1/accounts/me`, which returns the whole updated account and is\n   * the one way the account changes WITHOUT a session response to carry it.\n   *\n   * Optional for the same reason `account` is: the SPA's thin `Identity` test\n   * doubles do not model an account, and a caller must therefore treat this\n   * as a best-effort refresh (`identity.setAccount?.(...)`), never as the\n   * thing that makes the patch durable — the server already did that.\n   */\n  setAccount?(account: AccountInfo): void;\n\n  /**\n   * Create a NEW account and enrol this device's passkey as its ORIGIN\n   * credential — the one whose PRF output IS the account root (described\n   * under \"`is_origin`\" in `ARCHITECTURE.md`, which lives in the wallet\n   * repo, not in this package). Registration writes no root\n   * envelope: adding a further device is recover-then-enrol, a later\n   * sub-project, not a second `register()`.\n   *\n   * `emailToken` comes from `accounts.emailVerify` — S0 proves the mailbox\n   * BEFORE the ceremony, so there is no half-registered account to clean up\n   * and a conflicting address is disclosed only to someone who controls it.\n   * It is REQUIRED: there is no registration path that does not begin with a\n   * verified mailbox. `IdentityImpl` still rejects an empty one by name\n   * (`email-unverified`) as defence in depth for JS callers the type cannot\n   * reach.\n   *\n   * `displayName` is the account's display name, and becomes the WebAuthn\n   * `user.displayName` hint.\n   *\n   * `email` is the address the caller just proved control of, and becomes the\n   * WebAuthn `user.name` hint — the line an OS passkey manager shows as the\n   * account. It is optional only because JS callers exist that cannot supply\n   * it; omitting it leaves the entry reading `scp-wallet`, which identifies\n   * nothing (CEF-AI/wallet#72). Pass the address that `emailToken` attests,\n   * never a separately-typed one.\n   *\n   * Neither field is transmitted: the WebAuthn user entity is consumed by the\n   * authenticator and never appears in `clientDataJSON` or the attestation, so\n   * `email` here creates no second, unverified copy server-side. It is\n   * persisted in the resident credential and so may sync to the platform's\n   * passkey store (iCloud Keychain and equivalents) — which is where a passkey\n   * manager has to read it from to show it at all.\n   */\n  register(opts: { emailToken: string; displayName?: string; email?: string; label?: string }): Promise<void>;\n  /**\n   * `opts.credentialId`, when supplied, pins the WebAuthn `allowCredentials`\n   * to that credential (no discoverable-credential picker) instead of the\n   * vault-snapshot credential this identity would otherwise fall back to.\n   * Used by the step-up ceremony (Task 5 fix), where the vault is closed —\n   * so there is no vault-snapshot credential to pin to — but the caller\n   * (the SDK) already knows which credential the active session belongs to\n   * and must not let the ceremony silently authenticate a different one.\n   * Omitted for a normal login()/step-up-of-a-fresh-session, unchanged from\n   * prior behavior.\n   */\n  login(opts?: { credentialId?: string }): Promise<void>;\n  logout(): void;\n\n  /** Returns a valid JWT; runs the passkey ceremony if needed. */\n  getJwt(): Promise<string>;\n\n  /**\n   * Release any browser-owned handles this identity holds (cross-tab channel,\n   * timers, broadcast listeners). After `dispose()` the identity is unusable.\n   *\n   * Idempotent: callable multiple times safely.\n   */\n  dispose(): void;\n}\n\n/**\n * Signers/* receive this function via a constructor-time closure handoff\n * from a concrete Identity instance. It is exported as a type-only alias so\n * dependents can annotate the closures they receive, but it is intentionally\n * absent from the `Identity` interface — there is no public `sign()` method.\n */\nexport type InternalSign = (chain: Chain, payload: Uint8Array) => Promise<Uint8Array>;\n","/** Spec §3.2 locked decision #14. Permanent for v1; rotate via \"-v2\" suffix. */\nexport const PRF_INPUT_LABEL = 'cere-wallet-prf-v1';\n\n/**\n * SHA-256(\"cere-wallet-prf-v1\") — 32 bytes. Fed to WebAuthn `prf.eval.first`.\n *\n * Hard-coded as a byte literal so this module is browser-safe with NO\n * `node:crypto` dependency — it is evaluated at module-load time, and relying on\n * `createHash` there forced every browser consumer to wire a `node:crypto`\n * polyfill (`vite-plugin-node-polyfills`). The literal is verified to equal\n * `sha256(PRF_INPUT_LABEL)` in `constants.test.ts`, so the constant cannot drift\n * silently. Rotating the label (`-v2`) means recomputing these bytes.\n */\nexport const PRF_INPUT_SEED: Uint8Array = new Uint8Array([\n  241, 55, 252, 128, 200, 121, 231, 173, 149, 167, 104, 105, 124, 215, 6, 125, 113, 218, 113, 114, 238, 51, 232, 185,\n  156, 209, 26, 46, 78, 6, 58, 137,\n]);\n\n/** Spec §3.2. Used as HKDF-SHA256 info to derive the secp256k1 32-byte secret. */\nexport const EVM_HKDF_INFO = 'cere-wallet-evm-secp256k1-v1';\n","/**\n * Runtime support detection for WebAuthn + PRF.\n *\n * Spec §3.7. Real PRF capability is only knowable from a completed ceremony\n * (via `clientExtensionResults.prf.enabled` or `prf.results.first`). This\n * pre-check returns the best signal we can get without spending a credential.\n */\n\n/**\n * The key PRF occupies in a `PublicKeyCredential.getClientCapabilities()` map.\n *\n * Singular `extension:`, matching the rest of the family the map exposes\n * (`extension:appid`, `extension:credBlob`, `extension:hmacCreateSecret`, …).\n * There is no `extensions:` prefix in the map and there never was — a plural\n * spelling here is not a legacy form, it is a key no browser has ever\n * produced, and reading it makes the whole pre-check `undefined === false`,\n * i.e. permanently optimistic. That is exactly the bug #80 fixed.\n *\n * Exported so the pin in `tests-e2e/prf-capability-key.spec.ts` (in the wallet\n * repo -- not part of this package) can assert THIS value against a capability\n * map taken from a real browser, rather than against a literal a typo would\n * travel with.\n */\nexport const PRF_CAPABILITY_KEY = 'extension:prf';\n\nexport interface PrfSupportResult {\n  webAuthn: boolean;\n  /**\n   * Whether a platform authenticator (Touch ID / Windows Hello) is enrolled.\n   * INFORMATIONAL ONLY — it is NOT a precondition for using the wallet, which\n   * also works with roaming security keys and phone passkeys (via hybrid).\n   */\n  platformAuthenticator: boolean;\n  /**\n   * Best-effort PRF signal. `true` unless we can prove otherwise: modern\n   * browsers expose `PublicKeyCredential.getClientCapabilities()`, which gives\n   * a definitive `extension:prf` answer. When that API is absent (older\n   * browsers) we stay optimistic and let the ceremony be the final arbiter\n   * (it throws `prf-unsupported` if the authenticator returns no PRF output).\n   */\n  prfPotentiallySupported: boolean;\n}\n\nexport async function detectPrfSupport(): Promise<PrfSupportResult> {\n  const PKC = (globalThis as any).PublicKeyCredential;\n  const webAuthn = typeof PKC !== 'undefined';\n  if (!webAuthn) {\n    return { webAuthn: false, platformAuthenticator: false, prfPotentiallySupported: false };\n  }\n\n  let platformAuthenticator = false;\n  try {\n    platformAuthenticator =\n      typeof PKC.isUserVerifyingPlatformAuthenticatorAvailable === 'function' &&\n      (await PKC.isUserVerifyingPlatformAuthenticatorAvailable());\n  } catch {\n    platformAuthenticator = false;\n  }\n\n  // Definitive PRF capability when the browser supports getClientCapabilities\n  // (Chrome 133+, Safari 18+). Only a hard `false` blocks; absence keeps us\n  // optimistic. We deliberately do NOT require a platform authenticator here.\n  let prfPotentiallySupported = true;\n  try {\n    if (typeof PKC.getClientCapabilities === 'function') {\n      const caps = await PKC.getClientCapabilities();\n      if (caps && caps[PRF_CAPABILITY_KEY] === false) {\n        prfPotentiallySupported = false;\n      }\n    }\n  } catch {\n    // Keep optimistic; the ceremony will surface prf-unsupported if needed.\n  }\n\n  return { webAuthn, platformAuthenticator, prfPotentiallySupported };\n}\n\n/**\n * Inspect a `PublicKeyCredential.getClientExtensionResults()` payload to\n * determine whether PRF actually worked in the ceremony. Spec §3.7 calls this\n * \"final answer; the pre-check above is best-effort only\".\n */\nexport function isPrfSupportedResult(extensions: {\n  prf?: { enabled?: boolean; results?: { first?: Uint8Array } };\n}): boolean {\n  const prf = extensions?.prf;\n  if (!prf) return false;\n  if (prf.enabled === true) return true;\n  if (prf.results?.first instanceof Uint8Array) return true;\n  return false;\n}\n","import bs58 from 'bs58';\nimport { blake2b } from '@noble/hashes/blake2b';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { concatBytes } from '@noble/hashes/utils';\n\n/** Cere's SS58 network prefix. Spec §3.2. */\nexport const CERE_SS58_PREFIX = 54;\n\nconst SS58PRE = new TextEncoder().encode('SS58PRE');\n\n/**\n * SS58 address for the Cere network.\n *\n * SS58 spec (Substrate): for prefixes 0–63 the format is:\n *   [prefix byte] || [32-byte pubkey] || [2-byte checksum]\n * where checksum = blake2b-512(\"SS58PRE\" || prefix || pubkey)[0..2].\n * The whole thing is base58-encoded. We use prefix 54 (Cere) which fits in\n * the 1-byte encoding (< 64).\n *\n * @param edPubkey 32-byte Ed25519 public key.\n */\nexport function deriveCereAddress(edPubkey: Uint8Array): string {\n  if (edPubkey.length !== 32) {\n    throw new Error(`expected 32-byte Ed25519 pubkey, got ${edPubkey.length}`);\n  }\n  const prefix = new Uint8Array([CERE_SS58_PREFIX]);\n  const body = concatBytes(prefix, edPubkey);\n  const checksum = blake2b(concatBytes(SS58PRE, body), { dkLen: 64 }).slice(0, 2);\n  const payload = concatBytes(body, checksum);\n  return bs58.encode(payload);\n}\n\n/**\n * Solana address: base58(Ed25519 pubkey). No prefix, no checksum.\n *\n * @param edPubkey 32-byte Ed25519 public key.\n */\nexport function deriveSolanaAddress(edPubkey: Uint8Array): string {\n  if (edPubkey.length !== 32) {\n    throw new Error(`expected 32-byte Ed25519 pubkey, got ${edPubkey.length}`);\n  }\n  return bs58.encode(edPubkey);\n}\n\n/**\n * Recover the raw 32-byte Ed25519 public key from a Solana address.\n *\n * The Solana address is `base58(edPubkey)` with no prefix or checksum\n * ({@link deriveSolanaAddress}), so a base58 decode yields the pubkey directly.\n * The public key is not secret — it IS the address — so this introduces no key\n * exposure beyond what the address already reveals; the session keys never\n * leave `SessionVault`. The same Ed25519 key backs the Cere SS58 address.\n *\n * @param solanaAddress base58 Solana address.\n * @returns the 32-byte Ed25519 public key.\n */\nexport function decodeEd25519Pubkey(solanaAddress: string): Uint8Array {\n  const pubkey = bs58.decode(solanaAddress);\n  if (pubkey.length !== 32) {\n    throw new Error(`expected a 32-byte Ed25519 pubkey from the Solana address, got ${pubkey.length}`);\n  }\n  return pubkey;\n}\n\n/**\n * EVM address: '0x' + last 20 bytes of keccak256(secp256k1 uncompressed pubkey X||Y).\n *\n * Accepts either 64-byte (raw X||Y) or 65-byte (0x04||X||Y) input. The 0x04\n * prefix byte is stripped if present.\n *\n * @param secpPubkey secp256k1 public key.\n */\nexport function deriveEvmAddress(secpPubkey: Uint8Array): string {\n  let xy: Uint8Array;\n  if (secpPubkey.length === 65 && secpPubkey[0] === 0x04) {\n    xy = secpPubkey.slice(1);\n  } else if (secpPubkey.length === 64) {\n    xy = secpPubkey;\n  } else {\n    throw new Error(`expected 64-byte (X||Y) or 65-byte (0x04||X||Y) secp256k1 pubkey, got ${secpPubkey.length}`);\n  }\n  const hash = keccak_256(xy);\n  const last20 = hash.slice(12);\n  return '0x' + Buffer.from(last20).toString('hex');\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { secp256k1 } from '@noble/curves/secp256k1';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { EVM_HKDF_INFO } from './constants';\n\nexport interface DerivedKeys {\n  /** Ed25519 32-byte secret key seed; used for Cere + Solana signing. */\n  edSeed: Uint8Array;\n  /** secp256k1 32-byte secret key; used for EVM signing. */\n  secpKey: Uint8Array;\n  /** 32-byte Ed25519 public key derived from edSeed. */\n  edPubkey: Uint8Array;\n  /** 65-byte uncompressed secp256k1 public key (0x04 || X || Y) derived from secpKey. */\n  secpPubkeyUncompressed: Uint8Array;\n}\n\n/**\n * Derive the v2 session keys from a WebAuthn PRF output. Spec §3.2.\n *\n *   edSeed  = prfOutput\n *   secpKey = HKDF-SHA256(prfOutput, salt=\"\", info=\"cere-wallet-evm-secp256k1-v1\", L=32)\n *\n * @param prfOutput 32-byte PRF output from `prf.results.first`.\n */\nexport function derivedKeys(prfOutput: Uint8Array): DerivedKeys {\n  if (prfOutput.length !== 32) {\n    throw new Error(`expected 32-byte PRF output, got ${prfOutput.length}`);\n  }\n  const edSeed = new Uint8Array(prfOutput); // copy to detach from caller's buffer\n  const secpKey = hkdf(sha256, prfOutput, new Uint8Array(0), EVM_HKDF_INFO, 32);\n\n  const edPubkey = ed25519.getPublicKey(edSeed);\n  // @noble/curves exposes both compressed and uncompressed forms; we want uncompressed.\n  const secpPubkeyUncompressed = secp256k1.getPublicKey(secpKey, false);\n\n  return { edSeed, secpKey, edPubkey, secpPubkeyUncompressed };\n}\n","import { xsalsa20poly1305 } from '@noble/ciphers/salsa';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { WalletError } from '@cef-ai/wallet-api-client';\n\n/**\n * HKDF info for the root-wrapping key. Deliberately distinct from the signing\n * derivation: `derive.ts` uses the PRF output AS the ed25519 seed, so wrapping\n * under an unlabelled copy of it would encrypt the root under the key it\n * protects. Rotating this label invalidates every stored envelope, so it does\n * not change without a migration.\n */\nexport const ROOT_WRAP_HKDF_INFO = 'cere-wallet-root-wrap-v1';\n\n/**\n * The `wrapVersion` this client writes into every envelope it creates\n * (`RootEnvelopeWire.wrapVersion`). Bump this, and add an entry to\n * `ROOT_WRAP_LABELS_BY_VERSION`, when a new label is introduced — never\n * repoint an existing version string at a different label.\n */\nexport const ROOT_WRAP_VERSION = 'v1';\n\n/**\n * Closed map from a `wrapVersion` wire string to its compiled-in HKDF label.\n * This is the ONLY place a `wrapVersion` value is turned into key-derivation\n * input. The map exists so that a server-supplied version string is always\n * looked up against a fixed, compiled-in set rather than used directly as\n * HKDF `info` -- an unrecognised value must be a named, loud error (see\n * `deriveWrapKeyForVersion`), never a silently-derived key under attacker- or\n * server-chosen material.\n *\n * Typed via `as const` (not `Readonly<Record<string, string>>`) so the key\n * set is closed at the TYPE level, not just at runtime: this repo does not\n * enable `noUncheckedIndexedAccess`, so indexing a `Record<string, string>`\n * with an arbitrary `string` types as `string`, not `string | undefined` --\n * the `=== undefined` guard that used to gate this would still work, but the\n * compiler would have no opinion if a later edit deleted it. With `as const`,\n * indexing this object with a plain `string` (as `wrapVersion` off the wire\n * necessarily is) is a compile error, so the only way in is the `in`-narrowed\n * `isKnownWrapVersion` guard below -- deleting *that* guard is what now fails\n * `tsc`, not a silently-widened lookup.\n */\nconst ROOT_WRAP_LABELS_BY_VERSION = {\n  [ROOT_WRAP_VERSION]: ROOT_WRAP_HKDF_INFO,\n} as const;\n\n/** The closed set of `wrapVersion` strings this build knows how to unwrap. */\ntype KnownWrapVersion = keyof typeof ROOT_WRAP_LABELS_BY_VERSION;\n\n/**\n * Type guard narrowing an arbitrary wire `wrapVersion` string down to\n * `KnownWrapVersion`. This is the only place that narrowing happens --\n * `deriveWrapKeyForVersion` cannot index `ROOT_WRAP_LABELS_BY_VERSION` with\n * an unnarrowed `string` without a type error, so this check cannot be\n * silently removed without the compiler noticing.\n */\nfunction isKnownWrapVersion(version: string): version is KnownWrapVersion {\n  return Object.prototype.hasOwnProperty.call(ROOT_WRAP_LABELS_BY_VERSION, version);\n}\n\n/**\n * Thrown by `deriveWrapKeyForVersion` for a `wrapVersion` outside the\n * compiled-in set.\n *\n * A `WalletError` subclass, not a bare `Error`: the SPA's `walletErrorMessage`\n * only recognises `WalletError`, so anything else reaches the user as raw\n * technical text, and a caller with only a message string to match on cannot\n * tell this apart from the other envelope failures. Subclassing keeps BOTH\n * discriminators — `instanceof UnrecognisedWrapVersionError` and\n * `err.code === 'root-wrap-version-unknown'` — and `wrapVersion` still names\n * the offending value programmatically.\n */\nexport class UnrecognisedWrapVersionError extends WalletError {\n  constructor(public readonly wrapVersion: string) {\n    super('root-wrap-version-unknown', `unrecognised root-wrap version: ${JSON.stringify(wrapVersion)}`);\n    this.name = 'UnrecognisedWrapVersionError';\n  }\n}\n\n/**\n * Thrown when an envelope is not wrapped by the credential's PRF output at all\n * — `wrapSource: 'password'`, say, which belongs to the password-recovery path\n * and cannot be opened with a credential key.\n *\n * Its own condition rather than a decrypt failure: attacking a password\n * envelope with a PRF-derived key is fail-safe (it will not open, so no wrong\n * root is ever derived) but it would be diagnosed as \"this envelope is\n * damaged\" when the truth is \"this is the wrong kind of envelope\" — a\n * difference between \"your data is corrupt\" and \"use your recovery password\".\n */\nexport class UnsupportedWrapSourceError extends WalletError {\n  constructor(public readonly wrapSource: string, public readonly credentialId: string) {\n    super(\n      'root-wrap-source-unsupported',\n      `root envelope for credential ${JSON.stringify(credentialId)} is wrapped by ${JSON.stringify(\n        wrapSource,\n      )}, not by this credential's PRF output`,\n    );\n    this.name = 'UnsupportedWrapSourceError';\n  }\n}\n\n/**\n * Thrown when an envelope's ciphertext will not open under the wrap key derived\n * from a credential's PRF output — a wrong credential, or a corrupt/tampered\n * envelope. Deliberately its own type so the three envelope failures stay three\n * diagnoses:\n *\n *   - `root-envelope-missing` — the server holds no envelope for this\n *     credential (it was never enrolled, or the row is gone);\n *   - `UnrecognisedWrapVersionError` / `root-wrap-version-unknown` — an envelope\n *     exists but names a label this build does not know (a client too old for a\n *     rotated label);\n *   - `UnsupportedWrapSourceError` / `root-wrap-source-unsupported` — the\n *     envelope is not wrapped by a credential PRF at all;\n *   - `RootUnwrapFailedError` / `root-unwrap-failed` — the envelope is the right\n *     kind and a known label, and still will not open.\n *\n * Collapsing them would leave a caller unable to tell \"this device was never\n * added to the account\" from \"upgrade this client\" from \"use your recovery\n * password\" from \"this envelope is damaged\" — four different next actions. The underlying failure is preserved\n * on `cause`; nothing is swallowed.\n */\nexport class RootUnwrapFailedError extends WalletError {\n  constructor(public readonly credentialId: string, options?: { cause?: unknown }) {\n    super('root-unwrap-failed', `root envelope failed to decrypt for credential ${JSON.stringify(credentialId)}`);\n    this.name = 'RootUnwrapFailedError';\n    // `WalletError`'s constructor takes no `ErrorOptions`, so carry the cause\n    // across by hand rather than dropping it — the primitive's own failure is\n    // the only detail that says WHY the ciphertext would not open.\n    if (options && 'cause' in options) {\n      (this as Error).cause = options.cause;\n    }\n  }\n}\n\n/**\n * Derives the root-wrap key for a given `wrapVersion`, resolving it through\n * the compiled-in `ROOT_WRAP_LABELS_BY_VERSION` map. The `wrapVersion` string\n * itself is NEVER passed into the HKDF as `info` -- only the label it maps\n * to is. This is deliberate defense in depth: a leaked or server-supplied\n * `wrapVersion` on its own is not enough to move the derived key, even\n * though the label is not secret and even though an attacker would still\n * need the credential's PRF output to do anything with it. Deciding this\n * once, here, means no future call site has to re-derive the same judgment.\n *\n * Throws `UnrecognisedWrapVersionError` -- distinguishable from a decryption\n * failure -- for any version outside the known set, so \"unknown version\",\n * \"wrong credential\", and \"corrupt ciphertext\" stay three separate,\n * diagnosable conditions instead of collapsing into one generic failure.\n */\nexport function deriveWrapKeyForVersion(prfOutput: Uint8Array, wrapVersion: string): Uint8Array {\n  if (!isKnownWrapVersion(wrapVersion)) {\n    throw new UnrecognisedWrapVersionError(wrapVersion);\n  }\n  const label = ROOT_WRAP_LABELS_BY_VERSION[wrapVersion];\n  return deriveWithLabel(prfOutput, label);\n}\n\nexport function deriveWrapKey(prfOutput: Uint8Array): Uint8Array {\n  return deriveWithLabel(prfOutput, ROOT_WRAP_HKDF_INFO);\n}\n\nfunction deriveWithLabel(prfOutput: Uint8Array, label: string): Uint8Array {\n  if (prfOutput.length !== 32) {\n    // A `WalletError`, not a bare `Error`, for the same reason\n    // `UnrecognisedWrapVersionError` is one: `walletErrorMessage` renders a\n    // bare `Error`'s own `message` straight into the UI, so an internal\n    // sentence about byte lengths would reach the reader as user-facing copy.\n    // `validation` is how this file's other caller-fault failures are raised.\n    // The length stays in the detail — it is the only thing that says WHAT was\n    // wrong, and it is for the log, not for the reader.\n    throw new WalletError('validation', `expected 32-byte PRF output, got ${prfOutput.length}`);\n  }\n  return hkdf(sha256, prfOutput, new Uint8Array(0), label, 32);\n}\n\nexport interface RootWrapper {\n  wrap(root: Uint8Array, key: Uint8Array): { ciphertext: Uint8Array; nonce: Uint8Array };\n}\nexport interface RootUnwrapper {\n  unwrap(ciphertext: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;\n}\n\n/**\n * XSalsa20-Poly1305 (NaCl secretbox shape), matching the envelope format\n * wallet-api stores and the format PR #94's recovery envelope uses — one\n * primitive for device envelopes and recovery envelopes alike.\n *\n * Exposed behind the `RootWrapper`/`RootUnwrapper` interfaces so a consumer\n * can substitute its own wrapper implementation; this one is the provided\n * default, not a mandated one.\n */\nexport function createSecretboxWrapper(): RootWrapper & RootUnwrapper {\n  return {\n    wrap(root, key) {\n      const nonce = crypto.getRandomValues(new Uint8Array(24));\n      // noble returns a subarray view into a larger internal ArrayBuffer\n      // (byteOffset 16). Copy it out so callers that reach for `.buffer`\n      // (e.g. `Buffer.from(ciphertext.buffer)`) get the right bytes -- this\n      // value crosses the injection seam into SPA code we do not control.\n      const ciphertext = Uint8Array.from(xsalsa20poly1305(key, nonce).encrypt(root));\n      return { ciphertext, nonce };\n    },\n    unwrap(ciphertext, nonce, key) {\n      // Same view-into-larger-buffer issue as encrypt, mirrored on decrypt --\n      // and here the view holds the ACCOUNT ROOT in the clear.\n      const plaintext = xsalsa20poly1305(key, nonce).decrypt(ciphertext);\n      try {\n        // `Uint8Array.from` COPIES; the returned root shares no memory with\n        // `plaintext`, which is what makes the wipe below safe. (Copying is\n        // also what callers reaching for `.buffer` need -- see `wrap`.)\n        return Uint8Array.from(plaintext);\n      } finally {\n        // Wipe the primitive's own output view. It is a second plaintext copy\n        // of the root sitting in a buffer this module does not own and cannot\n        // otherwise reach again; the caller's copy is the one that lives on.\n        // `key` is NOT wiped here: it belongs to the caller, who is the only\n        // one who knows whether it is still needed (see\n        // `unwrapRootForCredential`, which does wipe it).\n        plaintext.fill(0);\n      }\n    },\n  };\n}\n","/**\n * Base64url (RFC 4648 §5) helpers used by the WebAuthn ceremony adapter.\n * Trailing `=` padding is stripped on encode; both padded and unpadded inputs\n * are accepted on decode.\n */\n\n/** Encode a Uint8Array or ArrayBuffer as a base64url string (no padding). */\nexport function bytesToB64u(input: Uint8Array | ArrayBuffer): string {\n  const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);\n  let bin = '';\n  for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n  const b64 = btoa(bin);\n  return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/** Decode a base64url string into a Uint8Array. Padding is optional. */\nexport function b64uToBytes(s: string): Uint8Array {\n  const b64 = s.replace(/-/g, '+').replace(/_/g, '/');\n  const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n  const bin = atob(padded); // throws DOMException on invalid input — correct behavior\n  const out = new Uint8Array(bin.length);\n  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n  return out;\n}\n","import { bytesToB64u, b64uToBytes } from './base64url';\n\n/**\n * `hints` is a top-level field on the WebAuthn creation/request options\n * (WebAuthn L3), but our TS DOM lib doesn't ship it yet. Declare-merge the\n * precise W3C union so the ceremony can request authenticator hints without\n * casts. Drop this augmentation once the workspace lib.dom.d.ts ships `hints`.\n */\ndeclare global {\n  interface PublicKeyCredentialCreationOptions {\n    hints?: ('client-device' | 'hybrid' | 'security-key')[];\n  }\n  interface PublicKeyCredentialRequestOptions {\n    hints?: ('client-device' | 'hybrid' | 'security-key')[];\n  }\n}\n\n/**\n * Adapter that runs WebAuthn ceremonies. Production implementations call\n * `navigator.credentials.create/get`; the test implementation in\n * `SoftAuthenticator` produces deterministic results without browser APIs.\n *\n * Spec §3.7. The `prfOutput` field is the result of the WebAuthn PRF extension\n * — present when the authenticator supports PRF, absent otherwise.\n */\nexport interface CeremonyAdapter {\n  register(opts: RegisterOptions): Promise<RegisterResult>;\n  login(opts: LoginOptions): Promise<LoginResult>;\n}\n\nexport interface RegisterOptions {\n  rpId: string;\n  challenge: Uint8Array;\n  userHandle: Uint8Array;\n  /** 32-byte PRF input (typically `PRF_INPUT_SEED`). */\n  prfInput: Uint8Array;\n  /**\n   * Legacy single label. Used as a fallback for both `user.displayName` and\n   * `user.name` when `name`/`email` are not supplied. Prefer `name` + `email`.\n   */\n  label?: string;\n  /** Friendly account name → WebAuthn `user.displayName` (e.g. \"Alex Müller\"). */\n  name?: string;\n  /**\n   * Account identifier → WebAuthn `user.name` (e.g. \"alex@example.com\"). This is\n   * the field most OS/browser passkey managers surface to distinguish credentials.\n   */\n  email?: string;\n}\n\nexport interface RegisterResult {\n  credentialId: string; // base64url\n  clientDataJSON: string; // base64url\n  attestationObject: string; // base64url\n  transports: string[];\n  /** Present iff PRF extension worked. */\n  prfOutput?: Uint8Array;\n}\n\nexport interface LoginOptions {\n  rpId: string;\n  challenge: Uint8Array;\n  /** Optional: if absent, the authenticator picks a credential. Present from us. */\n  credentialId?: string;\n  prfInput: Uint8Array;\n}\n\nexport interface LoginResult {\n  credentialId: string;\n  clientDataJSON: string;\n  authenticatorData: string;\n  signature: string;\n  prfOutput?: Uint8Array;\n}\n\nexport interface WebAuthnCeremonyAdapterOptions {\n  /**\n   * Override for the WebAuthn credentials manager. Defaults to\n   * `globalThis.navigator?.credentials`. Tests inject a mock.\n   */\n  credentials?: CredentialsContainer;\n}\n\n/**\n * Authenticator selection `hints` (WebAuthn L3, Chrome/Edge/Safari 18+).\n *\n * These bias the browser's passkey picker toward authenticators that\n * reliably support the PRF extension — platform authenticators (Touch ID /\n * Windows Hello / Android), phone passkeys (hybrid), and security keys.\n *\n * Hints are *preferences, not filters*: a software password-manager passkey\n * (e.g. Bitwarden, 1Password) still appears in the picker, just de-prioritized.\n * On a machine with Touch ID enrolled this makes \"This device\" the default\n * choice, so users no longer land on Bitwarden's passkey (which can't satisfy\n * PRF and fails the ceremony with `prf-unsupported`). The post-ceremony\n * `prfOutput` check remains the real capability gate.\n */\nconst PRF_CAPABLE_HINTS = ['client-device', 'hybrid', 'security-key'] as const;\n\n/**\n * Production WebAuthn ceremony adapter. Calls `navigator.credentials.create/get`\n * with the PRF extension. Spec §3.7.\n *\n * - `register`: requires a platform authenticator, user verification, Ed25519\n *   (`alg: -8`), and the PRF extension with the wallet's input seed.\n * - `login`: allows a specific credentialId if known; otherwise lets the\n *   authenticator pick (resident key).\n *\n * Returned ArrayBuffers are base64url-encoded for API transport.\n */\nexport class WebAuthnCeremonyAdapter implements CeremonyAdapter {\n  private readonly credentials?: CredentialsContainer;\n\n  constructor(opts: WebAuthnCeremonyAdapterOptions = {}) {\n    this.credentials = opts.credentials ?? (globalThis as any).navigator?.credentials;\n  }\n\n  async register(opts: RegisterOptions): Promise<RegisterResult> {\n    if (!this.credentials) {\n      throw new Error('WebAuthnCeremonyAdapter: navigator.credentials is unavailable');\n    }\n    const cred = (await this.credentials.create({\n      publicKey: {\n        challenge: opts.challenge,\n        rp: { id: opts.rpId, name: opts.rpId },\n        user: {\n          id: opts.userHandle,\n          // WebAuthn convention: `name` is the account identifier (email), shown\n          // by passkey managers; `displayName` is the friendly name. Fall back to\n          // the legacy single `label`, then the product defaults.\n          name: opts.email ?? opts.label ?? 'scp-wallet',\n          displayName: opts.name ?? opts.label ?? 'SCP Wallet',\n        },\n        pubKeyCredParams: [\n          { alg: -8, type: 'public-key' }, // EdDSA (Ed25519 security keys) — preferred\n          { alg: -7, type: 'public-key' }, // ES256 (Apple/Windows/Android platform authenticators)\n        ],\n        authenticatorSelection: {\n          // No `authenticatorAttachment`: allow platform authenticators\n          // (Touch ID / Windows Hello), roaming security keys, and phone\n          // passkeys via hybrid — PRF works across all of them, and the\n          // pubKeyCredParams above explicitly prefer Ed25519 security keys.\n          // The post-ceremony PRF result is the real capability gate.\n          userVerification: 'required',\n          residentKey: 'preferred',\n        },\n        // Bias the picker toward PRF-capable authenticators so the user\n        // doesn't default onto a password-manager passkey. See PRF_CAPABLE_HINTS.\n        hints: [...PRF_CAPABLE_HINTS],\n        timeout: 60_000,\n        extensions: { prf: { eval: { first: opts.prfInput } } } as AuthenticationExtensionsClientInputs,\n      },\n    })) as PublicKeyCredential | null;\n    if (!cred) {\n      throw new Error('WebAuthnCeremonyAdapter.register: credentials.create returned null');\n    }\n\n    const response = cred.response as AuthenticatorAttestationResponse;\n    const transports = typeof response.getTransports === 'function' ? response.getTransports() : [];\n    const ext = cred.getClientExtensionResults() as { prf?: { results?: { first?: ArrayBuffer } } };\n    const prfFirst = ext?.prf?.results?.first;\n\n    return {\n      credentialId: cred.id,\n      clientDataJSON: bytesToB64u(response.clientDataJSON),\n      attestationObject: bytesToB64u(response.attestationObject),\n      transports,\n      prfOutput: prfFirst ? new Uint8Array(prfFirst) : undefined,\n    };\n  }\n\n  async login(opts: LoginOptions): Promise<LoginResult> {\n    if (!this.credentials) {\n      throw new Error('WebAuthnCeremonyAdapter: navigator.credentials is unavailable');\n    }\n    const cred = (await this.credentials.get({\n      publicKey: {\n        challenge: opts.challenge,\n        rpId: opts.rpId,\n        allowCredentials: opts.credentialId ? [{ id: b64uToBytes(opts.credentialId), type: 'public-key' }] : undefined,\n        userVerification: 'required',\n        // Bias first-login-on-device (no allowCredentials) toward PRF-capable\n        // authenticators, mirroring register. See PRF_CAPABLE_HINTS.\n        hints: [...PRF_CAPABLE_HINTS],\n        timeout: 60_000,\n        extensions: { prf: { eval: { first: opts.prfInput } } } as AuthenticationExtensionsClientInputs,\n      },\n    })) as PublicKeyCredential | null;\n    if (!cred) {\n      throw new Error('WebAuthnCeremonyAdapter.login: credentials.get returned null');\n    }\n\n    const response = cred.response as AuthenticatorAssertionResponse;\n    const ext = cred.getClientExtensionResults() as { prf?: { results?: { first?: ArrayBuffer } } };\n    const prfFirst = ext?.prf?.results?.first;\n\n    return {\n      credentialId: cred.id,\n      clientDataJSON: bytesToB64u(response.clientDataJSON),\n      authenticatorData: bytesToB64u(response.authenticatorData),\n      signature: bytesToB64u(response.signature),\n      prfOutput: prfFirst ? new Uint8Array(prfFirst) : undefined,\n    };\n  }\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { concatBytes } from '@noble/hashes/utils';\nimport type { CeremonyAdapter, RegisterOptions, RegisterResult, LoginOptions, LoginResult } from './ceremony';\n\n/** base64url encode without padding. */\nfunction b64u(bytes: Uint8Array): string {\n  return Buffer.from(bytes).toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/** Inverse of `b64u`. */\nfunction unb64u(value: string): Uint8Array {\n  return new Uint8Array(Buffer.from(value.replace(/-/g, '+').replace(/_/g, '/'), 'base64'));\n}\n\ninterface StoredCredential {\n  credentialId: string;\n  edSeed: Uint8Array;\n  prfSecret: Uint8Array;\n}\n\n/**\n * Somewhere a `SoftAuthenticator`'s credentials can outlive the JS context\n * that created them. Two strings in, two strings out — the authenticator owns\n * the encoding, so a store cannot corrupt a credential by mis-serialising it.\n *\n * Exists because a real platform authenticator is shared by every window on\n * the origin, and the in-memory Map is not: a credential created in a\n * registration popup was invisible to the iframe that then had to re-derive\n * from it, and to the step-up popup opened later. That gap silently disabled\n * e2e coverage of the whole registration path (CEF-AI/wallet#91).\n */\nexport interface SoftCredentialStore {\n  read(): string | null;\n  write(value: string): void;\n}\n\n/**\n * `localStorage`-backed store, for the e2e SPA build only.\n *\n * Per-test isolation comes from Playwright, which gives every test its own\n * BrowserContext and therefore its own `localStorage` — while a popup opened\n * BY the page shares that context, which is exactly the sharing this exists\n * for. Nothing here needs to clear between tests; if a spec ever drives two\n * contexts and wants them to share, that is a different mechanism.\n *\n * Returns a store that silently no-ops when `localStorage` is unreachable\n * (a sandboxed frame, a browser with site data blocked): the authenticator\n * then behaves exactly as it did before this existed — per-context memory —\n * rather than failing the ceremony outright.\n */\nexport function browserCredentialStore(key = 'scp-wallet-e2e:soft-authenticator'): SoftCredentialStore {\n  return {\n    read() {\n      try {\n        return globalThis.localStorage?.getItem(key) ?? null;\n      } catch {\n        return null;\n      }\n    },\n    write(value) {\n      try {\n        globalThis.localStorage?.setItem(key, value);\n      } catch {\n        /* no storage: degrade to per-context memory */\n      }\n    },\n  };\n}\n\n/** The JSON shape `SoftCredentialStore` carries. Internal to this module. */\ninterface SerialisedState {\n  regCounter: number;\n  creds: Array<{ credentialId: string; edSeed: string; prfSecret: string }>;\n}\n\nexport interface SoftAuthenticatorOptions {\n  /**\n   * If provided, derive the per-registration `edSeed`, `prfSecret`, and\n   * `credentialId` deterministically from this seed (mixed with a monotonic\n   * counter so multiple register() calls on the same instance still produce\n   * distinct credentials). Used by Playwright e2e specs that pin server-side\n   * address stubs against the SoftAuthenticator's output. Production code\n   * never sets this — the `useSoft` branch in `createIdentity.ts` is itself\n   * forbidden in production builds.\n   *\n   * Default: undefined (each register() draws fresh CSPRNG bytes, matching\n   * the pre-cycle-8.1 behavior used by unit tests in `ceremony.test.ts`).\n   */\n  seed?: string;\n\n  /**\n   * When the caller invokes login() without a credentialId, fall back to the\n   * most-recently-registered credential in memory. Default: false (login()\n   * throws when credentialId is missing, matching pre-cycle-8 behavior).\n   *\n   * Required for e2e flows that simulate the user signing back in after the\n   * vault has been cleared (post-logout re-login): the wallet has discarded\n   * the credentialId locally, but the soft authenticator's in-memory creds\n   * map still holds the registration, and the spec test needs login() to\n   * succeed anyway. Production code must never enable this — real WebAuthn's\n   * no-allowCredentials path shows an OS credential picker; the fallback\n   * here is a deterministic test convenience, not a real picker, so leaving\n   * it on by default would silently let a single registered credential\n   * impersonate any login.\n   */\n  fallbackToLastRegistered?: boolean;\n\n  /**\n   * Where credentials live. Omitted (the default) they live in this instance's\n   * memory, which is what every unit test wants and what this class always\n   * did. The e2e SPA build passes `browserCredentialStore()` so that a\n   * credential registered in a popup is visible to the iframe and to later\n   * popups — as a real platform authenticator's would be.\n   *\n   * The registration counter is stored alongside the credentials, so a second\n   * window registering against the same seed produces the NEXT credential\n   * rather than colliding with the first.\n   */\n  store?: SoftCredentialStore;\n}\n\n/**\n * Deterministic in-memory WebAuthn authenticator for tests. NOT for production.\n *\n * - Stores credentials in a private Map keyed by credentialId.\n * - `prfOutput` is computed as HKDF-SHA256(prfSecret, salt=\"\", info=prfInput, L=32),\n *   so for a given credential and prfInput the result is stable across login calls.\n * - `signature` is a valid Ed25519 signature over `authenticatorData || sha256(clientDataJSON)`.\n *\n * The output shapes match the real WebAuthn response well enough that the\n * `Identity` integration tests (Task 6) can drive register → login → derive\n * → cross-check without a real browser.\n */\nexport class SoftAuthenticator implements CeremonyAdapter {\n  private readonly creds: Map<string, StoredCredential> = new Map();\n  private readonly seedBytes: Uint8Array | null;\n  private readonly fallbackToLastRegistered: boolean;\n  private readonly store: SoftCredentialStore | null;\n  /** Monotonic counter mixed into the seed-derived material so successive\n   *  register() calls on a seeded instance produce distinct credentials. */\n  private regCounter = 0;\n\n  constructor(opts: SoftAuthenticatorOptions = {}) {\n    this.seedBytes = opts.seed != null ? new TextEncoder().encode(opts.seed) : null;\n    this.fallbackToLastRegistered = opts.fallbackToLastRegistered === true;\n    this.store = opts.store ?? null;\n  }\n\n  /**\n   * Pull the shared store into this instance's Map before any read of it.\n   *\n   * Re-read every time rather than once at construction: the other window may\n   * have registered since. Merges rather than replaces, so a credential this\n   * instance created is never lost to a stale write from elsewhere — the\n   * store is last-writer-wins per key, and these keys are content-addressed\n   * (a credentialId is derived from the material it names), so a merge cannot\n   * produce a credential whose id disagrees with its bytes.\n   */\n  private hydrate(): void {\n    if (!this.store) return;\n    let state: SerialisedState;\n    try {\n      const raw = this.store.read();\n      if (!raw) return;\n      state = JSON.parse(raw) as SerialisedState;\n    } catch {\n      // A store that throws (site data blocked, a sandboxed frame) or holds\n      // corrupt JSON is a harness problem, not a product one. Fall back to\n      // per-context memory — the behaviour before this store existed — rather\n      // than failing a WebAuthn ceremony, which would point every reader at\n      // entirely the wrong layer.\n      return;\n    }\n    for (const c of state.creds ?? []) {\n      if (this.creds.has(c.credentialId)) continue;\n      this.creds.set(c.credentialId, {\n        credentialId: c.credentialId,\n        edSeed: unb64u(c.edSeed),\n        prfSecret: unb64u(c.prfSecret),\n      });\n    }\n    // The counter is shared state too: two windows seeded identically must not\n    // both mint `reg-0`.\n    if (typeof state.regCounter === 'number' && state.regCounter > this.regCounter) {\n      this.regCounter = state.regCounter;\n    }\n  }\n\n  /** Publish this instance's credentials + counter back to the shared store. */\n  private persist(): void {\n    if (!this.store) return;\n    const state: SerialisedState = {\n      regCounter: this.regCounter,\n      creds: Array.from(this.creds.values()).map((c) => ({\n        credentialId: c.credentialId,\n        edSeed: b64u(c.edSeed),\n        prfSecret: b64u(c.prfSecret),\n      })),\n    };\n    try {\n      this.store.write(JSON.stringify(state));\n    } catch {\n      // Same reasoning as `hydrate`: an unwritable store degrades this\n      // instance to memory, it does not break the ceremony in progress.\n    }\n  }\n\n  async register(opts: RegisterOptions): Promise<RegisterResult> {\n    // Before minting: another window may already have used this counter value.\n    this.hydrate();\n    // Generate per-credential material. When seeded, derive deterministically\n    // via HKDF-SHA256(seed, salt=<counter>, info=<purpose>) so the e2e specs\n    // can pin server-side address stubs against the resulting addresses.\n    let edSeed: Uint8Array;\n    let prfSecret: Uint8Array;\n    let credentialIdBytes: Uint8Array;\n    if (this.seedBytes) {\n      const salt = new TextEncoder().encode(`reg-${this.regCounter++}`);\n      edSeed = hkdf(sha256, this.seedBytes, salt, 'ed-seed', 32);\n      prfSecret = hkdf(sha256, this.seedBytes, salt, 'prf-secret', 32);\n      credentialIdBytes = hkdf(sha256, this.seedBytes, salt, 'credential-id', 16);\n    } else {\n      edSeed = ed25519.utils.randomPrivateKey();\n      prfSecret = ed25519.utils.randomPrivateKey(); // 32 random bytes\n      credentialIdBytes = ed25519.utils.randomPrivateKey().slice(0, 16); // 16-byte id\n    }\n    const credentialId = b64u(credentialIdBytes);\n\n    this.creds.set(credentialId, { credentialId, edSeed, prfSecret });\n    this.persist();\n\n    // Compose the WebAuthn-shaped artifacts. These are NOT spec-correct attestations;\n    // they're test fixtures whose only requirement is to round-trip through identity.\n    const clientData = {\n      type: 'webauthn.create',\n      challenge: b64u(opts.challenge),\n      origin: `https://${opts.rpId}`,\n      crossOrigin: false,\n    };\n    const clientDataJSON = new TextEncoder().encode(JSON.stringify(clientData));\n    // Not a spec-correct attestation — but it carries the credential id, the\n    // one field a real attestation has that a server actually needs. A real\n    // `register/finish` reads the id out of the authenticator data inside the\n    // attestation object; the client never sends it separately. A stub server\n    // that invents its own id instead leaves the frame pinning\n    // `allowCredentials` to something no authenticator knows, which is what\n    // silently disabled every e2e of the post-registration re-derivation\n    // (CEF-AI/wallet#91). See `credentialIdFromAttestation` in the e2e harness.\n    const attestationObject = new TextEncoder().encode(JSON.stringify({ credentialId }));\n\n    const prfOutput = this.computePrf(prfSecret, opts.prfInput);\n\n    return {\n      credentialId,\n      clientDataJSON: b64u(clientDataJSON),\n      attestationObject: b64u(attestationObject),\n      transports: ['internal'],\n      prfOutput,\n    };\n  }\n\n  async login(opts: LoginOptions): Promise<LoginResult> {\n    // Two modes:\n    // 1. Explicit credentialId (the common path, including IdentityImpl's\n    //    warm-vault re-mint flow): look it up and throw on miss.\n    // 2. No credentialId: by default this is an error (pre-cycle-8 behaviour),\n    //    because a silent \"pick the last one\" is a security trap if it ever\n    //    leaks into production. Callers that genuinely need the fallback\n    //    (Playwright post-logout re-login) opt in via\n    //    `new SoftAuthenticator({ fallbackToLastRegistered: true })`.\n    // The credential may have been registered in another window — the popup\n    // that ran `register()` while this is the iframe re-deriving from it, or a\n    // popup that has since closed.\n    this.hydrate();\n    let stored: StoredCredential | undefined;\n    if (opts.credentialId) {\n      stored = this.creds.get(opts.credentialId);\n      if (!stored) {\n        throw new Error(`SoftAuthenticator.login: unknown credential ${opts.credentialId}`);\n      }\n    } else {\n      if (!this.fallbackToLastRegistered) {\n        throw new Error(\n          'SoftAuthenticator.login: credentialId is required ' +\n            '(set `fallbackToLastRegistered: true` in the constructor to opt into the e2e convenience of picking the most-recently-registered credential)',\n        );\n      }\n      const all = Array.from(this.creds.values());\n      stored = all[all.length - 1];\n      if (!stored) {\n        throw new Error('SoftAuthenticator.login: no credentials registered');\n      }\n    }\n\n    const clientData = {\n      type: 'webauthn.get',\n      challenge: b64u(opts.challenge),\n      origin: `https://${opts.rpId}`,\n      crossOrigin: false,\n    };\n    const clientDataJSON = new TextEncoder().encode(JSON.stringify(clientData));\n\n    // Minimal authenticatorData: 32-byte rpIdHash || 1-byte flags || 4-byte signCount.\n    // We don't need a real rpIdHash for client-side tests.\n    const authenticatorData = new Uint8Array(37);\n    authenticatorData[32] = 0x05; // UP + UV flags\n\n    const clientDataHash = sha256(clientDataJSON);\n    const toSign = concatBytes(authenticatorData, clientDataHash);\n    const signature = ed25519.sign(toSign, stored.edSeed);\n\n    const prfOutput = this.computePrf(stored.prfSecret, opts.prfInput);\n\n    return {\n      credentialId: stored.credentialId,\n      clientDataJSON: b64u(clientDataJSON),\n      authenticatorData: b64u(authenticatorData),\n      signature: b64u(signature),\n      prfOutput,\n    };\n  }\n\n  /** Public-key bytes for a credential. Used by integration tests for cross-checks. */\n  getEd25519PublicKey(credentialId: string): Uint8Array {\n    this.hydrate();\n    const stored = this.creds.get(credentialId);\n    if (!stored) throw new Error(`unknown credential ${credentialId}`);\n    return ed25519.getPublicKey(stored.edSeed);\n  }\n\n  private computePrf(prfSecret: Uint8Array, prfInput: Uint8Array): Uint8Array {\n    // Deterministic per (prfSecret, prfInput) — matches the spirit of how a real\n    // authenticator's PRF extension works (HKDF-style mixing of a per-credential\n    // secret with the caller's input). NOT identical to any real authenticator.\n    return hkdf(sha256, prfSecret, new Uint8Array(0), prfInput, 32);\n  }\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { secp256k1 } from '@noble/curves/secp256k1';\nimport type { DerivedKeys } from './derive';\nimport type { Addresses, Chain } from './types';\n\nexport interface VaultSnapshot {\n  addresses: Addresses;\n  credentialId: string;\n}\n\nexport interface SessionVault {\n  isOpen(): boolean;\n  /** Public-safe view: never includes raw keys. */\n  snapshot(): VaultSnapshot | null;\n  /** Open the vault with derived keys + the addresses they yielded + the credentialId. */\n  set(args: { keys: DerivedKeys; addresses: Addresses; credentialId: string }): void;\n  /** Drop the keys and address state. Safe to call when already closed. */\n  clear(): void;\n  /**\n   * Sign a payload with the chain-appropriate key. Spec §3.6 invariant:\n   * this is the only way the rest of the package can reach the keys.\n   *\n   * - 'cere' / 'solana': Ed25519 signature over the raw payload (64 bytes).\n   * - 'evm': secp256k1 ECDSA over the raw payload (65 bytes: r || s || v).\n   *   The caller (an EVM signer) is responsible for framing (e.g. EIP-191)\n   *   and hashing (keccak256) before passing the digest as the payload.\n   */\n  sign(chain: Chain, payload: Uint8Array): Promise<Uint8Array>;\n}\n\n/**\n * Closure-encapsulated session vault. The keys live in the closure variables\n * `state.edSeed` / `state.secpKey` and are not reachable from outside.\n *\n * Spec §3.4. Lifetime: from `set()` until `clear()` or tab unload.\n */\nexport function createSessionVault(): SessionVault {\n  let state: {\n    edSeed: Uint8Array;\n    secpKey: Uint8Array;\n    addresses: Addresses;\n    credentialId: string;\n  } | null = null;\n\n  return {\n    isOpen() {\n      return state !== null;\n    },\n    snapshot() {\n      if (!state) return null;\n      return { addresses: state.addresses, credentialId: state.credentialId };\n    },\n    set({ keys, addresses, credentialId }) {\n      state = {\n        edSeed: new Uint8Array(keys.edSeed),\n        secpKey: new Uint8Array(keys.secpKey),\n        addresses,\n        credentialId,\n      };\n    },\n    clear() {\n      if (state) {\n        state.edSeed.fill(0);\n        state.secpKey.fill(0);\n      }\n      state = null;\n    },\n    async sign(chain, payload) {\n      if (!state) {\n        throw new Error('SessionVault: closed — no session keys available');\n      }\n      if (chain === 'cere' || chain === 'solana') {\n        return ed25519.sign(payload, state.edSeed);\n      }\n      if (chain === 'evm') {\n        // Caller (chain-specific signer) is responsible for framing + hashing.\n        // We sign the payload directly and return 65 bytes: r (32) || s (32) || v (1),\n        // where v is the recovery byte (0 or 1). Spec §3.6 invariant 1 holds —\n        // the secpKey never leaves this closure.\n        const sig = secp256k1.sign(payload, state.secpKey);\n        const compact = sig.toCompactRawBytes(); // 64 bytes (r || s)\n        const v = sig.recovery; // 0 or 1\n        const out = new Uint8Array(65);\n        out.set(compact, 0);\n        out[64] = v;\n        return out;\n      }\n      throw new Error(`SessionVault.sign: unknown chain \"${String(chain)}\"`);\n    },\n  };\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { secp256k1 } from '@noble/curves/secp256k1';\nimport type { DerivedKeys } from './derive';\n\nexport interface RegistrationProof {\n  /** ed25519 signature (64B) over the raw challenge — proves cere + solana. */\n  identitySig: Uint8Array;\n  /** secp256k1 uncompressed pubkey (65B, 0x04 || X || Y). */\n  evmPubkey: Uint8Array;\n  /** secp256k1 compact r||s signature (64B) over the raw challenge. */\n  evmSig: Uint8Array;\n}\n\n/**\n * Sign the single-use registration challenge with the PRF-derived keys to\n * prove possession of the chain identity. The server (wallet-api)\n * verifies these against the claimed addresses. Spec §3.3 (revised).\n *\n * - ed25519: sign the raw 32-byte challenge (EdDSA hashes internally).\n * - secp256k1: ECDSA over the 32-byte challenge treated as the message digest\n *   (matches the server's `ecdsa.Verify(challenge, pubkey)`). The challenge is\n *   32 random single-use bytes, so it is passed directly as the prehashed\n *   digest — NOT re-hashed (no `{ prehash: true }`). low-S is enforced by\n *   @noble's default, so the compact form is canonical.\n */\nexport function signRegistrationProof(keys: DerivedKeys, challenge: Uint8Array): RegistrationProof {\n  const identitySig = ed25519.sign(challenge, keys.edSeed);\n  const evmSig = secp256k1.sign(challenge, keys.secpKey).toCompactRawBytes();\n  return { identitySig, evmPubkey: keys.secpPubkeyUncompressed, evmSig };\n}\n","/**\n * Cross-tab session synchronisation helper.\n *\n * Wraps a `BroadcastChannel` on a fixed channel name so all tabs of the same\n * origin can coordinate. Today we only use it for `{ type: 'logout' }` — when\n * one tab logs out, every other tab clears its in-memory vault + JWT so its\n * MobX-reactive UI immediately reflects the logged-out state.\n *\n * The channel name must match `BROADCAST_CHANNEL_NAME` in\n * `packages/embed-sdk/src/protocol.ts` ('scp-wallet-v2'). Defined here as a\n * local constant rather than imported to avoid a circular dep between\n * `@cef-ai/wallet-identity` and `@cef-ai/wallet`. The name is part of the\n * wire protocol — if you change one, change the other (and bump the major).\n *\n * Spec §9.4 (cross-tab session sharing).\n */\n\n/** MUST match `packages/embed-sdk/src/protocol.ts` `BROADCAST_CHANNEL_NAME`. */\nconst CHANNEL_NAME = 'scp-wallet-v2';\n\nexport type CrossTabMessage = { type: 'logout' };\n\nexport class CrossTabSync {\n  private channel: BroadcastChannel | null;\n  private readonly listeners = new Set<(msg: CrossTabMessage) => void>();\n\n  constructor() {\n    // SSR / Node-without-BroadcastChannel safety. The class becomes a no-op\n    // (broadcast() returns silently, subscribe() returns a real unsub that\n    // just empties the local set, close() is a no-op).\n    if (typeof BroadcastChannel === 'undefined') {\n      this.channel = null;\n      return;\n    }\n    this.channel = new BroadcastChannel(CHANNEL_NAME);\n    this.channel.onmessage = (ev: MessageEvent) => {\n      const msg = ev.data as CrossTabMessage | undefined;\n      if (!msg || typeof msg !== 'object' || typeof (msg as { type?: unknown }).type !== 'string') {\n        return;\n      }\n      for (const fn of this.listeners) {\n        try {\n          fn(msg);\n        } catch {\n          // Listener errors must not break the channel for other listeners.\n        }\n      }\n    };\n  }\n\n  /** Broadcast a message to all other tabs (and not to this tab — that's the\n   * BroadcastChannel spec). The originating tab is expected to have already\n   * applied the state change locally before broadcasting. */\n  broadcast(msg: CrossTabMessage): void {\n    this.channel?.postMessage(msg);\n  }\n\n  /** Subscribe to messages from other tabs. Returns an unsubscribe function. */\n  subscribe(fn: (msg: CrossTabMessage) => void): () => void {\n    this.listeners.add(fn);\n    return () => {\n      this.listeners.delete(fn);\n    };\n  }\n\n  /**\n   * Close the underlying channel and drop all listeners. Idempotent.\n   *\n   * Sets `this.channel = null` so subsequent `broadcast()` calls become a\n   * no-op (the `?.postMessage` short-circuits) and we don't accidentally\n   * postMessage on a closed BroadcastChannel (which throws InvalidStateError\n   * in some implementations).\n   */\n  close(): void {\n    this.channel?.close();\n    this.channel = null;\n    this.listeners.clear();\n  }\n}\n","import { observable, runInAction } from 'mobx';\nimport bs58 from 'bs58';\nimport { WalletError, type ApiClient, type Addresses as ApiAddresses } from '@cef-ai/wallet-api-client';\nimport type { Identity, AccountInfo, Addresses, SessionResume, KeystoreEncryptor, WalletExport } from './types';\nimport type { CeremonyAdapter } from './ceremony';\nimport { createSessionVault, type SessionVault } from './vault';\nimport { derivedKeys, type DerivedKeys } from './derive';\nimport { signRegistrationProof } from './pop';\nimport { bytesToB64u } from './base64url';\nimport { deriveCereAddress, deriveSolanaAddress, deriveEvmAddress } from './addresses';\nimport { PRF_INPUT_SEED } from './constants';\nimport {\n  deriveWrapKeyForVersion,\n  RootUnwrapFailedError,\n  UnsupportedWrapSourceError,\n  type RootUnwrapper,\n} from './rootWrap';\nimport { CrossTabSync } from './CrossTabSync';\n\nexport interface IdentityImplOptions {\n  apiClient: ApiClient;\n  ceremony: CeremonyAdapter;\n  /**\n   * SPA-injected keystore encoder (see `KeystoreEncryptor`). Optional at the\n   * type level so lightweight test doubles need not supply one;\n   * `exportKeystore` throws if it is absent. NOTE: in the running wallet SPA\n   * this is always supplied — `createIdentity` defaults it to\n   * `spaKeystoreEncryptor` — and the single `IdentityImpl` instance is shared\n   * by both the `/settings` and `/embed/wallet` routes (one `WalletProvider`\n   * wraps both), so this option is not how the standalone/embed boundary is\n   * enforced. See `exportKeystore`'s doc comment for where that boundary\n   * actually lives.\n   */\n  keystoreEncryptor?: KeystoreEncryptor;\n  /**\n   * Opens a non-origin credential's root envelope (see `login()`). Optional at\n   * the type level so lightweight test doubles need not supply one — but a\n   * non-origin login without it fails rather than falling back to the\n   * credential's own PRF output, because that fallback IS the second-wallet\n   * bug. `createIdentity` defaults it to `createSecretboxWrapper()`, mirroring\n   * `keystoreEncryptor`.\n   */\n  rootUnwrapper?: RootUnwrapper;\n}\n\n/** Minimum slack before expiry where we treat the JWT as \"needs refresh\". */\nconst JWT_REFRESH_SLACK_MS = 60_000;\n\nfunction decodeJwtExpMs(token: string): number {\n  const parts = token.split('.');\n  if (parts.length < 2) {\n    throw new WalletError('validation', 'JWT does not have 3 parts');\n  }\n  const payloadB64 = parts[1];\n  const payload = JSON.parse(Buffer.from(payloadB64.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8'));\n  if (typeof payload.exp !== 'number') {\n    throw new WalletError('validation', 'JWT missing exp');\n  }\n  return payload.exp * 1000;\n}\n\nfunction b64uDecode(s: string): Uint8Array {\n  return Uint8Array.from(Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64'));\n}\n\n/**\n * True only for the two shapes that mean \"the server holds no envelope for\n * this credential\": a 404, or a problem+json response that already carries the\n * `root-envelope-missing` URN (which `ApiClient.request()` has turned into\n * that `code`). Everything else — a 500, a 401, a network error — is a\n * transient or authorization fault whose diagnosis is NOT the credential.\n */\nfunction isEnvelopeAbsence(e: unknown): boolean {\n  return e instanceof WalletError && (e.code === 'root-envelope-missing' || e.status === 404);\n}\n\n/** Narrow a session response down to the account fields the identity exposes. */\nfunction accountFromSession(session: {\n  accountId: string;\n  email: string | null;\n  displayName: string | null;\n}): AccountInfo {\n  return { accountId: session.accountId, email: session.email, displayName: session.displayName };\n}\n\nexport class IdentityImpl implements Identity, SessionResume, WalletExport {\n  private readonly vault: SessionVault = createSessionVault();\n  private cachedJwt: { token: string; expMs: number } | null = null;\n  /** Server-returned credential ID (used as the public identity.credentialId). */\n  private serverCredentialId: string | null = null;\n  /**\n   * Raw derived keys for the CURRENT session, captured at every `vault.set()`\n   * call site (register / login / adoptSession). The vault deliberately hides\n   * the seeds behind its closure (Spec §3.6 invariant 1); this field is the\n   * single, explicit, same-origin-only seam that lets `exportSession()` hand a\n   * live session to a sibling wallet-origin surface (the persistent iframe).\n   * Cleared on logout / cross-tab logout alongside the vault. Never serialised\n   * to storage and never sent to a host page.\n   */\n  private sessionKeys: DerivedKeys | null = null;\n  /**\n   * Absolute expiry (epoch ms) for `exportSession()`. Tracks the cached JWT\n   * expiry after register/login; set from the adopted session in adoptSession.\n   */\n  private sessionExpMs: number | null = null;\n  /**\n   * Addresses for a bearer-active session (set by `resumeSession`, which has\n   * no vault entry to read addresses from). Register/login/adoptSession keep\n   * this nil and rely on the vault snapshot instead; `syncPublicState()`\n   * falls back to this field only when the vault is closed.\n   */\n  private resumedAddresses: Addresses | null = null;\n  /**\n   * The S0 account behind the current session, as the server reported it on the\n   * last session response (register-finish / login-finish / refresh). Non-\n   * observable like `serverCredentialId`; pushed into `_account` by\n   * `syncPublicState()`. Cleared on logout / cross-tab logout.\n   */\n  private currentAccount: AccountInfo | null = null;\n  /**\n   * De-dupes concurrent bearer-session refreshes. Refresh-token rotation +\n   * reuse-detection means two parallel `session.refresh()` calls would replay a\n   * rotated token and revoke the whole family, so `getJwt()`'s bearer fallback\n   * shares a single in-flight refresh. Cleared once it settles.\n   */\n  private bearerRefreshInFlight: Promise<string> | null = null;\n\n  // Public observable state — derived from vault snapshot + serverCredentialId.\n  // Kept in sync via syncPublicState() so React `observer()` wrappers around\n  // components reading isAuthenticated/addresses/credentialId actually re-render\n  // on register/login/logout. The vault itself stays non-observable (security:\n  // the closure-encapsulated keys must not be tracked by MobX).\n  private readonly _isAuthenticated = observable.box(false);\n  // `deep: false` keeps the stored value a plain object instead of wrapping it\n  // in a MobX proxy. addresses is a wholesale-replaced snapshot (never mutated\n  // field-by-field), so deep observability buys nothing — and a proxy is not\n  // structured-cloneable, which breaks `postMessage` when the embed popup\n  // bridge sends addresses to the host page (wallet:login:ok).\n  private readonly _addresses = observable.box<Addresses | null>(null, { deep: false });\n  private readonly _credentialId = observable.box<string | null>(null);\n  // `deep: false` for the same reason as `_addresses`: this snapshot is\n  // replaced wholesale and is read by the session-status handler, whose reply\n  // is structured-cloned across `postMessage` to the host page. A MobX proxy is\n  // not cloneable.\n  private readonly _account = observable.box<AccountInfo | null>(null, { deep: false });\n\n  /** Cross-tab logout coordination via BroadcastChannel('scp-wallet-v2'). */\n  private readonly crossTabSync = new CrossTabSync();\n  /** Handle to detach the cross-tab logout subscriber on dispose(). */\n  private readonly crossTabSyncUnsubscribe: () => void;\n  /** Idempotency flag for dispose(). */\n  private disposed = false;\n\n  constructor(private readonly opts: IdentityImplOptions) {\n    // Hydrate the boxes from any existing vault snapshot. Currently the vault\n    // starts closed, so this is a no-op — but it's the right call site for a\n    // future BroadcastChannel-restored session.\n    this.syncPublicState();\n    // Cross-tab logout: when another tab broadcasts {type:'logout'}, mirror it\n    // locally so this tab's MobX-bound UI re-renders without a page reload.\n    // The guard prevents the originating tab from double-clearing — by the\n    // time its own listener fires (BroadcastChannel doesn't deliver to the\n    // sender, but defence-in-depth), local state is already cleared. The\n    // guard also makes the listener a no-op for tabs that were already\n    // signed out. Checks both the vault AND cachedJwt (not just\n    // `vault.isOpen()`) so a bearer-active tab (Task 2: `resumeSession` —\n    // cachedJwt set, vault closed, no keys) is also cleared by a peer's\n    // logout broadcast; otherwise such a tab would stay authenticated.\n    //\n    // Capture the unsubscribe handle so dispose() can detach this subscriber\n    // before closing the channel — without this, a long-lived test suite or\n    // a hot-reloaded SPA leaks listener closures into the next IdentityImpl.\n    this.crossTabSyncUnsubscribe = this.crossTabSync.subscribe((msg) => {\n      if (msg.type === 'logout' && (this.vault.isOpen() || this.cachedJwt != null)) {\n        this.vault.clear();\n        this.cachedJwt = null;\n        this.serverCredentialId = null;\n        this.sessionKeys = null;\n        this.sessionExpMs = null;\n        this.resumedAddresses = null;\n        this.currentAccount = null;\n        this.syncPublicState();\n      }\n    });\n  }\n\n  get isAuthenticated(): boolean {\n    return this._isAuthenticated.get();\n  }\n\n  get addresses(): Addresses | null {\n    return this._addresses.get();\n  }\n\n  /**\n   * Returns the server-returned credentialId from the last register/login ceremony.\n   * The vault internally tracks the authenticator-generated credential ID for login use.\n   */\n  get credentialId(): string | null {\n    return this._credentialId.get();\n  }\n\n  /**\n   * The account behind the current session, or `null` when signed out. Comes\n   * straight off the server's session response — reading it never runs a\n   * ceremony, which is what lets `wallet:session:status` report it.\n   */\n  get account(): AccountInfo | null {\n    return this._account.get();\n  }\n\n  /**\n   * Discriminates the two authenticated states: `true` when the session has\n   * derived signing keys (register/login/adoptSession); `false` for a\n   * bearer-active session adopted via `resumeSession` (JWT only). Token-\n   * authorized ops (e.g. delegation mint) only require `isAuthenticated`;\n   * anything that needs a raw signature must check `hasKeys()` first and\n   * step up (re-run login) when it's false.\n   */\n  hasKeys(): boolean {\n    return this.sessionKeys != null;\n  }\n\n  /**\n   * Read the (non-observable) vault snapshot + cached server credential ID and\n   * push the values into the three public observable boxes. Call after any\n   * mutation that could change the public derived state: register, login,\n   * logout, or constructor (hydration).\n   */\n  private syncPublicState(): void {\n    const snap = this.vault.snapshot();\n    // isAuthenticated is bearer-driven: a present, unexpired cachedJwt is\n    // sufficient on its own (Task 2 — \"bearer-active\" state, no keys). Keys\n    // are optional; use hasKeys() to discriminate whether signing is\n    // possible without step-up.\n    //\n    // `adoptSession()` (register-popup → iframe handoff, pre-dates Task 2) is\n    // the one path that installs keys WITHOUT a JWT — there is no ceremony,\n    // so no token is minted; the session is authorized purely by the\n    // transported key + its own expiry (`sessionExpMs`). It must keep\n    // satisfying isAuthenticated, so this is an OR, not a replacement:\n    // authenticated if the JWT is live, or if we hold keys that haven't\n    // expired. Login/register set cachedJwt+keys+sessionExpMs together, so\n    // both existing call sites keep tripping this true exactly as before.\n    const jwtLive = this.cachedJwt != null && Date.now() < this.cachedJwt.expMs;\n    const keysLive = this.sessionKeys != null && this.sessionExpMs != null && Date.now() < this.sessionExpMs;\n    const isAuthenticated = jwtLive || keysLive;\n    // Addresses can come from either the vault snapshot (keys present) or a\n    // bearer-active resumed session (no vault entry, addresses cached\n    // separately in `resumedAddresses`).\n    runInAction(() => {\n      this._isAuthenticated.set(isAuthenticated);\n      this._addresses.set(snap?.addresses ?? this.resumedAddresses);\n      this._credentialId.set(this.serverCredentialId);\n      this._account.set(this.currentAccount);\n    });\n  }\n\n  /**\n   * Create a NEW account and enrol this device's passkey as its ORIGIN\n   * credential: the PRF output of a credential registered here IS the account\n   * root (`derive.ts` uses it verbatim as the ed25519 seed), which is exactly\n   * what `is_origin` records server-side. Registration therefore writes NO root\n   * envelope — there is no other root to wrap. Adding a SECOND device is\n   * recover-then-enrol (a later sub-project), never a second `register()`.\n   */\n  async register(opts: { emailToken: string; displayName?: string; email?: string; label?: string }): Promise<void> {\n    // 0. S0: the mailbox is proven BEFORE the ceremony. `emailToken` is\n    //    required by the type, so this guard is defence in depth — it still\n    //    catches an empty string, and any JS caller the compiler never saw.\n    //    Rejecting here (rather than sending a request the server must reject\n    //    anyway) keeps the failure named, and keeps it BEFORE the ceremony, so\n    //    there is no half-registration and no authenticator prompt to abandon.\n    if (!opts.emailToken) {\n      throw new WalletError(\n        'email-unverified',\n        'register() requires an emailToken from accounts.emailVerify(); verify the email before the ceremony',\n      );\n    }\n\n    // 1. Start ceremony — get the challenge from the API. No addresses go up\n    //    here: they are only knowable after the ceremony produces a PRF output,\n    //    and /finish is where the server binds them (via proof-of-possession).\n    const start = await this.opts.apiClient.passkey.registerStart({\n      emailToken: opts.emailToken,\n      displayName: opts.displayName,\n      label: opts.label,\n    });\n\n    // 2. Run the ceremony.\n    const cer = await this.opts.ceremony.register({\n      rpId: start.rpId,\n      challenge: b64uDecode(start.challenge),\n      userHandle: b64uDecode(start.userHandle),\n      prfInput: PRF_INPUT_SEED,\n      label: opts.label,\n      // The WebAuthn user entity is what the OS passkey manager shows: `name`\n      // is the account line, `displayName` the friendly one. Both are hints to\n      // the authenticator — they are absent from `clientDataJSON` and from the\n      // attestation, so neither reaches the server and the address here is not\n      // a second, unverified copy of the one `emailToken` attests. Passing it\n      // is the whole difference between an entry that reads\n      // `alex@example.com` and one that reads `scp-wallet` (CEF-AI/wallet#72).\n      name: opts.displayName,\n      // `|| undefined`, not `??`: an empty string is not nullish, so it would\n      // reach the adapter and win its `opts.email ?? opts.label ?? 'scp-wallet'`\n      // fallback chain — producing a passkey entry with a BLANK account line,\n      // which is worse than the `scp-wallet` this fix exists to remove. Neither\n      // in-repo caller can produce one; this is the same defence-in-depth the\n      // empty-`emailToken` guard above is, for JS callers the compiler never saw.\n      email: opts.email || undefined,\n    });\n    if (!cer.prfOutput) {\n      throw new WalletError('prf-unsupported', 'authenticator did not return a PRF output');\n    }\n\n    // 3. Derive keys + addresses locally.\n    const keys = derivedKeys(cer.prfOutput);\n    const clientAddresses: Addresses = {\n      cere: deriveCereAddress(keys.edPubkey),\n      solana: deriveSolanaAddress(keys.edPubkey),\n      evm: deriveEvmAddress(keys.secpPubkeyUncompressed),\n    };\n\n    // 4. Prove possession of the PRF-derived chain keys by signing the\n    //    single-use challenge. The server binds addresses.{cere,solana,evm}\n    //    to these signatures (proof-of-possession), not to the WebAuthn\n    //    credential key. Spec §3.3 (revised).\n    const proof = signRegistrationProof(keys, b64uDecode(start.challenge));\n\n    // 5. Finish ceremony with the API.\n    const finish = await this.opts.apiClient.passkey.registerFinish({\n      challengeId: start.challengeId,\n      clientDataJSON: cer.clientDataJSON,\n      attestationObject: cer.attestationObject,\n      addresses: clientAddresses,\n      transports: cer.transports,\n      identitySig: bytesToB64u(proof.identitySig),\n      evmPubkey: bytesToB64u(proof.evmPubkey),\n      evmSig: bytesToB64u(proof.evmSig),\n    });\n\n    // 6. Cross-check returned addresses against locally-derived. Spec §3.3.\n    //    This doubles as the guard that a registration really did produce an\n    //    origin credential: the keys above come from `cer.prfOutput` directly,\n    //    so if the server had attached this credential to an EXISTING account\n    //    (whose root is some other credential's PRF output) it would answer\n    //    with that account's addresses and this comparison would fail loudly —\n    //    rather than us installing a second, silently-wrong wallet.\n    this.crossCheckAddresses(clientAddresses, finish.addresses);\n\n    // 7. Install the vault + cache the JWT.\n    //    The vault stores the authenticator-generated credential ID (cer.credentialId)\n    //    so subsequent login() calls can pass it back to the same authenticator.\n    //    The server-returned credential ID is tracked separately for the public getter.\n    this.vault.set({\n      keys,\n      addresses: clientAddresses,\n      credentialId: cer.credentialId,\n    });\n    this.serverCredentialId = finish.credentialId;\n    this.currentAccount = accountFromSession(finish);\n    this.cachedJwt = { token: finish.token, expMs: decodeJwtExpMs(finish.token) };\n    // Capture the session for a possible same-origin handoff (register popup →\n    // iframe). See `sessionKeys` field + `exportSession()`.\n    this.sessionKeys = keys;\n    this.sessionExpMs = this.cachedJwt.expMs;\n    this.syncPublicState();\n  }\n\n  async login(opts: { credentialId?: string } = {}): Promise<void> {\n    const start = await this.opts.apiClient.passkey.loginStart();\n\n    const cer = await this.opts.ceremony.login({\n      rpId: start.rpId,\n      challenge: b64uDecode(start.challenge),\n      // An explicit pin (step-up of a known session) takes precedence over\n      // the vault snapshot; the vault is closed during a step-up anyway\n      // (there is nothing to fall back to), but an explicit caller-supplied\n      // pin should win even if the vault happened to hold something.\n      credentialId: opts.credentialId ?? this.vault.snapshot()?.credentialId,\n      prfInput: PRF_INPUT_SEED,\n    });\n    if (!cer.prfOutput) {\n      throw new WalletError('prf-unsupported', 'authenticator did not return a PRF output');\n    }\n\n    const finish = await this.opts.apiClient.passkey.loginFinish({\n      challengeId: start.challengeId,\n      credentialId: cer.credentialId,\n      clientDataJSON: cer.clientDataJSON,\n      authenticatorData: cer.authenticatorData,\n      signature: cer.signature,\n    });\n\n    // The JWT is installed BEFORE the root is resolved, because resolving a\n    // non-origin root calls `accounts.getEnvelope` — an authRequired endpoint\n    // whose token comes back through `createIdentity`'s\n    // `getAuthToken: () => identity.getJwt()`. With no cached token that\n    // re-entry sees a closed vault and throws `unauthorized`, so the envelope\n    // fetch would fail as an auth error instead of fetching. Kept in a local so\n    // any failure below can put the previous token back: a login that cannot\n    // resolve its root must not leave behind a half-authenticated session whose\n    // `getJwt()` succeeds for an identity holding no keys.\n    const previousJwt = this.cachedJwt;\n    this.cachedJwt = { token: finish.token, expMs: decodeJwtExpMs(finish.token) };\n\n    let keys: DerivedKeys;\n    let clientAddresses: Addresses;\n    try {\n      // `isOrigin` means THIS credential's PRF output IS the account root\n      // (`derive.ts` uses it verbatim as the ed25519 seed). Every other\n      // credential's PRF output is a WRAPPING key only: deriving from it\n      // directly would yield a different, valid-looking address — a second\n      // wallet created silently, which no database fix can repoint because\n      // bucket ownership is recorded on-chain. This branch is the whole reason\n      // the S0 account model exists.\n      // The envelope is looked up by the AUTHENTICATOR-side `cer.credentialId`,\n      // while this class tracks the server-assigned id separately. Known\n      // asymmetry, not an oversight: see wallet#58. It fails CLOSED (a lookup\n      // by the wrong id finds no envelope and login fails loudly rather than\n      // deriving a wrong root), and choosing which id is canonical is a\n      // device-add-flow decision, not an S0 one.\n      const root = finish.isOrigin\n        ? cer.prfOutput\n        : await this.unwrapRootForCredential(cer.prfOutput, cer.credentialId);\n\n      keys = derivedKeys(root);\n      clientAddresses = {\n        cere: deriveCereAddress(keys.edPubkey),\n        solana: deriveSolanaAddress(keys.edPubkey),\n        evm: deriveEvmAddress(keys.secpPubkeyUncompressed),\n      };\n\n      // Deliberately AFTER the root resolution, not before it: derivation now\n      // runs on the root we actually resolved, so on the non-origin branch this\n      // compares addresses from the UNWRAPPED root against the account the\n      // server says we are. That comparison is what makes a wrong root loud.\n      // Run before the resolution (as it was), it would instead reject every\n      // added device — the client addresses would be the wrong ones.\n      this.crossCheckAddresses(clientAddresses, finish.addresses);\n    } catch (e) {\n      this.cachedJwt = previousJwt;\n      throw e;\n    }\n\n    this.vault.set({\n      keys,\n      addresses: clientAddresses,\n      credentialId: cer.credentialId,\n    });\n    this.serverCredentialId = finish.credentialId;\n    this.currentAccount = accountFromSession(finish);\n    this.sessionKeys = keys;\n    this.sessionExpMs = this.cachedJwt.expMs;\n    this.syncPublicState();\n  }\n\n  /**\n   * Resolve the account root for a NON-origin credential by unwrapping that\n   * credential's root envelope. `prfOutput` here is a wrapping key, never a\n   * seed.\n   *\n   * The ways this fails stay distinct, diagnosable conditions — that is what\n   * `wrapSource` and `wrapVersion` on the wire are for, and collapsing them\n   * would leave a caller unable to tell \"this device was never enrolled\" from\n   * \"upgrade this client\" from \"use your recovery password\" from \"this envelope\n   * is damaged\". Every one of them is a `WalletError` subclass, so a caller can\n   * switch on `.code` and the SPA can render real copy:\n   *\n   *   1. no envelope for the credential — a 404, or the `root-envelope-missing`\n   *      URN — → `WalletError('root-envelope-missing')`, with the underlying\n   *      failure kept on `cause`. Any OTHER failure of that fetch (a 500, a\n   *      401, a network error) is not this condition and propagates as\n   *      whatever `ApiClient` already made of it;\n   *   2. an envelope that is not credential-PRF-wrapped →\n   *      `UnsupportedWrapSourceError` (`root-wrap-source-unsupported`);\n   *   3. a `wrapVersion` outside this build's compiled-in set →\n   *      `UnrecognisedWrapVersionError` (`root-wrap-version-unknown`),\n   *      propagated untouched from `deriveWrapKeyForVersion` (which is also the\n   *      only place a server-supplied version string is turned into\n   *      key-derivation input);\n   *   4. ciphertext that will not open under the derived key →\n   *      `RootUnwrapFailedError` (`root-unwrap-failed`), carrying the\n   *      primitive's error as `cause`.\n   *\n   * Each `catch` wraps exactly one call for exactly that reason; there is no\n   * blanket try around the whole method.\n   */\n  private async unwrapRootForCredential(prfOutput: Uint8Array, credentialId: string): Promise<Uint8Array> {\n    const unwrapper = this.opts.rootUnwrapper;\n    if (!unwrapper) {\n      // Checked before the fetch: nothing here can succeed without it, and\n      // failing first spares an authenticated round trip. Failing at all --\n      // rather than falling back to `prfOutput` as a seed -- is the point: that\n      // fallback IS the silent-second-wallet bug.\n      throw new WalletError('validation', 'login: no rootUnwrapper configured; cannot open a non-origin root envelope');\n    }\n    let envelope;\n    try {\n      envelope = await this.opts.apiClient.accounts.getEnvelope(credentialId);\n    } catch (e) {\n      if (!isEnvelopeAbsence(e)) {\n        // A 500, a 401, a network error: the wallet service failed, and the\n        // credential is not implicated. `ApiClient.request()` has already\n        // translated the response into a `WalletError` with the right code\n        // (`internal`, `unauthorized`, ...), so the correct move is to let it\n        // through untouched. Rewriting it to `root-envelope-missing` told the\n        // reader their passkey \"isn't set up to open your wallet\" — the wrong\n        // diagnosis AND the wrong remedy for a fault that clears on retry, and\n        // one that teaches distrust of a passkey that works perfectly.\n        throw e;\n      }\n      // A genuine absence, and the only case this code belongs on. Re-raised\n      // rather than re-thrown so the message names the credential, with the\n      // original error on `cause` so nothing is lost (the pattern\n      // `RootUnwrapFailedError` uses below).\n      const missing = new WalletError(\n        'root-envelope-missing',\n        `no root envelope for non-origin credential ${credentialId}`,\n      );\n      (missing as Error).cause = e;\n      throw missing;\n    }\n    // A `password` envelope belongs to the recovery path and cannot be opened\n    // with a credential key. Rejecting it here rather than letting the unwrap\n    // fail keeps a FOURTH condition distinct: attacking it with the PRF-derived\n    // key is fail-safe (it will not open, so no wrong root is derived), but it\n    // would be reported as a damaged envelope when the real answer is \"this is\n    // the wrong kind of envelope\".\n    if (envelope.wrapSource !== 'credential-prf') {\n      throw new UnsupportedWrapSourceError(envelope.wrapSource, credentialId);\n    }\n    // Resolved through the closed version → label map, never from the wire\n    // string itself. Throws `UnrecognisedWrapVersionError`, which is left to\n    // propagate as itself.\n    const wrapKey = deriveWrapKeyForVersion(prfOutput, envelope.wrapVersion);\n    try {\n      return unwrapper.unwrap(b64uDecode(envelope.ciphertext), b64uDecode(envelope.nonce), wrapKey);\n    } catch (e) {\n      throw new RootUnwrapFailedError(credentialId, { cause: e });\n    } finally {\n      // Wipe the wrap key on every exit, success or failure. It is a fresh\n      // HKDF output owned by this call and aliased by nothing: `unwrap`\n      // returns a COPY of the plaintext (`Uint8Array.from` in\n      // `createSecretboxWrapper`), so clearing the key cannot reach the value\n      // being returned. `keyBytes.fill(0)` in `exportKeystore` bounds a\n      // plaintext key copy the same way.\n      //\n      // The boundary, stated so the remaining asymmetry is deliberate rather\n      // than missed: the ROOT is NOT wiped here. It is this method's return\n      // value — the caller derives the session keys from it, and those live in\n      // the vault, which owns their zeroization (`vault.ts` wipes `edSeed` /\n      // `secpKey` on close). `prfOutput` is likewise the caller's, and this\n      // method is not the end of its life. Only the wrap key dies here.\n      wrapKey.fill(0);\n    }\n  }\n\n  /**\n   * Adopt a session from a token-refresh response (`apiClient.session.refresh()`)\n   * WITHOUT running a passkey ceremony and WITHOUT deriving keys — the\n   * \"bearer-active\" state (Task 2). Sets `cachedJwt` + its expiry + addresses;\n   * leaves `sessionKeys` (and the vault) untouched, so `hasKeys()` stays\n   * false unless a prior login already populated them. Callers needing a raw\n   * signature must step up via `login()` first; token-authorized ops (e.g.\n   * delegation mint) only need `isAuthenticated`, which this satisfies.\n   */\n  resumeSession(session: {\n    token: string;\n    addresses: Addresses;\n    credentialId: string;\n    accountId?: string;\n    email?: string | null;\n    displayName?: string | null;\n  }): void {\n    this.cachedJwt = { token: session.token, expMs: decodeJwtExpMs(session.token) };\n    this.serverCredentialId = session.credentialId;\n    this.resumedAddresses = session.addresses;\n    // A bearer-resumed tab knows its account without a ceremony — the refresh\n    // response carries the same account fields a login-finish does, so\n    // `identity.account` is populated on a cold reload too. Only overwritten\n    // when the caller actually supplied an account: an older call site that\n    // passes just token/addresses/credentialId should not blank a known one.\n    if (session.accountId != null) {\n      this.currentAccount = {\n        accountId: session.accountId,\n        email: session.email ?? null,\n        displayName: session.displayName ?? null,\n      };\n    }\n    this.syncPublicState();\n  }\n\n  /**\n   * Replace the account fields for the CURRENT session, without a ceremony and\n   * without touching keys, the vault, or the JWT.\n   *\n   * The one caller is `PATCH /v1/accounts/me`, which returns the full updated\n   * account. Everything else that sets `currentAccount` does so from a session\n   * response (register-finish / login-finish / refresh), and a patch produces\n   * none — so without this, an account that has just bound an email keeps\n   * reporting `email: null` from `identity.account` until the next session\n   * response, and every reader of that field (the Settings nudge, the\n   * `wallet:session:status` payload) is working from a value the client itself\n   * knows to be stale.\n   *\n   * Deliberately narrow: it writes `currentAccount` and re-syncs public state,\n   * nothing else. It is NOT a login — a call with no live session is ignored\n   * rather than resurrecting an account onto a signed-out identity, which is\n   * what a logout racing an in-flight patch would otherwise do.\n   */\n  setAccount(account: AccountInfo): void {\n    if (!this.cachedJwt) return;\n    this.currentAccount = { ...account };\n    this.syncPublicState();\n  }\n\n  logout(): void {\n    this.vault.clear();\n    this.cachedJwt = null;\n    this.serverCredentialId = null;\n    this.sessionKeys = null;\n    this.sessionExpMs = null;\n    this.resumedAddresses = null;\n    this.currentAccount = null;\n    this.syncPublicState();\n    // Broadcast AFTER clearing local state so other tabs converge on the same\n    // post-logout snapshot. BroadcastChannel does not deliver to the sender,\n    // so this tab won't re-enter the subscriber callback above.\n    this.crossTabSync.broadcast({ type: 'logout' });\n  }\n\n  async getJwt(): Promise<string> {\n    if (this.cachedJwt && Date.now() < this.cachedJwt.expMs - JWT_REFRESH_SLACK_MS) {\n      return this.cachedJwt.token;\n    }\n    // The wallet-origin refresh cookie can re-mint the JWT with no ceremony,\n    // and `refreshBearerSession` lands in `resumeSession()`, which touches only\n    // cachedJwt / serverCredentialId / resumedAddresses — never the vault and\n    // never `sessionKeys`. So this path is safe for a WARM session (vault open)\n    // too, not just a bearer one, and trying it FIRST is what stops an idle\n    // keyed tab from prompting at the ~20m JWT TTL.\n    //\n    // It used to be the other way round: a bearer session refreshed silently\n    // while a warm one was sent to `login()` — a real WebAuthn ceremony — which\n    // made the tab HOLDING the keys the one that interrupts the user. That is\n    // the reported idle-tab symptom.\n    if (this.cachedJwt) {\n      try {\n        return await this.refreshBearerSession();\n      } catch {\n        // Cookie missing/expired/revoked — or the API is genuinely down, since\n        // `session.refresh()` already retried a 5xx once and gave up\n        // (CEF-AI/wallet#60). A warm session can still re-mint via a ceremony;\n        // a keyless one cannot, and falls through to unauthorized. During a\n        // real outage the ceremony dies at `passkey/login/start`, before any\n        // prompt is shown — the passkey prompt this path is accused of is\n        // reachable only when the fault clears between the two calls, which is\n        // the window the retry closes.\n      }\n    }\n    // Warm session (vault open): re-run login to mint a fresh JWT (+ keys).\n    // Now the FALLBACK for a dead cookie rather than the first move.\n    if (this.vault.isOpen()) {\n      await this.login();\n      return this.cachedJwt!.token;\n    }\n    throw new WalletError('unauthorized', 'session is closed; call register() or login() first');\n  }\n\n  /**\n   * Silently re-mint the bearer session's JWT from the wallet-origin refresh\n   * cookie, de-duped across concurrent callers. Refresh-token rotation means two\n   * parallel refreshes would replay a rotated token → reuse-detection revokes\n   * the family; sharing one in-flight promise avoids that. Resolves to the fresh\n   * JWT; rejects (propagating the `session.refresh()` error) if the cookie is\n   * missing/expired/revoked.\n   */\n  private refreshBearerSession(): Promise<string> {\n    if (!this.bearerRefreshInFlight) {\n      this.bearerRefreshInFlight = this.opts.apiClient.session\n        .refresh()\n        .then((resp) => {\n          // `SessionResponse.addresses` is the wire shape (solana/evm optional);\n          // `resumeSession` needs a complete tri-chain `Addresses`. Every real\n          // session has all three (register derives cere+solana+evm), so a\n          // partial response is malformed — fail closed (→ `unauthorized`)\n          // rather than adopting a half-populated identity. Mirrors the SPA\n          // boot-resume completeness guard.\n          const { cere, solana, evm } = resp.addresses;\n          if (!cere || !solana || !evm) {\n            throw new WalletError('unauthorized', 'session refresh returned incomplete addresses');\n          }\n          this.resumeSession({\n            token: resp.token,\n            addresses: { cere, solana, evm },\n            credentialId: resp.credentialId,\n            accountId: resp.accountId,\n            email: resp.email,\n            displayName: resp.displayName,\n          });\n          // `resumeSession` deliberately leaves the vault and `sessionKeys`\n          // alone, which is what makes it safe for a WARM caller — but\n          // `sessionExpMs` still has to track the new JWT when we hold keys.\n          // `login()` advanced it as a side effect of the ceremony; now that a\n          // warm session refreshes silently instead, nothing else would, and\n          // `exportSession()` gates on it. Leaving it stale would let a warm\n          // tab stay authenticated while quietly losing the ability to hand its\n          // session to the persistent iframe — a failure with no error. Nil for\n          // a bearer session, which holds no keys to export.\n          if (this.sessionKeys != null && this.cachedJwt != null) {\n            this.sessionExpMs = this.cachedJwt.expMs;\n            this.syncPublicState();\n          }\n          return resp.token;\n        })\n        .finally(() => {\n          this.bearerRefreshInFlight = null;\n        });\n    }\n    return this.bearerRefreshInFlight;\n  }\n\n  /**\n   * Read the cached JWT verbatim — no expiry check, no refresh. A cache\n   * INSPECTOR, for tests and diagnostics.\n   *\n   * NOT the token source for `ApiClient.getAuthToken`. It was, under the name\n   * `getCachedJwtForApiClient`, and that is what caused the outage this rename\n   * exists to prevent: the JWT TTL (~20m) is far shorter than a tab's life, so\n   * every authRequired call from an idle tab shipped a long-dead token and\n   * wallet-api answered `token-expired` — permanently, because nothing on that\n   * path refreshed. Anything that needs a token to SEND must call `getJwt()`,\n   * which refreshes within `JWT_REFRESH_SLACK_MS` (silently, off the refresh\n   * cookie, for a bearer-active session). See `src/bootstrap/createIdentity.ts`\n   * and its `createIdentity.authToken.test.ts`.\n   */\n  peekCachedJwt(): string | null {\n    return this.cachedJwt?.token ?? null;\n  }\n\n  /**\n   * Returns the session vault. Used by popup-side `wallet:sign` handlers\n   * which need direct access to `vault.sign(chain, payload)`. NOT for host-\n   * side use — keys never reach the host. Spec §3.6 invariant 1.\n   */\n  getVault(): SessionVault {\n    return this.vault;\n  }\n\n  /*\n   * S1 PR2 deleted `exportSession()`. It returned raw `edSeed`/`secpKey` bytes\n   * to any same-origin caller so a sibling wallet surface could adopt the\n   * session. Nothing needs it now: the frame re-derives its own key from PRF\n   * after a registration popup, so no session is ever handed over.\n   *\n   * `exportKeystore` below is NOT the same thing and deliberately survives —\n   * it returns an ENCRYPTED keystore the caller must supply a passphrase to\n   * open, which is the user exporting their own key on purpose.\n   */\n\n  /**\n   * Export the current account's key for `chain` as a password-encrypted\n   * keystore JSON string. The raw seed is read from the `sessionKeys` seam,\n   * handed to the injected `KeystoreEncryptor`, and only the returned ciphertext\n   * escapes — Spec §3.6 containment holds.\n   *\n   * This method has no notion of \"standalone\" vs \"embed\" surface: one\n   * `WalletProvider` wraps both the `/settings` and `/embed/wallet` routes,\n   * `createIdentity` always defaults the `keystoreEncryptor`, and the same\n   * `IdentityImpl` instance backs both — so this call is exactly as reachable\n   * from the embed popup as from Settings. The standalone-only boundary is\n   * enforced above this layer: `ExportAccount` is only mounted by the\n   * `/settings` route, and it additionally asserts `!isEmbedSurface()` at\n   * runtime (`src/wallet/export/isEmbedSurface.ts`) as a belt-and-suspenders\n   * check. Do not rely on this method to gate the surface.\n   */\n  async exportKeystore(chain: 'cere' | 'evm', passphrase: string): Promise<string> {\n    if (!this.vault.isOpen() || !this.sessionKeys) {\n      throw new WalletError('unauthorized', 'exportKeystore: no open session');\n    }\n    if (!passphrase) {\n      throw new WalletError('validation', 'exportKeystore: passphrase is required');\n    }\n    const encryptor = this.opts.keystoreEncryptor;\n    if (!encryptor) {\n      throw new WalletError('validation', 'exportKeystore: no keystore encryptor configured');\n    }\n    // Read only for the address below — `VaultSnapshot` is `{addresses, credentialId}`\n    // and never carries key bytes (see packages/identity/src/vault.ts). The key\n    // material comes exclusively from `this.sessionKeys`.\n    const snap = this.vault.snapshot();\n    if (!snap) {\n      throw new WalletError('unauthorized', 'exportKeystore: vault snapshot unavailable');\n    }\n    // Copy so the encryptor cannot retain a handle to our live key buffer.\n    const keyBytes =\n      chain === 'cere' ? new Uint8Array(this.sessionKeys.edSeed) : new Uint8Array(this.sessionKeys.secpKey);\n    const address = chain === 'cere' ? snap.addresses.cere : snap.addresses.evm;\n    try {\n      return await encryptor(chain, keyBytes, passphrase, address);\n    } finally {\n      // Zero the copy synchronously on the way out. `keyBytes` is the exact\n      // reference the encryptor received; per the `KeystoreEncryptor` contract\n      // it must be pure and retain nothing, so it is done with the buffer by\n      // the time this runs. Wiping here bounds the plaintext key copy's\n      // lifetime to this call rather than leaving it for GC. Callers that need\n      // to inspect the bytes handed to the encryptor must snapshot them inside\n      // the encryptor (the buffer is live for the whole encryptor invocation),\n      // not read the argument reference afterwards.\n      keyBytes.fill(0);\n    }\n  }\n\n  /**\n   * Reveal the Solana secret key as base58 (64 bytes: seed || pubkey). This is\n   * the ONE raw-key export path — Phantom cannot import an encrypted file. It is\n   * a deliberately separate, explicitly-named method so the raw path is never\n   * taken by accident. As with `exportKeystore`, this method itself does not\n   * know the standalone/embed surface; that boundary is enforced by the UI\n   * (see `exportKeystore`'s doc comment).\n   */\n  async revealSolanaSecret(): Promise<string> {\n    if (!this.vault.isOpen() || !this.sessionKeys) {\n      throw new WalletError('unauthorized', 'revealSolanaSecret: no open session');\n    }\n    const seed = this.sessionKeys.edSeed;\n    const pub = this.sessionKeys.edPubkey;\n    const secret = new Uint8Array(64);\n    secret.set(seed, 0);\n    secret.set(pub, 32);\n    // Copy is local to this call; the live session keys (`this.sessionKeys.edSeed`\n    // / `edPubkey`) are untouched. Wiped here for symmetry with `exportKeystore`'s\n    // `keyBytes.fill(0)` — bounds this plaintext copy's lifetime to the call.\n    const encoded = bs58.encode(secret);\n    secret.fill(0);\n    return encoded;\n  }\n\n  /*\n   * S1 PR2 deleted `adoptSession()`, the consumer half of the seam above. It\n   * rebuilt derived keys from transported seeds and opened the vault WITHOUT a\n   * WebAuthn ceremony — the one path by which a surface could hold keys it had\n   * not derived itself. See `no-key-export.test.ts`.\n   */\n\n  /**\n   * Release the BroadcastChannel + cross-tab subscriber. Idempotent.\n   *\n   * Called by:\n   *   - WalletProvider's useEffect cleanup (SPA unmount / apiBaseUrl change)\n   *   - test afterEach hooks (prevent listener leakage across tests)\n   *\n   * After dispose(), the identity is unusable: register/login/logout/getJwt\n   * still execute their normal logic, but cross-tab broadcasts no longer\n   * propagate (channel is closed) and the local subscriber is detached.\n   * The vault remains cleared (logout() runs locally). Spec §9.4 lifecycle.\n   */\n  dispose(): void {\n    if (this.disposed) return;\n    this.disposed = true;\n    // Detach subscriber first so any in-flight onmessage from this channel\n    // does not re-enter the now-closing identity.\n    this.crossTabSyncUnsubscribe();\n    this.crossTabSync.close();\n  }\n\n  // ---- private --------------------------------------------------------------\n\n  private crossCheckAddresses(client: Addresses, server: ApiAddresses | undefined): void {\n    if (!server) {\n      throw new WalletError('derivation-mismatch', 'API did not return addresses');\n    }\n    // `cere` is required by the wire type, so make the runtime agree. Guarded\n    // like the optional chains below, a response of `{cere: null}` would skip\n    // EVERY comparison and let the login succeed having checked nothing — and\n    // on the non-origin branch this comparison is the one thing standing\n    // between a wrong root and a silently created second wallet. A cross-check\n    // that can quietly no-op is not a cross-check.\n    if (!server.cere) {\n      throw new WalletError('derivation-mismatch', 'API returned no Cere address to cross-check against');\n    }\n    if (client.cere !== server.cere) {\n      throw new WalletError('derivation-mismatch', `Cere mismatch: client=${client.cere} api=${server.cere}`);\n    }\n    if (server.solana != null && client.solana !== server.solana) {\n      throw new WalletError('derivation-mismatch', `Solana mismatch: client=${client.solana} api=${server.solana}`);\n    }\n    if (server.evm != null && client.evm.toLowerCase() !== server.evm.toLowerCase()) {\n      throw new WalletError('derivation-mismatch', `EVM mismatch: client=${client.evm} api=${server.evm}`);\n    }\n  }\n}\n"]}