deploy/compose: mobile pairing broken out of the box — bundle ships no buzz-pair-relay and relay 404s the legacy /pair fallback
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Symptom
On a self-hosted relay deployed from the official `deploy/compose` bundle, mobile pairing fails for every user: the desktop's Settings → mobile-pairing card generates a QR, the phone scans it, and the connection errors out.
## Root cause
The pairing flow depends on a **dedicated pairing relay** (`buzz-pair-relay`), and the self-hosted bundle neither runs one nor tells the desktop where to find one:
1. `start_pairing` (desktop) probes the main relay's NIP-11 for `pairing_relay_url` — the compose bundle sets no `BUZZ_PAIRING_RELAY_URL`, so the field is absent.
2. Fallback: because the relay advertises NIP-43, `pairing_relay_from_nip11` resolves to the **legacy `/pair` path** on the main relay.
3. `buzz-relay` does not serve `/pair` (no such route in `router.rs`). Live check on a fresh bundle deploy:
```
GET https:///pair → HTTP 404
WS upgrade /pair → HTTP 404
```
4. The QR therefore encodes a dead endpoint, and the phone fails to connect.
Net: **every deployment from `deploy/compose` has mobile pairing broken out of the box** — no error on the relay side, nothing in the docs, and the operator's only signal is a generic connection error on the phone.
The kicker: the relay image **already ships the fix** — `/usr/local/bin/buzz-pair-relay` is present in `ghcr.io/block/buzz:main`; it just isn't wired into the bundle.
## Verified fix (running in production tonight)
On our deployment we added a `pair` service to the compose stack using the same image:
```yaml
pair:
image: ${BUZZ_IMAGE:-ghcr.io/block/buzz:main}
entrypoint: ["/usr/local/bin/buzz-pair-relay"]
environment:
- BUZZ_PAIR_RELAY_BIND_ADDR=0.0.0.0:5000
restart: unless-stopped
```
…exposed it at `wss://pair.`, and set `BUZZ_PAIRING_RELAY_URL=wss://pair.` on the relay. Result: NIP-11 advertises `pairing_relay_url`, the desktop QR encodes the working endpoint, and pairing proceeds.
## Proposed upstream change (PR to follow — I'm claiming this)
- Add the `pair` service to `deploy/compose/compose.yml`.
- Route it in the Caddy TLS overlay — likely as a `/pair` path proxy on the main domain, which also makes the existing NIP-43 legacy fallback Just Work with zero extra DNS, plus document `BUZZ_PAIRING_RELAY_URL` for split-domain setups.
- Document both in `deploy/compose/.env.example` and the bundle README.
Related, for context: #1953 (closed — pairing relay auth optionality), #2324 (pairing how-to docs request), #2595 (QR version validation). None cover the missing sidecar in the self-hosted bundle.
## Environment
- `ghcr.io/block/buzz:main` (2026-07-24), single-node `deploy/compose` derivative on Docker 29.x, desktop 0.4.24, iOS app current TestFlight.
Contributor guide
Assessment
This issue has not been assessed yet.