Skip to content

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

Description

@Hus10sain

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 (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.

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