Skip to content

fix(desktop): keep machine onboarding for unrecognized identities after reset#2244

Merged
wpfleger96 merged 5 commits into
mainfrom
duncan/signout-dev-webview-state
Jul 21, 2026
Merged

fix(desktop): keep machine onboarding for unrecognized identities after reset#2244
wpfleger96 merged 5 commits into
mainfrom
duncan/signout-dev-webview-state

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 21, 2026

Copy link
Copy Markdown
Member

After a dev-build sign-out+reset, the Rust webview wipe renames ~/Library/WebKit/<bundle_id> — the correct path for installed .app builds. In tauri dev builds WebKit keys storage by process name (buzz-desktop/), so this wipe is a no-op and the frontend's localStorage survives the reset. The migrateMachineOnboardingCompletion heuristic added in #1936 then sees the stale active-community entry and auto-marks the freshly generated key as onboarded, skipping the machine-onboarding screen entirely — including the "Use an existing key" button.

Fix A — pubkey-sound completion heuristic

migrateMachineOnboardingCompletion in machineOnboarding.ts now accepts activeCommunityPubkey: string | null | undefined instead of a bare hasConfiguredCommunity: boolean. The community arm grants only when the recorded pubkey exactly matches the current pubkey — both absent pubkey (null) and no community (undefined) do not vouch.

Community-creation paths now stamp pubkey at write time so that absent means genuinely pre-stamp:

  • App.tsx first-community flow (handleCommunityOnboardingConnect): stamps pubkey: currentPubkey ?? undefined, where currentPubkey is threaded down from MachineBootstrap.
  • AppShell.tsx onAddCommunity callback: stamps community.pubkey ?? identityQuery.data?.pubkey. Note: AddCommunityDialog never calls its onSubmit prop — it routes all submissions through communityOnboarding.start — so this callback is currently unreachable. The stamp is correct if the dead prop is ever wired up; it costs nothing now.

