Skip to content

POST /events NIP-98 requirements are undocumented, and the in-repo e2e tests use a dev-only auth path that fails against production #6499

Description

@WifiDan

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.

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