5c40e27fde
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.
112 lines
3.6 KiB
Markdown
112 lines
3.6 KiB
Markdown
# 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
|
|
|
|
1. Listening peers MUST request `publish: false`. Speaking peers MUST request
|
|
`publish: true` AND the JWT's `put` claim MUST list the speaker's own
|
|
pubkey hex.
|
|
2. 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.
|
|
3. The auth sidecar SHOULD reject any request whose NIP-98 `created_at` is
|
|
more than 60 s in the past or future.
|
|
4. 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/`.
|
|
5. 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.
|
|
6. The relay MUST NOT trust the WebTransport authority for authorisation —
|
|
only the JWT's `root` claim. A token issued for namespace A MUST NOT be
|
|
accepted on a session opened against namespace B.
|
|
7. 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.
|