fix(relay,desktop): only advertise NIP-43 when enforced; probe pairing by supported_nips#601
Merged
Conversation
…ported_nips The desktop pairing flow rewrites the QR's relay URL to wss://…/pair whenever the target relay's NIP-11 doc reports auth as required. That heuristic is too broad: NIP-42 and NIP-OA relays also report auth_required: true, but the /pair sidecar is only needed for NIP-43 (relay membership), where unpaired peers can't reach the main relay yet. The relay made it worse by unconditionally advertising NIP-43 in supported_nips, regardless of whether SPROUT_REQUIRE_RELAY_MEMBERSHIP was on. Result on staging (membership off, no sidecar deployed): the QR pointed at a 404, and the phone gave up with a connect error. Relay-side fix -------------- - Drop NIP-43 from the static SUPPORTED_NIPS. - RelayInfo::build(relay_self: Option<&str>, advertise_nip43: bool): two facts, gated separately. NIP-29 group metadata (kinds 39000/39001/39002) are signed by the relay master key per nips/29.md and verified against NIP-11 self, so self must follow stable-key identity, not membership. NIP-43 is gated on stable-key AND require_relay_membership. - debug_assert!(!advertise_nip43 || relay_self.is_some()): NIP-43 events are verified against self; advertising NIP-43 without self would leave clients unable to verify membership events. - Single helper nip11_facts(&state) consumed by both /info and the content-negotiated root handler so they can't drift. Desktop-side fix ---------------- - Rename probe_relay_requires_auth -> probe_relay_supports_nip43 and read supported_nips for 43 instead of limitation.auth_required. - Unreachable / malformed / non-ws(s) responses now fall through to using the main relay rather than misrouting to an undeployed /pair sidecar. Tests ----- - nip43_not_in_static_supported_nips - build_open_relay_ephemeral_key_omits_self_and_nip43 - build_open_relay_stable_key_advertises_self_but_not_nip43 (the staging-default regression we must not reintroduce) - build_membership_relay_advertises_self_and_nip43 - build_nip43_without_self_panics_in_debug (the debug_assert) - Existing 150 sprout-relay lib tests still pass; existing e2e test_nip11_relay_info unaffected. Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
tlongwell-block
added a commit
that referenced
this pull request
May 15, 2026
* origin/main: (33 commits) dev-mcp: add view_image tool (#602) fix(relay,desktop): only advertise NIP-43 when enforced; probe pairing by supported_nips (#601) fix(desktop): derive unread state from NIP-RS + relay catch-up only (#599) docs(testing): rewrite TESTING.md for current API and CLI-first workflow (#597) fix(agent): fix OpenAI-compat request body serialization and max_tokens (#595) feat(desktop): per-persona and per-agent env var overrides (#594) fix(desktop): stop pinning agents to deprecated SPROUT_ACP_TURN_TIMEOUT (#592) fix(desktop): populate member_count in get_channels so channel browser shows real counts (#548) fix(desktop): autofocus message composer on channel/thread open (#572) refactor(cli): restructure flat commands into 12 subcommand groups (#585) feat(sdk): add builder functions for workflows, DMs, and presence (#589) feat(desktop): add message more-actions dropdown menu (#590) fix(mobile): preserve channel list across background/resume reconnection (#588) Redesign Home as an inbox (#582) fix(desktop): drive unread badges from live subscription, not refetched lastMessageAt (#581) fix(desktop): refine header scaling and shadow (#573) fix(desktop): keep day dividers below header (#574) Move agent activity below composer (#579) docs(nips): NIP-AE — Agent Engrams (#575) refactor: extract shared @mention resolver into sprout-sdk (#580) ... Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
tlongwell-block
added a commit
that referenced
this pull request
May 15, 2026
Signed-off-by: Tyler Longwell <tlongwell@squareup.com> * origin/main: dev-mcp: add view_image tool (#602) fix(relay,desktop): only advertise NIP-43 when enforced; probe pairing by supported_nips (#601) fix(desktop): derive unread state from NIP-RS + relay catch-up only (#599) docs(testing): rewrite TESTING.md for current API and CLI-first workflow (#597) fix(agent): fix OpenAI-compat request body serialization and max_tokens (#595) feat(desktop): per-persona and per-agent env var overrides (#594)
This was referenced May 28, 2026
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
Mobile pairing from the desktop GUI was broken on the staging relay (
sprout-oss.stage.blox.sqprod.co). The phone got "Could not reach the pairing relay" because the QR's relay URL pointed atwss://…/pair, which 404s — the pair sidecar isn't deployed on this relay.Two coupled bugs:
supported_nipsunconditionally, regardless of whetherSPROUT_REQUIRE_RELAY_MEMBERSHIPwas actually on.probe_relay_requires_authkeyed offlimitation.auth_required— which is alsotruefor plain NIP-42 / NIP-OA relays that don't need a pair sidecar at all.Together they produced the failure on every open-but-stable-key relay (i.e. all current Sprout deployments).
Fix
crates/sprout-relay/src/nip11.rs/router.rs:43from the staticSUPPORTED_NIPSconstant.RelayInfo::build(relay_self: Option<&str>, advertise_nip43: bool)— two facts, gated independently.selffollows stable signing-key (preserves NIP-29 group-metadata verification: pernips/29.mdline 129, kinds 39000/39001/39002 are signed by the relay master key and verified against NIP-11self; Sprout signs those unconditionally).require_relay_membership.debug_assert!(!advertise_nip43 || relay_self.is_some())— NIP-43 events are verified againstself; advertising NIP-43 without it would be a programmer bug.nip11_facts(&state)consumed by both the dedicated/infohandler and the content-negotiated root handler so they can't drift.desktop/src-tauri/src/commands/pairing.rs:probe_relay_requires_auth→probe_relay_supports_nip43. Readsupported_nipsand look for numeric 43.ws(s)://responses fall through to using the main relay (better to fail loudly there than misroute to an undeployed sidecar).Tests
New in
crates/sprout-relay/src/nip11.rs:nip43_not_in_static_supported_nipsbuild_open_relay_ephemeral_key_omits_self_and_nip43build_open_relay_stable_key_advertises_self_but_not_nip43build_membership_relay_advertises_self_and_nip43build_nip43_without_self_panics_in_debugtest_nip11_relay_infounaffected (it asserts field presence andauth_required: true, no specific NIP list).cargo clippy -p sprout-relay --no-deps -- -D warningsclean.cargo checkclean.Behavior change
selfsupported_nipsincludes 43…/pair)…/pairOut of scope but related
sprout-pair-relaysidecar isn't built, deployed, or routed by the staging helm chart. Filing a follow-up for when membership is turned on (three pieces per Mari's audit: Dockerfile build, pod deployment binding 127.0.0.1:5000, Istio VirtualService/pairmatch).Review
Pre-reviewed in-channel with Max and Mari:
selfto NIP-43 — that would have silently broken NIP-29 verification on every open-but-stable-key relay. v2 decouples them.