[HIGH] Create-with-nametag is not atomic: a failure after the Nostr binding publishes burns the nametag forever
Summary
useOnboardingFlow.handleMintNametag (create branch) calls createWallet({ nametag }). The SDK stores the mnemonic and then registerNametag publishes the first-write-wins Nostr binding before the final persistAddressNametags() IndexedDB write (no try/catch). If anything throws after the relay accepted the binding (WebSocket drop before OK ack, IndexedDB blocked after a recent DB deletion — a state the app itself acknowledges at SphereProvider.tsx:463), createWallet rejects and SphereProvider.cleanupOnError runs Sphere.clear(...), destroying the only key that owns the nametag. The mnemonic was never shown (backup screen comes later). Retry @alice → "already taken", bound to a key that no longer exists anywhere. Identity permanently lost.
Where
src/components/wallet/onboarding/hooks/useOnboardingFlow.ts:545 (create) + src/sdk/SphereProvider.tsx:513 (cleanupOnError).
Fix (app-side)
Create the wallet without the nametag first → show/download the mnemonic → register the nametag as a separate, retryable step. Never wipe storage after the binding may have been published. Related to the known nametag-squatting trap.
Companion SDK improvement (optional)
The deeper root is in the SDK: registerNametag publishes the binding before persisting with no rollback path, and it runs inside Sphere.create. Consider making registration atomic / extractable from create so the app cannot end up with a published binding it can no longer own. Tracked separately in the SDK repo.
Found by multi-agent audit @ main b477d4d7; adversarially verified.
Source: unicity-sphere/sphere