Summary
POST /events on the relay requires NIP-98 auth, but the requirements aren't documented anywhere I could find, and the in-repo e2e tests use an auth path that only works in dev — so copying the tests against a production relay fails with a 401 and no indication why.
Working this out took a while, and none of it is discoverable from the docs. Writing it down here in case it's useful, and I'm happy to send a docs PR if you'd like it (opening the issue first, per CONTRIBUTING).
The misleading part
The e2e tests authenticate with an X-Pubkey header. That header is a dev-mode fallback gated on require_auth_token (crates/buzz-relay/src/api/bridge.rs:117). Against a production relay it returns:
401 {"error":"missing Nostr auth"}
So the natural move — copy what the tests do — produces an error that says nothing about the header being dev-only, and there's no doc to contradict the tests.
What actually works
Authorization: Nostr <base64(json(kind:27235 event))>, where the event must satisfy (per crates/buzz-auth/src/nip98.rs):
kind = 27235
created_at within ±60s of the relay's clock
- a single-letter
u tag whose value matches the normalized request URL — trailing slash stripped. Not a multi-letter url tag.
- a
method tag matching the HTTP method
- a
payload tag = SHA-256 hex of the request body
The u-vs-url tag detail is the one that cost us the most time. NIP-98 in the wild is inconsistent about it, the failure is an opaque 401, and there's nothing to check your work against.
Suggested fix
A short section in the docs covering: which endpoints require NIP-98, the five requirements above, one worked example of a valid auth event, and an explicit note that X-Pubkey is dev-only and will not work against a normal deployment. Ideally also a comment in the e2e tests saying the same, since those are what people copy.
Happy to write it — say the word and I'll open a PR with DCO sign-off.
Possibly related
Environment
Self-hosted buzz-relay (ghcr.io/block/buzz:main, image built 2026-08-09), reached over HTTPS on 443 behind a reverse proxy.
Summary
POST /eventson the relay requires NIP-98 auth, but the requirements aren't documented anywhere I could find, and the in-repo e2e tests use an auth path that only works in dev — so copying the tests against a production relay fails with a 401 and no indication why.Working this out took a while, and none of it is discoverable from the docs. Writing it down here in case it's useful, and I'm happy to send a docs PR if you'd like it (opening the issue first, per CONTRIBUTING).
The misleading part
The e2e tests authenticate with an
X-Pubkeyheader. That header is a dev-mode fallback gated onrequire_auth_token(crates/buzz-relay/src/api/bridge.rs:117). Against a production relay it returns:So the natural move — copy what the tests do — produces an error that says nothing about the header being dev-only, and there's no doc to contradict the tests.
What actually works
Authorization: Nostr <base64(json(kind:27235 event))>, where the event must satisfy (percrates/buzz-auth/src/nip98.rs):kind= 27235created_atwithin ±60s of the relay's clockutag whose value matches the normalized request URL — trailing slash stripped. Not a multi-letterurltag.methodtag matching the HTTP methodpayloadtag = SHA-256 hex of the request bodyThe
u-vs-urltag detail is the one that cost us the most time. NIP-98 in the wild is inconsistent about it, the failure is an opaque 401, and there's nothing to check your work against.Suggested fix
A short section in the docs covering: which endpoints require NIP-98, the five requirements above, one worked example of a valid auth event, and an explicit note that
X-Pubkeyis dev-only and will not work against a normal deployment. Ideally also a comment in the e2e tests saying the same, since those are what people copy.Happy to write it — say the word and I'll open a PR with DCO sign-off.
Possibly related
POST /query(bare filter objects vs. the required array wrapper). Same class of problem on a neighbouring endpoint; might be worth fixing together.RELAY_URLthat drops the port, breaking self-hosted relays on non-443 ports. Same code path as theutag matching above.Environment
Self-hosted
buzz-relay(ghcr.io/block/buzz:main, image built 2026-08-09), reached over HTTPS on 443 behind a reverse proxy.