diff --git a/deploy/compose/.env.example b/deploy/compose/.env.example index f6ab4fcab9..bb2cb27805 100644 --- a/deploy/compose/.env.example +++ b/deploy/compose/.env.example @@ -8,6 +8,10 @@ BUZZ_IMAGE=ghcr.io/block/buzz:main # Public host name. Used by compose.caddy.yml and URL-derived settings below. BUZZ_DOMAIN=buzz.example.com RELAY_URL=wss://buzz.example.com +# Dedicated NIP-AB pairing URL advertised in NIP-11. With compose.caddy.yml this +# is same-host /pair (proxied to pairing-relay). Without Caddy, point at the +# published pairing port instead, e.g. ws://buzz.example.com:5000. +BUZZ_PAIRING_RELAY_URL=wss://buzz.example.com/pair BUZZ_MEDIA_BASE_URL=https://buzz.example.com/media BUZZ_MEDIA_SERVER_DOMAIN=buzz.example.com BUZZ_CORS_ORIGINS=https://buzz.example.com @@ -38,6 +42,8 @@ BUZZ_S3_ADDRESSING_STYLE=path # Optional host ports. Base compose publishes the relay directly on BUZZ_HTTP_PORT. BUZZ_HTTP_PORT=3000 +# Pairing sidecar host port (non-Caddy). Caddy TLS installs use /pair on 443 instead. +BUZZ_PAIR_RELAY_PORT=5000 # Caddy host ports. Only used with compose.caddy.yml. CADDY_HTTP_PORT=80 diff --git a/deploy/compose/Caddyfile b/deploy/compose/Caddyfile index 205cf4c5bc..cc3577d359 100644 --- a/deploy/compose/Caddyfile +++ b/deploy/compose/Caddyfile @@ -1,5 +1,11 @@ {$BUZZ_DOMAIN} { encode zstd gzip + # Device-pairing sidecar (buzz-pair-relay). Must be before the catch-all so + # NIP-AB QR sessions do not hit the main relay and 404 (#3842). + handle_path /pair* { + reverse_proxy pairing-relay:5000 + } + reverse_proxy relay:3000 } diff --git a/deploy/compose/README.md b/deploy/compose/README.md index bb0e63fe15..b80f353991 100644 --- a/deploy/compose/README.md +++ b/deploy/compose/README.md @@ -38,6 +38,13 @@ keypair. migrations. - The stack uses Postgres, Redis, MinIO, and a git data volume because those are real Buzz dependencies today. Minimal mode can simplify this later. +- Mobile QR pairing needs the `pairing-relay` sidecar. Compose stands it up on + port 5000 and advertises `BUZZ_PAIRING_RELAY_URL` in NIP-11. With + `BUZZ_COMPOSE_TLS=true`, Caddy proxies `/pair*` to that sidecar — set + `BUZZ_PAIRING_RELAY_URL=wss:///pair`. Without Caddy, point + `BUZZ_PAIRING_RELAY_URL` at the published pairing port instead + (e.g. `ws://:5000`). Skipping this leaves membership-enforcing relays + advertising a pairing path they cannot serve. - The bundled Compose stack fixes the relay endpoint to `http://minio:9000` and `BUZZ_S3_ADDRESSING_STYLE=path`: Docker DNS resolves `minio`, not `.minio`. It is not configurable for an external S3 provider through diff --git a/deploy/compose/compose.caddy.yml b/deploy/compose/compose.caddy.yml index c7dcbf106c..cf7c4f372a 100644 --- a/deploy/compose/compose.caddy.yml +++ b/deploy/compose/compose.caddy.yml @@ -7,6 +7,8 @@ services: depends_on: relay: condition: service_healthy + pairing-relay: + condition: service_healthy environment: BUZZ_DOMAIN: ${BUZZ_DOMAIN:?set BUZZ_DOMAIN} ports: diff --git a/deploy/compose/compose.yml b/deploy/compose/compose.yml index 15337c92a2..9173469459 100644 --- a/deploy/compose/compose.yml +++ b/deploy/compose/compose.yml @@ -20,6 +20,9 @@ services: BUZZ_GIT_REPO_PATH: /data/git BUZZ_AUTO_MIGRATE: ${BUZZ_AUTO_MIGRATE:-false} BUZZ_GIT_CONFORMANCE_PROBE: ${BUZZ_GIT_CONFORMANCE_PROBE:-true} + # Advertise the pairing sidecar in NIP-11 so clients don't fall back to a + # non-existent legacy /pair path on the main relay (see #3842). + BUZZ_PAIRING_RELAY_URL: ${BUZZ_PAIRING_RELAY_URL:?set BUZZ_PAIRING_RELAY_URL} ports: - "${BUZZ_HTTP_PORT:-3000}:3000" volumes: @@ -121,6 +124,33 @@ services: networks: - buzz-net + # Stateless NIP-AB pairing sidecar. Desktop/mobile QR pairing needs this when + # the main relay enforces membership (NIP-43) — without it, clients fall back + # to a legacy /pair path on the relay and get 404 (#3842). + # Use entrypoint (not command): the image ENTRYPOINT is buzz-relay, and + # Compose `command:` only appends args. + pairing-relay: + 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 + ports: + # Exposed for non-Caddy installs; TLS setups reach it via Caddy /pair*. + - "${BUZZ_PAIR_RELAY_PORT:-5000}:5000" + healthcheck: + test: + [ + "CMD-SHELL", + "bash -ec 'exec 3<>/dev/tcp/127.0.0.1:5000'", + ] + interval: 10s + timeout: 3s + retries: 6 + start_period: 5s + restart: unless-stopped + networks: + - buzz-net + volumes: buzz-postgres-data: labels: diff --git a/deploy/compose/run.sh b/deploy/compose/run.sh index d5465ea1f5..84613b20b6 100755 --- a/deploy/compose/run.sh +++ b/deploy/compose/run.sh @@ -122,7 +122,12 @@ Commands: Environment switches: BUZZ_COMPOSE_TLS=true Include compose.caddy.yml for automatic HTTPS + (also proxies /pair* to the pairing-relay sidecar) BUZZ_COMPOSE_DEV=true Include compose.dev.yml for local admin ports/tools + +Pairing: + Set BUZZ_PAIRING_RELAY_URL in .env (see .env.example). TLS installs use + wss:///pair; non-Caddy installs use the published pair port. MSG ;; *)