Skip to content

fix(desktop): harden relay mesh connect p-tag#834

Merged
tlongwell-block merged 6 commits into
mainfrom
max/mesh-connect-p-tag-hardening
Jun 4, 2026
Merged

fix(desktop): harden relay mesh connect p-tag#834
tlongwell-block merged 6 commits into
mainfrom
max/mesh-connect-p-tag-hardening

Conversation

@tlongwell-block

@tlongwell-block tlongwell-block commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Defense-in-depth for relay-mesh connect requests + the desktop e2e harness. Hardens the create-agent → Run on relay mesh path so a malformed reporter pubkey fails locally with a clear error instead of producing an opaque relay rejection downstream:

  • canonicalize the selected mesh target reporter pubkey before signing kind 24621 (trim → lowercase → require canonical 64-hex; otherwise throw a clear invalid-reporter-pubkey error locally)
  • make the e2e WebSocket bridge reject mock kind 24621 connect requests missing a #p target instead of blindly ACKing them
  • Playwright coverage that Create Agent → Run on relay mesh signs a kind 24621 with canonical ["p", <64-hex>], including an uppercase/whitespace reporter-pubkey regression case

What this is and is NOT

Is: correct, verified hardening of the one create-path publisher + a real regression guard.

Is NOT (and we are explicit about this): a confirmed fix for the live report —

Mesh client failed to start: invalid: mesh connect request missing #p target

Joint trace (Eva + Max) findings, now reconciled:

  • "invalid: mesh connect request missing #p target" exists in exactly one place — sprout-relay/src/handlers/mesh_signaling.rs. It fires only when an incoming 24621 carries zero ["p", <nonempty>].
  • The only Sprout-24621 publisher in the desktop tree is TS publishMeshConnectRequest, reachable through exactly one chain: CreateAgentDialog → startRelayMeshClientForTarget → publishMeshConnectRequest (exhaustively enumerated; no restore/auto-start hook touches it).
  • That publisher already attaches ["p", targetPubkey], and startRelayMeshClientForTarget guards an empty reporterPubkey with a different error. Against the relay's actual nostr dep, a non-canonical p-tag value (uppercase/whitespace/npub) is preserved, so it would surface as a membership/identity error, not "missing #p".
  • The mesh SDK does not publish a Sprout 24621 at all (it gossips kind 31990 listings to public relays). So the Rust agent-start/restore path cannot be the direct source of this exact relay string.

Net: current main should not be able to emit "missing #p" on any traced path. The live repro therefore implies either a build predating these guards, or a publisher not yet found — pending the raw outbound 24621.tags frame or the failing build SHA.

Known structural gap (separate follow-up, not this PR)

