Mobile: scanned pairing QR cannot be opened — `nostrpair://` scheme is not declared in AndroidManifest

Open Beginner friendly
#7,643 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
Half a day
Newbie friendliness
88/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
android, dart, flutter, ios
Domain
mobile

Research direction

Start with mobile/android/app/src/main/AndroidManifest.xml and mobile/ios/Runner/Info.plist, comparing their existing buzz URL registrations with the nostrpair:// input handled in mobile/lib/features/pairing/pairing_provider.dart. Register the missing scheme on both platforms, then verify that a system-scanned pairing QR opens the app and reaches the existing pairing flow.

Written by the indexing model from the issue text.

Description

Titre : Mobile: scanned pairing QR cannot be opened — nostrpair:// scheme is not declared in AndroidManifest


Summary

Scanning the device-pairing QR code with a browser/OS-level QR scanner on Android reads the code
correctly, but tapping “Open link” fails with “Cannot resolve URL”. The generated QR payload is
a nostrpair:// URI, and the Buzz mobile app declares only the buzz:// scheme in its
AndroidManifest.xml, so Android finds no app registered to handle it.

The app itself is fine at runtime — it already parses the scheme. Only the manifest declaration is
missing, so the OS-level link open never reaches the app.

Environment

  • Component: mobile/ (Flutter app), Android
  • Buzz main @ commit 6c35e82 (2026-09-12)
  • Relay: self-hosted, single community, membership enforced (BUZZ_REQUIRE_RELAY_MEMBERSHIP=true)
  • Sidecar buzz-pair-relay deployed and reachable; NIP-11 advertises pairing_relay_url

Steps to reproduce

  1. Start a pairing session from the desktop app (it displays the QR).
  2. Scan the QR with the system camera / QR scanner (not from inside the app).
  3. The scan succeeds and the OS shows a link.
  4. Tap “Open link”.

Expected

The Buzz mobile app opens and resumes the pairing flow.

Actual

Android shows “Cannot resolve URL” (Impossible de résoudre l'URL). No app is offered.

Root cause

mobile/android/app/src/main/AndroidManifest.xml declares only:

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="buzz"/>
</intent-filter>

but the QR payload is a nostrpair:// URI — see
crates/buzz-core/src/pairing/qr.rs (module doc: “The QR code encodes a nostrpair:// URI”):

nostrpair://<source_pubkey_hex>?secret=<session_secret_hex>&relay=<url-encoded-relay>&v=1

The Dart side already handles it — mobile/lib/features/pairing/pairing_provider.dart:

final trimmed = rawInput.trim();
if (trimmed.startsWith('nostrpair://')) {
  return _pairNipAb(trimmed);
}
// Legacy buzz:// flow.
return _pairLegacy(trimmed);

and the pairing page even documents the expected input format in its hint text
(pairing_welcome_view.dart: 'nostrpair://... or buzz://...').

So the NIP-AB path is implemented, reachable by pasting the URI, and unreachable by tapping the
scanned link — the gap is purely the missing intent-filter.

Suggested fix

Add a second intent-filter for the nostrpair scheme alongside the existing buzz one:

<!-- NIP-AB device pairing: the QR carries a nostrpair:// URI. -->
<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="nostrpair"/>
</intent-filter>

iOS has the same gap — confirmed, not presumed. mobile/ios/Runner/Info.plist declares:

<key>CFBundleURLSchemes</key>
<array>
    <string>buzz</string>
</array>

nostrpair is absent there too, so the scanned link is equally unopenable on iOS. Adding
nostrpair to the same array fixes it. flutter_deeplinking_enabled is already false, so
app_links owns incoming URLs and will route them through the existing Dart handler.

Workaround (verified)

Do not tap “Open link”. Copy the raw QR text and paste it into the pairing screen's input field —
the same _pairNipAb() path runs. Confirmed working end-to-end: WebSocket 101 on /pair, then
["EOSE","pair"] returned by the sidecar.

Impact

The QR pairing flow is not usable by scanning from the system camera, which is the documented
user-facing path. Pasting the URI works, but nothing in the QR UI hints at that.

Dominant language
Rust
Stars
33.7k
Forks
4.4k
Avg merge
1d 21h
Merged PRs (30d)
239

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from block/buzz

All issues in block/buzz

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.