Wire-protocol specs for nostrnests-style audio rooms, in the style of Nostr NIPs and Blossom BUDs. Each spec documents one self-contained capability that a client or relay can implement; two compliant peers implementing the same set of EGGs round-trip without further coordination. Layout: README.md cover, status table, conformance levels EGG-01.md Room event (kind:30312) required EGG-02.md Auth + WebTransport handshake required EGG-03.md Audio plane (moq-lite) required EGG-04.md Presence (kind:10312) required EGG-05.md In-room chat (kind:1311) optional EGG-06.md Reactions (kind:7) optional EGG-07.md Roles & moderation (kind:4312) optional EGG-08.md Scheduling (status=planned) optional EGG-09.md User server list (kind:10112) optional EGG-10.md Theming (c/f/bg tags) decorative EGG-11.md Recording decorative EGG-12.md Catalog track (catalog.json) optional Conformance levels (Listener / Speaker / Host) defined in the README so a deployment can declare "we implement EGG-01..EGG-04" and other peers know exactly what to expect. Each spec follows the same shape (Summary / Wire format / Behavior numbered MUST/SHOULD/MAY rules / Example / Compatibility) and fits on a single printed page. Wire formats are documented exactly as nostrnests + amethyst implement them on this branch — no hypothetical capabilities, no "future" tags without an EGG number.
3.6 KiB
EGG-02: Auth & WebTransport handshake
status: draft
requires: EGG-01, NIP-98
category: required
Summary
To open the audio plane (EGG-03), a peer first proves identity to a per-room auth sidecar (moq-auth) using a NIP-98 HTTP signature, receives a short-lived JWT, then opens a WebTransport session against the moq-relay carrying the JWT in the URL.
Two separate URLs are involved: the service tag from EGG-01 is the auth
sidecar; the endpoint tag is the relay.
Wire format
Step 1 — token request
POST <service>/auth
Authorization: Nostr <base64(NIP-98 kind:27235 event)>
Content-Type: application/json
{ "namespace": "nests/<kind>:<host_pubkey_hex>:<room_d>",
"publish": <boolean> }
<kind> is 30312 for nests audio rooms (EGG-01).
The NIP-98 event MUST bind to this exact URL, method POST, and the SHA-256
hash of the request body.
Step 2 — token response
HTTP/1.1 200 OK
Content-Type: application/json
{ "token": "<jwt>" }
The JWT carries (at minimum):
| claim | meaning |
|---|---|
root |
The exact namespace echoed back. Authorisation is scoped here. |
get |
Read-allowed sub-paths; for nests this is [""] (any). |
put |
Publish-allowed sub-paths. For listeners: []. For speakers: [<own pubkey hex>]. |
iat |
Unix seconds; issuance. |
exp |
Unix seconds; expiry. MUST be iat + 600 for nests today. |
Step 3 — WebTransport CONNECT
:method = CONNECT
:protocol = webtransport
:scheme = https
:authority = <host:port from `endpoint`>
:path = /<namespace>?jwt=<token>
The relay reads the JWT from the ?jwt= query string. No Authorization
header is used at this step.
Behavior
- Listening peers MUST request
publish: false. Speaking peers MUST requestpublish: trueAND the JWT'sputclaim MUST list the speaker's own pubkey hex. - The auth sidecar's TLS certificate MUST be a publicly-trusted chain. The relay's TLS chain MAY be self-signed in development; production deployments MUST use a public chain.
- The auth sidecar SHOULD reject any request whose NIP-98
created_atis more than 60 s in the past or future. - The JWT lifetime is fixed at 600 s. There is no refresh endpoint. A client
that needs a longer session MUST mint a fresh token and open a new
WebTransport session before the old token expires; see the deployment-side
commentary in
nestsClient/plans/. - The relay MUST close the WebTransport session within 30 s of
exp. A peer receiving an unexpected close MUST be prepared to mint a fresh token and reconnect. - The relay MUST NOT trust the WebTransport authority for authorisation —
only the JWT's
rootclaim. A token issued for namespace A MUST NOT be accepted on a session opened against namespace B. - A peer MUST NOT log the JWT or include it in error reports. The token is a bearer credential.
Example
> POST https://moq.nostrnests.com/auth
> Authorization: Nostr eyJ...kind27235...
> Content-Type: application/json
>
> {"namespace":"nests/30312:abchost:office-hours-2026-04","publish":false}
< HTTP/1.1 200 OK
< Content-Type: application/json
<
< {"token":"eyJhbGc..."}
> CONNECT :path=/nests/30312:abchost:office-hours-2026-04?jwt=eyJhbGc...
< 200 OK (WebTransport session established)
Compatibility
EGG-03 (audio plane) operates inside the WebTransport session this EGG opens. EGG-12 (catalog) shares the same session.