From 551b629e9fde307ccac7b73768a7af8df97e5171 Mon Sep 17 00:00:00 2001 From: Jeff Litt Date: Sat, 25 Jul 2026 18:47:10 -0400 Subject: [PATCH] docs(compose): clarify BUZZ_CORS_ORIGINS must match client origin, not BUZZ_DOMAIN Self-hosting operators intuitively set BUZZ_CORS_ORIGINS to their relay's own domain (matching the pattern of every other BUZZ_DOMAIN-derived variable in this file). That breaks the desktop/mobile client's community-connect flow with a generic browser "Load failed" error, because the relay actually responds 200 to GET /api/join-policy but the browser's CORS check rejects it since the client's real Origin (tauri://localhost in production, or http://localhost: in a dev build) never matches BUZZ_DOMAIN. Hit this during a fresh self-hosted install: relay health, NIP-11, the raw WebSocket handshake, and the join-policy endpoint all worked fine via curl, but the desktop app failed silently until building from source with devtools enabled surfaced the actual CORS console error. Left BUZZ_CORS_ORIGINS empty (the relay's own supported "permissive" fallback) as the fix, which is safe once BUZZ_REQUIRE_AUTH_TOKEN / BUZZ_REQUIRE_RELAY_MEMBERSHIP already gate real data access. Docs-only change: expands the existing .env.example comment and adds a troubleshooting line to deploy/compose/README.md's Production notes. Signed-off-by: Jeff Litt --- deploy/compose/.env.example | 12 ++++++++++++ deploy/compose/README.md | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/deploy/compose/.env.example b/deploy/compose/.env.example index cebe879da0..0c18761812 100644 --- a/deploy/compose/.env.example +++ b/deploy/compose/.env.example @@ -10,6 +10,18 @@ BUZZ_DOMAIN=buzz.example.com RELAY_URL=wss://buzz.example.com BUZZ_MEDIA_BASE_URL=https://buzz.example.com/media BUZZ_MEDIA_SERVER_DOMAIN=buzz.example.com + +# Comma-separated origins allowed to make cross-origin HTTP requests (e.g. the +# desktop/mobile client's community-connect flow calling GET /api/join-policy). +# IMPORTANT: this must match the *client's* origin, not this relay's own +# BUZZ_DOMAIN. Setting it to your relay's own domain — the intuitive-looking +# default below — makes the browser reject the response with a generic +# "Load failed" error even though the relay itself answers 200, because the +# client's Origin header (typically `tauri://localhost` for the production +# desktop app, or `http://localhost:` for a dev build) never matches. +# Leave empty for permissive CORS (all origins), which the relay treats as +# "dev mode" and is safe once BUZZ_REQUIRE_AUTH_TOKEN / BUZZ_REQUIRE_RELAY_MEMBERSHIP +# are already gating real data access. BUZZ_CORS_ORIGINS=https://buzz.example.com # Production defaults. Closed relay mode requires RELAY_OWNER_PUBKEY and a stable relay key. diff --git a/deploy/compose/README.md b/deploy/compose/README.md index 0de524fb5b..61b511d1ab 100644 --- a/deploy/compose/README.md +++ b/deploy/compose/README.md @@ -38,6 +38,12 @@ 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. +- `BUZZ_CORS_ORIGINS` must match the *client's* origin, not `BUZZ_DOMAIN`. If + the desktop/mobile app's "join a community" flow fails with a generic + browser "Load failed" while `curl`ing the relay directly works fine, this is + almost always why — see the comment above `BUZZ_CORS_ORIGINS` in + `.env.example`. Leaving it empty (permissive CORS) is the simplest fix for + a single-operator or closed-relay deployment. Run `./run.sh backup-hint` for the backup checklist.