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
17 changes: 16 additions & 1 deletion deploy/compose/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,28 @@ BUZZ_S3_ACCESS_KEY=CHANGE_ME_RANDOM_ACCESS_KEY
BUZZ_S3_SECRET_KEY=CHANGE_ME_RANDOM_SECRET_KEY
BUZZ_S3_BUCKET=buzz-media

# Optional host ports. Base compose publishes the relay directly on BUZZ_HTTP_PORT.
# Optional host ports. Base compose publishes the relay directly on
# BUZZ_HTTP_PORT and the pairing sidecar on BUZZ_PAIR_RELAY_PORT;
# compose.caddy.yml unpublishes both and fronts them with Caddy.
BUZZ_HTTP_PORT=3000
BUZZ_PAIR_RELAY_PORT=5000

# Caddy host ports. Only used with compose.caddy.yml.
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: the pair service is already published on
# BUZZ_PAIR_RELAY_PORT, so just 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
}
}
31 changes: 31 additions & 0 deletions deploy/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,37 @@ 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. The
overlay unpublishes the sidecar's host port; Caddy is the only way in.
- **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 sidecar in your proxy — the base stack publishes it on
`BUZZ_PAIR_RELAY_PORT` (default 5000). If your proxy runs *outside* this
compose project and you would rather not publish the port, attach the proxy
to the project's `buzz-net` network and target `pair:5000` directly. The
relay then advertises the URL in NIP-11 and the desktop skips the `/pair`
fallback.
- **Non-TLS / direct (no Caddy).** The base stack publishes the sidecar on
`BUZZ_PAIR_RELAY_PORT` (default 5000); set
`BUZZ_PAIRING_RELAY_URL=ws://<host>:5000` so the QR points at it. Without
that, 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
6 changes: 6 additions & 0 deletions deploy/compose/compose.caddy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ services:
relay:
ports: !reset []

# Caddy routes /pair to the sidecar, so its direct host port goes away too.
pair:
ports: !reset []

caddy:
image: caddy:2-alpine
depends_on:
relay:
condition: service_healthy
pair:
condition: service_healthy
environment:
BUZZ_DOMAIN: ${BUZZ_DOMAIN:?set BUZZ_DOMAIN}
ports:
Expand Down
25 changes: 25 additions & 0 deletions deploy/compose/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,31 @@ 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. Published directly on
# BUZZ_PAIR_RELAY_PORT, mirroring the relay's BUZZ_HTTP_PORT; compose.caddy.yml
# unpublishes it and routes /pair through Caddy instead (see README.md
# § Device pairing).
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
ports:
- "${BUZZ_PAIR_RELAY_PORT:-5000}: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