Editing a community's Relay URL orphans the existing Welcome Team and mints a fresh Fizz/Honey/Bumble trio (exact-string match, no canonicalization)
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
Changing a community's configured Relay URL to a different string — even when it points at the exact same relay — silently orphans the existing Welcome Team (Fizz/Honey/Bumble) and mints a brand-new trio with fresh keypairs. This is a distinct root cause from #2648 (second Desktop install) and #3639 (`forceNewInstance: true` on "Deploy team to channel"): no second install and no manual team-deploy involved, just an ordinary relay URL edit.
## Root cause
Two places do a bare exact-string comparison on the relay URL, with no canonicalization anywhere in the path:
1. `hasEnsuredWelcomeChannel` / `markWelcomeChannelEnsured` (`desktop/src/features/onboarding/welcome.ts:284-324`) build a `localStorage` key by embedding `communityScope` (= `activeCommunity.relayUrl`, unmodified) verbatim via `welcomeChannelEnsuredStorageKey`. A different string for the same relay → cache miss → treated as a never-provisioned community.
2. `isAgentScopedToRelay` (`desktop/src/features/onboarding/welcomeGuide.ts:56-62`), used by `pickWelcomeTeamStarterAgentForRelay`, compares `normalizeRelayUrl(agent.relayUrl) === normalizeRelayUrl(targetRelayUrl)` — `normalizeRelayUrl` only trims trailing slashes, it doesn't resolve hostnames vs IPs vs schemes to a canonical identity. An existing Fizz/Honey/Bumble stamped with one string is invisible to a lookup using a different (but logically identical) string.
The re-provisioning path itself is `useAppOnboardingState`'s effect in `desktop/src/features/onboarding/hooks.ts:578-595`, which calls `requestStarterChannels(false)` → `initializeStarterChannels` → `seedWelcomeExperience` → `ensureWelcomeTeam` (`welcomeGuide.ts:343-356` → `provisionWelcomeTeam`) whenever `hasEnsuredWelcomeChannel` misses for the current `communityScope`. `provisionWelcomeTeam` (`welcomeGuide.ts:268-341`) does look for an existing agent per persona via `pickWelcomeTeamStarterAgentForRelay` before creating one — but since that lookup is also keyed on the (mismatched) relay URL string, it never finds the existing instance and calls `createManagedAgent` instead, minting a new keypair.
## Real-world reproduction (not a synthetic repro — this is what actually happened on one Desktop install)
Self-hosted single-user community, single Desktop install, no second machine, no manual "Deploy team to channel" click. `managed-agents.json` currently has 3 records each for Bumble/Fizz/Honey (9 total for what should be 3), in three timestamp clusters, each stamped with a different `relay_url`:
| `relay_url` on the created agents | Created at | Trigger |
|---|---|---|
| `ws://localhost:3000` | 2026-07-22T19:28 | First-run default (`defaultRelayUrl` in `useCommunityInit.ts`), before the real relay was configured |
| `ws://100.119.50.98:3000` (a raw Tailscale IP, non-TLS, port 3000) | 2026-07-27T21:33 | Community's Relay URL edited to point at the relay via its Tailscale IP directly |
| `wss://:8443` (the final, correct address, TLS-terminated via `tailscale serve`) | 2026-07-27T21:39 (six minutes later) | Community's Relay URL edited again, to the final hostname, while wiring up mobile pairing that same session |
Every persona has a distinct pubkey per cluster. The two abandoned clusters are stale: never restarted since creation (`last_started_at: null`), sitting as dead `bot` members in the private "Welcome" channel with the same display names as the live ones, indistinguishable in the `@mention` picker (same failure mode independently reported in #2648 by `AI-OWEN`).
The user reported having removed the stale channel members once already; they reappeared "a few minutes later" — which lines up exactly with the two edits six minutes apart above, not a background process re-triggering on every app launch. Desktop has since run 13+ continuous hours without the relay URL changing again, and no further duplicate cluster has appeared, consistent with the trigger being the URL edit itself rather than a boot-time race.
## Expected behavior
Editing a community's Relay URL should not orphan and re-mint the Welcome Team. Options:
- Resolve/canonicalize relay identity by community ID (already the real join key elsewhere in the app) rather than the raw URL string, for both the `localStorage` ensured-marker and the agent-reuse lookup.
- At minimum, when a Relay URL is edited for an *existing* community, offer to re-point (not re-mint) the existing Welcome Team agents at the new URL, the same way other community-scoped state gets migrated on a relay change.
## Related
- #2648 — second-install duplication (identity-per-machine, different trigger)
- #3639 — `forceNewInstance: true` on Deploy team to channel (different trigger, same class of symptom)
- #4593 — foundational identity-portability work referenced in #2648; doesn't address this path either, since this isn't a cross-device identity problem
Contributor guide
Research direction
Start by tracing the onboarding flow in desktop/src/features/onboarding/welcome.ts, welcomeGuide.ts, and hooks.ts, including the ensured marker and starter-agent lookup. Reproduce the issue by editing a community's Relay URL, then verify that an existing Welcome Team is reused rather than orphaned or duplicated when equivalent relay URL strings are used.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100