Adversarial verification of the audit fixes found real gaps in them:
- HIGH throw-safety: the switch worker and the encryption-restart worker set
daemon_restarting_=true but reset it only on their normal/early-return paths —
a throw from stop/startEmbeddedDaemon left the flag stuck true, wedging
reconnect and every future switch/rescan/encryption. Both now reset it on all
paths (try/catch); a throw during a switch is treated as a failed switch and
triggers the revert.
- HIGH residual chat leak: resetChatSession() cleared the flags but an already-
posted z_exportmnemonic worker job still held wallet A's secret, and its
completion callback (guarded only by isLocked(), false for an unencrypted
wallet) would provision A's identity under B. Add a chat_session_generation_
epoch bumped on every wallet change; the fetch captures it and its callback
discards the (previous-wallet) secret if the epoch no longer matches.
- LOW vault-scope collision: the per-wallet vault tag was a lossy char-substitution
(two distinct files could map to one vault). Append an 8-hex FNV-1a of the raw
filename so distinct wallets never share a vault. +unit test.
- LOW seed-adopt: removeVault() on adopt so the legacy wallet's PIN passphrase
isn't left associated with the new seed wallet (same file name).
- LOW: clear lock_unlock_in_progress_ on switch too.
Deferred (documented): the 1.5s start grace can't catch a wallet that fails LATE
in daemon init (the existing crash-wedge detection still applies); beginShutdown's
join of the switch task can briefly freeze the UI during quit (necessary to avoid
orphaning the daemon).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>