Saved relay-mesh agents on restore/manual-start resolve a target by model_id and then discard everything but endpoint_addr. Reconnecting cleanly needs a target Nostr identity:

  • own-machine target → no relay punch at all; dial locally (relay rejects self-#p anyway).
  • peer target → no peer Nostr pubkey is carried in mesh status today (PeerInfo/PeerAnnouncement are iroh-addressed + owner_id, not Sprout pubkeys). Near-term: fail with actionable copy. Full fix: protocol work to carry a per-target Nostr pubkey in relay status.

A containment patch for the targetless cold-start path exists (Max) and is parked pending the evidence above, so we don't ship behavior changes against an unconfirmed mechanism.

Test plan

  • bin/pnpm --dir desktop exec biome check src/shared/api/relayMeshSignaling.ts src/testing/e2eBridge.ts tests/e2e/mesh-compute.spec.ts tests/helpers/bridge.ts
  • bin/pnpm --dir desktop typecheck
  • bin/pnpm --dir desktop check:file-sizes
  • bin/pnpm --dir desktop test:e2e:integration -- mesh-compute.spec.ts ✅ 5/5
  • pre-commit / pre-push hooks ✅ (run with repo bin/ first on PATH to use Hermit Rust)

Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block tlongwell-block force-pushed the max/mesh-connect-p-tag-hardening branch 2 times, most recently from cf8c886 to abda3c6 Compare June 3, 2026 21:32
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block tlongwell-block force-pushed the max/mesh-connect-p-tag-hardening branch from abda3c6 to ea7762c Compare June 3, 2026 22:08
npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta added 2 commits June 3, 2026 18:50
Signed-off-by: Max <max@sprout.local>
Signed-off-by: Max <max@sprout.local>
@tlongwell-block tlongwell-block force-pushed the max/mesh-connect-p-tag-hardening branch from f99a1b4 to 52ba3d1 Compare June 4, 2026 01:23
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block tlongwell-block force-pushed the max/mesh-connect-p-tag-hardening branch from 5e6cb63 to 07f9840 Compare June 4, 2026 15:38
@tlongwell-block tlongwell-block merged commit 34ac3ba into main Jun 4, 2026
15 checks passed
@tlongwell-block tlongwell-block deleted the max/mesh-connect-p-tag-hardening branch June 4, 2026 15:50
michaelneale added a commit that referenced this pull request Jun 5, 2026
* origin/main:
  chore(release): release version 0.3.11 (#865)
  fix(mobile+desktop): cross-device read state sync + diagnostic logging (#843)
  feat(mobile): star channels (Slack-style favorites) (#863)
  feat: desktop-screenshot skill to stop agents uploading relay media to PRs (#862)
  feat(desktop): star channels (Slack-style favorites) (#860)
  fix(desktop): handle symlinked persona pack directories (#859)
  feat: channel muting for desktop and mobile (#838)
  feat(acp): default SPROUT_ACP_MEMORY to on (#854)
  fix(desktop): eliminate image-hover layout jump in messages (#813)
  chore(release): release version 0.3.10 (#849)
  fix(desktop): harden relay mesh connect p-tag (#834)
  fix(desktop): scroll activity panel to bottom on open (#848)
  Polish desktop profile menu interactions (#836)
  fix(desktop): outline thread hover targets (#845)
  fix(desktop): keep message actions hover-only (#844)
  fix(desktop): let inbox composer fill available width (#841)

# Conflicts:
#	desktop/src/app/AppShell.tsx
#	desktop/src/features/workspaces/useWorkspaceInit.ts
tlongwell-block pushed a commit that referenced this pull request Jun 5, 2026
* origin/main: (39 commits)
  docs: add VISION_MESH.md — the compute-commons vision (#867)
  fix(desktop): simplify profile popover header (#853)
  fix(desktop): remove thread comment hover outline (#861)
  feat(desktop): always show channel section search/add buttons (#856)
  chore(release): release version 0.3.11 (#865)
  fix(mobile+desktop): cross-device read state sync + diagnostic logging (#843)
  feat(mobile): star channels (Slack-style favorites) (#863)
  feat: desktop-screenshot skill to stop agents uploading relay media to PRs (#862)
  feat(desktop): star channels (Slack-style favorites) (#860)
  fix(desktop): handle symlinked persona pack directories (#859)
  feat: channel muting for desktop and mobile (#838)
  feat(acp): default SPROUT_ACP_MEMORY to on (#854)
  fix(desktop): eliminate image-hover layout jump in messages (#813)
  chore(release): release version 0.3.10 (#849)
  fix(desktop): harden relay mesh connect p-tag (#834)
  fix(desktop): scroll activity panel to bottom on open (#848)
  Polish desktop profile menu interactions (#836)
  fix(desktop): outline thread hover targets (#845)
  fix(desktop): keep message actions hover-only (#844)
  fix(desktop): let inbox composer fill available width (#841)
  ...
tlongwell-block pushed a commit that referenced this pull request Jun 5, 2026
* origin/main: (39 commits)
  docs: add VISION_MESH.md — the compute-commons vision (#867)
  fix(desktop): simplify profile popover header (#853)
  fix(desktop): remove thread comment hover outline (#861)
  feat(desktop): always show channel section search/add buttons (#856)
  chore(release): release version 0.3.11 (#865)
  fix(mobile+desktop): cross-device read state sync + diagnostic logging (#843)
  feat(mobile): star channels (Slack-style favorites) (#863)
  feat: desktop-screenshot skill to stop agents uploading relay media to PRs (#862)
  feat(desktop): star channels (Slack-style favorites) (#860)
  fix(desktop): handle symlinked persona pack directories (#859)
  feat: channel muting for desktop and mobile (#838)
  feat(acp): default SPROUT_ACP_MEMORY to on (#854)
  fix(desktop): eliminate image-hover layout jump in messages (#813)
  chore(release): release version 0.3.10 (#849)
  fix(desktop): harden relay mesh connect p-tag (#834)
  fix(desktop): scroll activity panel to bottom on open (#848)
  Polish desktop profile menu interactions (#836)
  fix(desktop): outline thread hover targets (#845)
  fix(desktop): keep message actions hover-only (#844)
  fix(desktop): let inbox composer fill available width (#841)
  ...

Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
tellaho pushed a commit that referenced this pull request Jun 8, 2026
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
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.

1 participant