feat(desktop): add Sign Out to Settings to reset and relaunch#1842
Open
wpfleger96 wants to merge 5 commits into
Open
feat(desktop): add Sign Out to Settings to reset and relaunch#1842wpfleger96 wants to merge 5 commits into
wpfleger96 wants to merge 5 commits into
Conversation
Mathieu and Kalvin reported that switching Nostr identities required manually deleting keychain entries, ~/Library/Application Support dirs, WebKit localStorage, ~/.buzz, and ~/.config/buzz-agent — an opaque and error-prone process that blocked routine identity switching. Implements a two-step Sign Out action in Settings → Profile: Rust (commands/identity.rs): - `sign_out` command stops all managed agents, deletes keychain blobs for both buzz-desktop and buzz-desktop-dev services, removes all App Support and WebKit dirs matching xyz.block.buzz.app* and xyz.block.sprout.app* prefixes (covers dev-worktree variants), removes the nest (~/.buzz / ~/.buzz-dev), legacy ~/.sprout, OAuth token cache, and CLI symlinks, then relaunches via exec+exit(0). - `SecretStore::delete_all()` removes the entire blob entry under the interprocess advisory file lock, bypassing the per-key delete path. - Guarded by `is_shared_identity()` — no-op in harness/CI mode where the key comes from BUZZ_PRIVATE_KEY and keychain wipe is meaningless. Frontend (ProfileSettingsCard.tsx): - Sign Out section below the Identity card with an AlertDialog confirmation before the irreversible wipe. - Spinner + disabled state while the wipe is in flight; toast on the error path (sign_out never resolves on success — it restarts the process). Also removes a stale biome-ignore suppression comment in useIndependentThreadPanel.ts and replaces non-null assertions with optional chaining in e2eBridge.ts (pre-existing lint issues that were blocking the pre-commit hook).
wpfleger96
added a commit
that referenced
this pull request
Jul 14, 2026
Collaborator
Author
Captures the Settings → Profile sign-out section and the confirmation AlertDialog, exercising the testids added in the sign_out feature.
Replace the inline sign-out wipe (which left a crash window between keychain delete and app-data delete) with a sentinel-based two-phase design: Phase 1 — sign_out: writes a durable `.bundle-id.reset-pending` sentinel outside all wiped paths, then relaunches. No destruction happens in the command itself, so a crash between write and relaunch is safe — the sentinel persists and the wipe completes on the next open. Phase 2 — boot-time hook at the top of setup(): if the sentinel is present, perform the full wipe before migrations and identity resolution: - Rename app-data dir atomically (.trash-<pid>), same for WebKit dir - Remove nest, ~/.sprout, ~/.config/buzz-agent, this build's CLI symlink - Keychain: delete_all_with_legacy_cleanup() which reads the blob to collect all key names and deletes per-key legacy DPK/keyring entries, preventing old identity resurrection through migrate_legacy_key() - Verify: keychain empty + app-data absent + nest absent - On success: delete sentinel, set completed flag, fall into clean onboarding - On failure: keep sentinel (retry next boot), set reset_failed flag Additional correctness fixes: - Wipe is scoped to the running build only (not prod+dev simultaneously) - reset_completed suppresses migrate_legacy_nest() and migrate_dev_nest() so dev-build reset cannot re-import ~/.buzz into ~/.buzz-dev - reset_failed flag surfaces a recovery screen in the frontend Tests: 7 unit tests in reset.rs via injected ResetKeychain trait (fake).
… test Bump ProfileSettingsCard.tsx px-text override from :672 → :684 to account for sign-out section line drift. Add a real-keychain integration test that seeds a legacy per-key `identity` entry + blob, calls `delete_all_with_legacy_cleanup()`, and proves probe/load cannot resurrect the old identity — closes the CRITICAL-adjacent gap the FakeKeychain tests cannot cover.
F1 (CRITICAL): The wipe left the legacy Sprout App Support dir untouched, so `migrate_legacy_app_data_dir` could restore the old identity on next boot. Expose `legacy_app_data_dir` and `is_dev_data_dir_name` as `pub(crate)` from `migration.rs`, add `legacy_app_data_dir` and `nest_dir` fields to `ResetContext`, compute both in `run_boot_reset`, and rename-then-delete the legacy dir atomically in Step 1b. Verify its absence in Step 6. F2 (CRITICAL): DPK blob and per-key DPK deletes in `delete_all_with_legacy_cleanup` were `let _ =` — real errors were silently swallowed. Replace with match arms that propagate real errors (not-found and dpk-unavailable are treated as success). Add `verify_fully_wiped` to `SecretStore` and `ResetKeychain` trait that checks all three keychain shapes the migration path can consume (main blob, DPK blob, per-key "identity"). Replace the `probe_empty` verification call with `verify_fully_wiped`. F3 (IMPORTANT): Tests 6 and 7 only asserted sentinel path strings. Replace them with behavioral tests that create both `.buzz` and `.buzz-dev` dirs, inject `nest_dir` into `ResetContext`, run the wipe, and assert the target nest is gone while the opposite nest survives. Add tests for `legacy_app_data_dir` removal and that `completed` is `true` on success. F4 (MINOR): Use `crate::migration::is_dev_data_dir_name` (the authoritative discriminator) instead of an ad-hoc `starts_with` in `run_boot_reset` and `lib.rs`. Use `keyring_service()` from `app_state` instead of duplicating the inline service string.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Adds a Sign Out action to Settings → Profile that wipes all local Buzz state and relaunches into first-run onboarding — replacing a manual process that required deleting keychain entries,
~/Library/Application Supportdirectories, WebKit localStorage,~/.buzz, and~/.config/buzz-agent.commands/identity.rs):sign_outTauri command stops all managed agents, deletes keychain blobs forbuzz-desktopandbuzz-desktop-dev(viaSecretStore::delete_all()), removes all App Support and WebKit directories matchingxyz.block.buzz.app*andxyz.block.sprout.app*prefixes (covers dev-worktree variants), removes the agent nest, legacy Sprout nest, OAuth token cache, and CLI symlinks, then relaunches viaexec + exit(0). Guarded byis_shared_identity()— no-op in harness/CI mode.secret_store.rs:delete_all()removes the entire keychain blob under the interprocess advisory lock and clears the in-memory cache.ProfileSettingsCard.tsx): Sign Out section at the bottom of Profile settings with anAlertDialogconfirmation (warns to back up nsec first), spinner + disabled state while in-flight, toast on the error path. The command never resolves on success — it restarts the process.tauriIdentity.ts:signOut()binding.check-file-sizes.mjs: Updated overrides forsecret_store.rs(1148) andProfileSettingsCard.tsx(1005) with load-bearing justifications.