Skip to content

Windows desktop cannot join a self-hosted relay: http://tauri.localhost missing from CORS guidance, fails with a silent "Failed to fetch" #3490

Description

@Salty1960

Windows desktop cannot join a self-hosted relay: http://tauri.localhost missing from CORS guidance, fails with a silent "Failed to fetch"

Summary

On Windows, Buzz Desktop's WebView2 origin is http://tauri.localhost. On macOS/Linux it is tauri://localhost. A self-hosted relay whose BUZZ_CORS_ORIGINS was built from the shipped .env.example (which lists no Tauri origin at all) or from a macOS-based walkthrough will silently reject every request the Windows desktop makes during "Join a community".

The only user-visible symptom is a faint "Failed to fetch" on the join screen. Nothing is written to the relay log — the request never gets past the browser's CORS check — and nothing is persisted client-side, so the app loops back to onboarding with no diagnostic. It looks identical to a network or auth failure, which sends self-hosters down the wrong path (membership gates, auth tokens, firewall, ws:// vs wss://) for hours.

This is likely a contributing factor in several open self-hosting reports, e.g. #3088, #2816, #2662.

Environment

  • Relay: ghcr.io/block/buzz:main (image 75b532832ca1), deploy/compose bundle, plain HTTP on :3000 over Tailscale (no TLS proxy)
  • Desktop: Buzz Desktop 0.4.26, Windows 11
  • Relay config: BUZZ_REQUIRE_RELAY_MEMBERSHIP=false, BUZZ_REQUIRE_AUTH_TOKEN=false (fully open relay)

Reproduction

  1. Deploy via deploy/compose with BUZZ_CORS_ORIGINS=http://<host>:3000,tauri://localhost,https://tauri.localhost — i.e. the two origins a macOS/Linux guide would give you.
  2. On Windows, open Buzz Desktop → "Join a community" → enter ws://<host>:3000.
  3. Join fails with "Failed to fetch". No community is persisted.

Evidence

Preflight against the relay, same host, differing only in Origin:

$ curl -s -i -X OPTIONS -H "Origin: tauri://localhost" \
    -H "Access-Control-Request-Method: GET" http://<host>:3000/health
HTTP/1.1 200 OK
access-control-allow-origin: tauri://localhost      <-- allowed

$ curl -s -i -X OPTIONS -H "Origin: http://tauri.localhost" \
    -H "Access-Control-Request-Method: GET" http://<host>:3000/health
HTTP/1.1 200 OK
                                                    <-- no ACAO header, blocked

That the Windows origin really is http://tauri.localhost is confirmed by the desktop's own storage — every key in
%LOCALAPPDATA%\xyz.block.buzz.app\EBWebView\Default\Local Storage\leveldb is namespaced under _http://tauri.localhost:

_http://tauri.localhost  buzz-theme-cache  {...}
_http://tauri.localhost  buzz.machine-onboarding.identity-key-help-seen.v1  true
_http://tauri.localhost  buzz-machine-onboarding-complete.v2:<pubkey>  true

Note there is no buzz-communities key: the join never persists anything, so the app cannot retry or show the community in a broken state.

Relay-side, a machine in this condition produces zero entries in docker compose logs relay — the connection is never attempted, which strongly misleads diagnosis toward network/firewall causes.

The codebase already knows both origins exist — desktop/src-tauri/src/media_proxy.rs:38 checks for both:

if !origin.is_empty() && origin != "tauri://localhost" && origin != "http://tauri.localhost" {

but the relay-side guidance does not:

  • deploy/compose/.env.exampleBUZZ_CORS_ORIGINS=https://buzz.example.com, no Tauri origin at all
  • crates/buzz-relay/src/config.rs:88 — doc example is "tauri://localhost,http://localhost:3000", omitting http://tauri.localhost

So any operator following the shipped example produces a config that works on macOS/Linux and is broken on Windows.

Workaround

Add the Windows WebView2 origin to the relay allowlist and restart:

# deploy/compose/.env
BUZZ_CORS_ORIGINS=http://<host>:3000,tauri://localhost,https://tauri.localhost,http://tauri.localhost

$ ./run.sh restart

Verify:

$ curl -s -i -X OPTIONS -H "Origin: http://tauri.localhost" \
    -H "Access-Control-Request-Method: GET" http://<host>:3000/health | grep -i allow-origin
access-control-allow-origin: http://tauri.localhost

The Windows desktop joins immediately afterwards.

Suggested fix

  1. deploy/compose/.env.example: ship all desktop origins by default —
    BUZZ_CORS_ORIGINS=https://buzz.example.com,tauri://localhost,http://tauri.localhost,https://tauri.localhost
  2. crates/buzz-relay/src/config.rs:88: include http://tauri.localhost in the doc example.
  3. Consider always allowing the two fixed Tauri desktop origins regardless of BUZZ_CORS_ORIGINS — they are constants of the first-party client, not deployment-specific, and every self-hoster needs them.
  4. Surface the failure: the join screen should distinguish a CORS/preflight rejection from a network error. "Failed to fetch" with no host, status, or hint is the single biggest time sink here — even echoing the attempted URL and the browser error would help.
  5. Document a self-hosting/LAN page covering the required env vars (relates to Self-hosting onboarding is frustrating #3088).

Happy to send a PR for 1 and 2 if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions