diff --git a/deploy/compose/Caddyfile b/deploy/compose/Caddyfile index 205cf4c5bc..73852c73e0 100644 --- a/deploy/compose/Caddyfile +++ b/deploy/compose/Caddyfile @@ -1,5 +1,13 @@ {$BUZZ_DOMAIN} { encode zstd gzip - reverse_proxy relay:3000 + # Device-pairing sidecar (compose.pair.yml). If the pairing overlay is + # disabled, requests to /pair fail with 502 instead of reaching the relay. + handle /pair { + reverse_proxy pair-relay:5000 + } + + handle { + reverse_proxy relay:3000 + } } diff --git a/deploy/compose/README.md b/deploy/compose/README.md index bb0e63fe15..0af286318c 100644 --- a/deploy/compose/README.md +++ b/deploy/compose/README.md @@ -44,6 +44,31 @@ keypair. `.env`; use the Helm chart or a custom Compose configuration for providers such as new Railway Storage Buckets that require `virtual` addressing. +## Device pairing + +Mobile QR pairing (NIP-AB) needs the `buzz-pair-relay` sidecar: a +membership-gated (NIP-43) relay rejects unpaired devices, so the pairing +handshake runs through a separate ephemeral relay instead. Without it, phones +scanning the desktop QR fail with a WebSocket 404 on `/pair`. + +The TLS stack includes the sidecar by default (`compose.pair.yml`): + +- Runs `buzz-pair-relay` from the same relay image. +- Caddy routes `/pair` to it; everything else still goes to the relay. +- Sets `BUZZ_PAIRING_RELAY_URL=wss://$BUZZ_DOMAIN/pair` on the relay so the + pairing URL is advertised in NIP-11. + +Set `BUZZ_COMPOSE_PAIRING=false` to opt out — `/pair` then returns 502 from +Caddy. Pairing is not wired for the non-TLS stack: there is no reverse proxy +to route `/pair`, and iOS requires `wss://` anyway. + +Verify after `./run.sh start`: + +```bash +curl -fsS "https://" -H 'Accept: application/nostr+json' | grep -o 'pairing_relay_url[^,]*' +curl -is "https:///pair" | head -1 # expect HTTP 400 (non-WebSocket request rejected) +``` + Run `./run.sh backup-hint` for the backup checklist. ## Validation diff --git a/deploy/compose/compose.pair.yml b/deploy/compose/compose.pair.yml new file mode 100644 index 0000000000..05b4c9ac93 --- /dev/null +++ b/deploy/compose/compose.pair.yml @@ -0,0 +1,29 @@ +# Device-pairing sidecar (NIP-AB) overlay. Included automatically by run.sh +# when BUZZ_COMPOSE_TLS=true (opt out with BUZZ_COMPOSE_PAIRING=false). +# Pairing requires the Caddy overlay: the phone connects over wss:// and the +# Caddyfile routes /pair to this service. +services: + relay: + environment: + # Advertised in the relay's NIP-11 document so clients connect here + # directly instead of guessing the legacy /pair path. + BUZZ_PAIRING_RELAY_URL: wss://${BUZZ_DOMAIN:?set BUZZ_DOMAIN}/pair + + pair-relay: + image: ${BUZZ_IMAGE:-ghcr.io/block/buzz:main} + # The image ENTRYPOINT is buzz-relay; entrypoint (not command) is required + # to run the sidecar binary instead. + entrypoint: ["/usr/local/bin/buzz-pair-relay"] + environment: + BUZZ_PAIR_RELAY_BIND_ADDR: 0.0.0.0:5000 + # TCP connect probe over /dev/tcp because the runtime image has bash but + # no curl/wget/socat (same approach as the relay healthcheck). + healthcheck: + test: ["CMD-SHELL", "bash -ec 'exec 3<>/dev/tcp/127.0.0.1/5000'"] + interval: 10s + timeout: 3s + retries: 12 + start_period: 5s + restart: unless-stopped + networks: + - buzz-net diff --git a/deploy/compose/run.sh b/deploy/compose/run.sh index d5465ea1f5..fa322f8612 100755 --- a/deploy/compose/run.sh +++ b/deploy/compose/run.sh @@ -7,6 +7,9 @@ cd "${SCRIPT_DIR}" COMPOSE_FILES=(-f compose.yml) if [[ "${BUZZ_COMPOSE_TLS:-false}" == "true" ]]; then COMPOSE_FILES+=(-f compose.caddy.yml) + if [[ "${BUZZ_COMPOSE_PAIRING:-true}" == "true" ]]; then + COMPOSE_FILES+=(-f compose.pair.yml) + fi fi if [[ "${BUZZ_COMPOSE_DEV:-false}" == "true" ]]; then COMPOSE_FILES+=(-f compose.dev.yml) @@ -121,8 +124,10 @@ Commands: roster event. Do not use parallel adds (e.g. xargs -P). Environment switches: - BUZZ_COMPOSE_TLS=true Include compose.caddy.yml for automatic HTTPS - BUZZ_COMPOSE_DEV=true Include compose.dev.yml for local admin ports/tools + BUZZ_COMPOSE_TLS=true Include compose.caddy.yml for automatic HTTPS + BUZZ_COMPOSE_DEV=true Include compose.dev.yml for local admin ports/tools + BUZZ_COMPOSE_PAIRING=false Disable the device-pairing sidecar + (compose.pair.yml, included by default with TLS) MSG ;; *)