The other two migration arms (completedLegacyOnboarding — already pubkey-scoped — and isSharedIdentity) are unchanged. Sprout-era veterans are safe: legacyCommunityStorage.ts migrates sprout-onboarding-complete.v1:<pubkey>buzz-onboarding-complete.v1:<pubkey> on first Buzz launch, and the v1 key predates community storage (#386 vs #409), so every legacy community's creator already holds a pubkey-scoped v1 entry.

Files: machineOnboarding.ts, App.tsx, AppShell.tsx

Fix B — sign-out clears origin web storage on success

The confirm handler for "Delete My Data" in ProfileSettingsCard.tsx now calls window.localStorage.clear() + window.sessionStorage.clear() immediately after signOut() resolves. This closes the dev-build gap directly at the sign-out call site.

  • Clears on the resolve path only — a failed sign-out must not wipe a still-running app.
  • In production the Rust wipe already handles this; the clear is redundant but harmless.
  • The restart may race the clear — acceptable. Fix A is the correctness gate; Fix B is belt-and-braces.

Files: ProfileSettingsCard.tsx

E2E fixture fix — seedDefaultCommunity pubkey stamp

seedDefaultCommunity in desktop/tests/helpers/bridge.ts now stamps pubkey on the fixture community. The init script reads buzz:e2e-identity-override.v1 from localStorage (written by seedActiveIdentity, which always runs before installMockBridge across all affected specs), validates its shape against the same three-field check as readStoredIdentityOverride() (privateKey, pubkey, and username must all be strings), and uses the override pubkey when valid. Malformed JSON, stored null, and partial objects fall through to the fallback pubkey (identity?.pubkey ?? DEFAULT_MOCK_PUBKEY). This ensures the fixture community's provenance matches the active identity the strict voucher checks.

Six previously-failing smoke specs now pass: onboarding-avatar-skip.spec.ts (×5) and onboarding-docked-cta-screenshots.spec.ts (×1). These specs seed BLANK_TYLER_IDENTITY via seedActiveIdentity + skipOnboardingSeed: true, so the default community was their only voucher.

Files: desktop/tests/helpers/bridge.ts

Tests

Seven unit tests in machineOnboarding.test.mjs:

  • Mismatched community pubkey → NOT vouched (original regression case)
  • Matching community pubkey → vouched
  • Absent community pubkey (null) → NOT vouched (stale current-format community + no stamp + fresh identity)
  • No community (undefined) → NOT vouched
  • Legacy v1 completion key → granted
  • Shared identity → always granted
  • Already-completed v2 key → returns true immediately

Playwright coverage: onboarding-avatar-skip.spec.ts (5/5), onboarding-docked-cta-screenshots.spec.ts (3/3), onboarding.spec.ts (57/57), deep-link-invite.spec.ts (included in combined run above, no regressions).

import.meta.env guard

forceMachineOnboarding() reads import.meta.env.DEV — Vite inlines this at bundle time, but the Node test runner has no import.meta.env. Added ?.DEV optional chain so the function returns false in test context. No behavior change in any built artifact.

…er reset

After a dev-build sign-out+reset the Rust webview wipe targets the
.app-bundle WebKit directory, which does not exist for `tauri dev`
builds. The frontend's localStorage survived, including the active
community entry. On relaunch the #1936 migration heuristic
(migrateMachineOnboardingCompletion) counted that stale community as
proof of prior completion, auto-marking the freshly generated key as
onboarded and skipping machine onboarding entirely — landing the user
in a half-onboarded relay-profile flow with no 'Use an existing key'
option.

Fix A (the regression): the hasConfiguredCommunity arm now checks
Community.pubkey against the current pubkey. A community whose
recorded pubkey doesn't match the live key is not a valid voucher.
Absent pubkey (legacy entries predating the field) still vouches, so
the veteran-upgrade path is preserved.

Fix B (defence-in-depth): the sign-out confirm handler clears
localStorage and sessionStorage on the resolve path only, covering
the dev-build gap where the Rust wipe misses the process-keyed
WebKit directory. The clear is a no-op in production (Rust already
handles it) and intentionally does not run on the reject path (a
failed sign-out must not wipe a still-running app).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 requested a review from a team as a code owner July 21, 2026 17:43
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 4 commits July 21, 2026 10:54
…oucher

Thufir pass 1: addCommunity() in App.tsx's first-community flow and
AppShell.tsx's add-community dialog both created records without
stamping Community.pubkey. Any such record — including a current-format
entry created in the session before a dev reset — lands in localStorage
with pubkey absent. The previous migration treated absent-pubkey as
a legacy signal and granted completion to every pubkey, so the
regression was still reachable.

This pass:
- Stamps pubkey at both addCommunity call sites (App.tsx and
  AppShell.tsx) using the active identity pubkey at create time.
- Removes the null-vouching arm from migrateMachineOnboardingCompletion:
  absent pubkey no longer vouches. A stale community without a stamp
  (legacy or pre-fix build) cannot trigger machine-onboarding bypass.
- Adds the required regression case: absent-pubkey community + fresh
  identity → machine onboarding remains incomplete.
- Updates currentPubkey in the useCallback deps array (biome lint).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
seedDefaultCommunity() created the community without a pubkey field.
With the strict migrateMachineOnboardingCompletion voucher (absent pubkey
no longer grants), any test that seeds this community but skips the v1
onboarding-completion seed would be routed to machine onboarding —
breaking six smoke E2E tests that use a Tyler identity.

Pass the active identity pubkey into seedDefaultCommunity and stamp it
on the fixture community, matching what production creation paths now do.
Uses identity?.pubkey ?? DEFAULT_MOCK_PUBKEY so mock-mode tests (where
the Tauri mock drives a deadbeef identity) also get a matched record.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The previous stamp used identity?.pubkey ?? DEFAULT_MOCK_PUBKEY, which
is always DEFAULT_MOCK_PUBKEY in mock mode regardless of seedActiveIdentity
overrides. Tests that call seedActiveIdentity(BLANK_TYLER_IDENTITY) before
installMockBridge() end up with a community stamped deadbeef... while the
app identity is Tyler — strict matching rejects the voucher.

Read buzz:e2e-identity-override.v1 from localStorage inside the init
script (addInitScript runs in registration order, so the seedActiveIdentity
write always precedes this read). Fall back to the passed pubkey (bridge
identity in relay mode, DEFAULT_MOCK_PUBKEY for mock) only when no valid
override exists. Mirror the storage key as a const in the init-script args
so the script closure doesn't capture module-scope imports.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Mirror readStoredIdentityOverride()'s shape validation exactly: wrap
the parse in try/catch and only set overridePubkey when privateKey,
pubkey, and username are all strings. Malformed JSON, stored null, and
partial objects (e.g. missing privateKey/username) now fall through to
the fallback pubkey instead of throwing or accepting a shape that
runtime readStoredIdentityOverride() would reject.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 enabled auto-merge (squash) July 21, 2026 19:00
@wpfleger96
wpfleger96 disabled auto-merge July 21, 2026 19:01
@wpfleger96
wpfleger96 merged commit fd55ab6 into main Jul 21, 2026
26 checks passed
@wpfleger96
wpfleger96 deleted the duncan/signout-dev-webview-state branch July 21, 2026 19:24
loganj added a commit that referenced this pull request Jul 21, 2026
…cache

* origin/main:
  feat(desktop): fuzzy emoji shortcode autocomplete (#1945)
  Revert "feat(relay): inventory unreachable Git objects" (#2275)
  feat(relay): inventory unreachable Git objects (#2264)
  Keep avatar preview visible during upload (#2237)
  fix(desktop): keep machine onboarding for unrecognized identities after reset (#2244)
  fix(managed-agents): stale harness pin shadows runtime edits; add Restart quick action (#2252)
  fix(desktop): surface model discovery failures in agent config UI (#2246)
  Hide bundled harnesses from onboarding (#2233)
  relay: add author_type label to buzz_events_stored_total (#2243)
  fix(justfile): use Hermit-pinned lefthook in hooks recipe (#2241)
  Refresh README screenshots (#2236)
  fix(acp): pace relay observer frames (6/s + 90/min, zero burst) (#2217)
  Restore npub copy option for private community joins (#2232)
  fix(desktop): align onboarding runtime auth (#2229)
  fix(desktop): survive degraded networks with rate-limit-aware relay client (#2197)
  fix(cli): retry transient relay failures and raise timeouts (#2196)
  feat(desktop): browse immutable repository tags (#2231)
  Update built-in agent avatars (#2215)
  cleanup old AI doc (#2227)
  chore(release): release Buzz Mobile version 0.4.11 (#2225)
loganj added a commit to loganj/buzz that referenced this pull request Jul 22, 2026
* origin/main: (24 commits)
  feat(relay): log NIP-98 pubkey attribution on HTTP bridge requests (block#2206)
  fix(ci): don't rebuild relay tests on every run (block#2203)
  fix(desktop): refresh cached channel member names (block#2258)
  chore(release): release Buzz Desktop version 0.4.22 (block#2220)
  feat(desktop): fuzzy emoji shortcode autocomplete (block#1945)
  Revert "feat(relay): inventory unreachable Git objects" (block#2275)
  feat(relay): inventory unreachable Git objects (block#2264)
  Keep avatar preview visible during upload (block#2237)
  fix(desktop): keep machine onboarding for unrecognized identities after reset (block#2244)
  fix(managed-agents): stale harness pin shadows runtime edits; add Restart quick action (block#2252)
  fix(desktop): surface model discovery failures in agent config UI (block#2246)
  Hide bundled harnesses from onboarding (block#2233)
  relay: add author_type label to buzz_events_stored_total (block#2243)
  fix(justfile): use Hermit-pinned lefthook in hooks recipe (block#2241)
  Refresh README screenshots (block#2236)
  fix(acp): pace relay observer frames (6/s + 90/min, zero burst) (block#2217)
  Restore npub copy option for private community joins (block#2232)
  fix(desktop): align onboarding runtime auth (block#2229)
  fix(desktop): survive degraded networks with rate-limit-aware relay client (block#2197)
  fix(cli): retry transient relay failures and raise timeouts (block#2196)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants