block / block/buzz

NIP-98 auth rejects valid RELAY_URL when it includes a non-default port (self-hosted, WSS on non-443)

Open
#6,441 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

Body
Summary

When RELAY_URL is configured with a non-default port (e.g. wss://example.com:3443), the relay correctly derives and registers the deployment community using the full authority including the port (via relay_url_authority / normalize_host in crates/buzz-relay/src/main.rs). However, NIP-98 HTTP Auth verification for the /query bridge endpoint appears to validate the request URL against a different normalization of RELAY_URL that drops the port and/or forces a fixed scheme, causing every authenticated request to fail with a URL mismatch — even though the client is using the exact RELAY_URL the deployment was configured with.

This makes it impossible to self-host a Buzz relay behind a reverse proxy on any port other than the implicit default for the scheme (443 for wss/https, 80 for ws/http).

Environment
Relay: self-hosted, ghcr.io/block/buzz:main, deployed via deploy/compose/compose.yml
Reverse proxy: Nginx, terminating TLS (Let's Encrypt), forwarding WebSocket upgrade to the relay container
Desktop: v0.5.2
RELAY_URL=wss://example.com:3443 (a port other than 443, chosen because port 443 was already in use by another site sharing the same domain)
Reproduction
Deploy relay with RELAY_URL=wss://your-domain.com:3443 (any non-default port for the scheme).
Confirm the deployment community is registered with the port included:
INFO Deployment community ensured host="your-domain.com:3443" community=""
Point Buzz Desktop's community Relay URL to wss://your-domain.com:3443, matching exactly.
Desktop connects (WebSocket upgrade succeeds, 101 Switching Protocols), then attempts NIP-42 auth / NIP-98 bridge requests.
Observe:
relay returned 401 Unauthorized: NIP-98: NIP-98 HTTP Auth verification failed:
URL mismatch: event has `https://your-domain.com:3443/query`,
expected `https://your-domain.com/query`

The "expected" URL in the error has the port stripped, despite RELAY_URL (used successfully for community host derivation) including it.

Expected behavior

NIP-98 URL validation should use the same host/authority normalization as community derivation (relay_url_authority / normalize_host), so that a RELAY_URL with an explicit non-default port is honored consistently across both code paths.

Suspected cause

Two independent normalization paths for the same RELAY_URL config value:

Community/tenant resolution (crates/buzz-relay/src/tenant.rs, relay_url_authority) — preserves the port.
NIP-98 HTTP Auth verification (bridge /query endpoint) — appears to reconstruct the "expected" URL using a default scheme/port instead of the configured RELAY_URL authority verbatim.
Additional context

This was discovered while trying to run mobile pairing (/pair, NIP-AB) over a dedicated HTTPS port on a self-hosted relay that shares its domain with another web application on port 443. Related regression also observed independently: [#3514](https://github.com/block/buzz/issues/3514) (Mobile 0.4.12 does not decode Desktop 0.5.0 styled pairing QR) — not the same bug, but encountered in the same troubleshooting session and may be worth cross-referencing if investigating mobile pairing reliability on self-hosted deployments generally.

Happy to provide relay logs, full Nginx config, or test further changes if useful for debugging.

Contributor guide

Open the contributing guide

Research direction

Start with crates/buzz-relay/src/main.rs and crates/buzz-relay/src/tenant.rs, especially relay_url_authority and normalize_host, then locate the /query bridge's NIP-98 URL verification. Reproduce with RELAY_URL=wss://your-domain.com:3443 and compare the expected URL with the request URL; done means authenticated requests no longer lose the explicit port and community derivation remains consistent.

Written by the indexing model from the issue text.

Assessment

Tech stack
nginx, rust
Domain
authentication, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.