Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions deploy/compose/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ BUZZ_HTTP_PORT=3000
CADDY_HTTP_PORT=80
CADDY_HTTPS_PORT=443

# Device pairing (mobile QR). The stack always runs the buzz-pair-relay sidecar.
# - TLS mode (compose.caddy.yml): Caddy routes /pair on your main domain to the
# sidecar, so the desktop's NIP-43 legacy fallback works with no config here.
# - Split-domain or your own proxy: advertise a dedicated URL so the desktop
# uses it directly instead of the /pair fallback. Must be ws:// or wss://.
# BUZZ_PAIRING_RELAY_URL=wss://pair.buzz.example.com
# - Non-TLS / direct: publish the pair service's port 5000 and point clients at
# it, e.g. BUZZ_PAIRING_RELAY_URL=ws://buzz.example.com:5000 — otherwise use
# TLS mode. See README.md § Device pairing.
# BUZZ_PAIRING_RELAY_URL=

# Dev override ports. Only used with compose.dev.yml.
POSTGRES_PORT=5432
REDIS_PORT=6379
Expand Down
12 changes: 11 additions & 1 deletion deploy/compose/Caddyfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{$BUZZ_DOMAIN} {
encode zstd gzip

reverse_proxy relay:3000
# Device-pairing sidecar. The desktop's NIP-43 legacy fallback resolves to
# wss://{$BUZZ_DOMAIN}/pair, so routing that path to buzz-pair-relay makes
# mobile pairing work with no extra DNS. The sidecar ignores the request path.
@pair path /pair /pair/*
handle @pair {
reverse_proxy pair:5000
}

handle {
reverse_proxy relay:3000
}
}
26 changes: 26 additions & 0 deletions deploy/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,32 @@ keypair.

Run `./run.sh backup-hint` for the backup checklist.

## Device pairing

Mobile pairing needs a dedicated pairing relay (`buzz-pair-relay`). The desktop
generates the QR, then resolves where the phone should connect: it first reads
the main relay's NIP-11 `pairing_relay_url`, and if that is unset it falls back
to the legacy `/pair` path on the main relay. The base relay does **not** serve
`/pair`, so the stack runs a `pair` sidecar (bundled in the same relay image)
and the deployment just has to route traffic to it. Three cases:

- **TLS (`compose.caddy.yml`) — works out of the box.** Caddy routes
`/pair` on your main domain to the sidecar, so the desktop's legacy fallback
(`wss://<domain>/pair`) Just Works with no extra DNS and nothing to set.
- **Split domain or your own reverse proxy.** Expose the sidecar at its own
host name and advertise it so the desktop uses it directly:
set `BUZZ_PAIRING_RELAY_URL=wss://pair.<domain>` in `.env` and point that
name at the `pair` service (port 5000) in your proxy. The relay then
advertises it in NIP-11 and the desktop skips the `/pair` fallback.
- **Non-TLS / direct (no Caddy).** The base stack keeps the sidecar on the
internal network only. Publish its port (add a `5000:5000` mapping to the
`pair` service) and set `BUZZ_PAIRING_RELAY_URL=ws://<host>:5000`, or run TLS
mode. Without one of these, the desktop QR points at a `/pair` endpoint the
relay 404s and pairing fails.

`BUZZ_PAIRING_RELAY_URL` must be a `ws://` or `wss://` URL; the relay rejects
anything else at startup.

## Validation

Before sharing an install link publicly, verify a fresh install with:
Expand Down
2 changes: 2 additions & 0 deletions deploy/compose/compose.caddy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ services:
depends_on:
relay:
condition: service_healthy
pair:
condition: service_healthy
environment:
BUZZ_DOMAIN: ${BUZZ_DOMAIN:?set BUZZ_DOMAIN}
ports:
Expand Down
23 changes: 23 additions & 0 deletions deploy/compose/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,29 @@ services:
networks:
- buzz-net

# Ephemeral NIP-AB device-pairing sidecar. The relay image already ships the
# binary; without this service the desktop QR resolves to a /pair endpoint the
# relay itself does not serve and mobile pairing fails. The sidecar binds only
# inside the compose network — a reverse proxy terminates TLS and routes /pair
# here (see compose.caddy.yml). To expose it directly, publish port 5000 and
# set BUZZ_PAIRING_RELAY_URL (see .env.example and README.md).
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
# The sidecar speaks only WebSocket upgrades and answers a plain GET with 400,
# so probe the raw TCP port over /dev/tcp rather than expecting an HTTP 200.
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

postgres:
image: postgres:17-alpine
environment:
Expand Down