diff --git a/nestsClient/specs/EGG-01.md b/nestsClient/specs/EGG-01.md index 1cb5b4f5e..5d46db9e2 100644 --- a/nestsClient/specs/EGG-01.md +++ b/nestsClient/specs/EGG-01.md @@ -89,6 +89,27 @@ publishers that emit it are non-conformant. share `(pubkey, d)` AND `created_at`, receivers MUST keep the event with the lexicographically SMALLEST `id` and discard the other (per NIP-01 replaceable-event tie-break). +12. **Publish-before-mint ordering.** A peer MUST NOT request a JWT + (EGG-02) for a room before that room's `kind:30312` event has + propagated to relays the auth sidecar can read. The auth sidecar + looks up the most-recent `kind:30312` by `(pubkey, kind, d)` to + validate the room exists and to enforce status-based gates + (EGG-08 rule 5). For hosts: this means publishing the freshly- + composed event BEFORE minting their own `publish: true` token. + For listeners: this means waiting until the event is visible on + the listener's own relay set (an unknown room cannot be joined). + Auth sidecars that cannot find the room MUST return HTTP 410 + `unknown_room` (EGG-02 error taxonomy); peers SHOULD retry with + 1 s / 2 s / 4 s exponential backoff before surfacing the error. +13. **Service / endpoint selection (host-side guidance).** When + composing a new room, hosts SHOULD pre-fill `service` and + `endpoint` from the FIRST entry of their own `kind:10112` user + server list (EGG-09). When the host has no `kind:10112`, the + client MAY ship a built-in default URL but MUST allow user + override. Both `service` and `endpoint` MUST be `https://` URLs; + `http://` MUST be rejected at compose time (mirrors EGG-09 + rule 1). The `service` and `endpoint` MAY be the same base URL + (a single deployment serving both is the common case). ## Example diff --git a/nestsClient/specs/EGG-07.md b/nestsClient/specs/EGG-07.md index e274fc8a4..3d8c853a3 100644 --- a/nestsClient/specs/EGG-07.md +++ b/nestsClient/specs/EGG-07.md @@ -51,6 +51,32 @@ the 60-second validity window defined below. ## Behavior +### Audio publish authorisation + +The auth sidecar (EGG-02) MUST grant a `publish: true` JWT — with the +caller's pubkey hex listed in the JWT's `put` claim — to any peer that +is one of: + +- the **host** (i.e. the author of the most-recent `kind:30312` event + for the requested `(kind, d)`), regardless of whether the host's own + `p`-tag carries a `"host"`, `"speaker"`, or no role marker. The host's + speaker capability is implicit in event authorship — clients MUST NOT + require the host to add themselves as `["p", _, _, "speaker"]` in + order to broadcast, +- a peer marked `["p", , _, "speaker"]` in that same event, +- a peer marked `["p", , _, "admin"]` in that same event. + +Other callers requesting `publish: true` MUST receive HTTP 403 +`publish_forbidden` per the EGG-02 error taxonomy. A `publish: false` +(listener) request MUST be accepted from any well-formed NIP-98 caller +when the room is `open` (per EGG-01 rule 6). + +The relay (EGG-03 endpoint) is independently authorised by the JWT's +`put` claim — it does NOT re-read the `kind:30312` event. Demoting a +speaker therefore does NOT terminate their existing audio session; +the speaker keeps publishing until either their JWT expires or the +host kicks them via EGG-07's kick command. + ### Promote / demote 1. To promote a listener to speaker, the host or an admin MUST re-publish diff --git a/nestsClient/specs/README.md b/nestsClient/specs/README.md index ad5e7d019..2bd8f56e2 100644 --- a/nestsClient/specs/README.md +++ b/nestsClient/specs/README.md @@ -154,6 +154,87 @@ get me to first audio frame". Every step references the EGG that defines it. Token lifetime is 600 s. Re-mint a fresh token (steps 3 + 4) before the old one expires. Relays MUST close sessions within 30 s past `exp`. +## Hosting a new room + +A normative walkthrough for "I am a host; create a fresh room and start +broadcasting". Symmetrical to the joining sequence above but starts one +step earlier — the host has to bring the `kind:30312` into existence +before anyone (including themselves) can authenticate against it. + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ 1. Pick service + endpoint (EGG-01, EGG-09) │ +│ Default: FIRST entry of host's own kind:10112 (EGG-09) │ +│ Both MUST be https://; http:// is rejected (EGG-09 rule 1) │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 2. Compose kind:30312 (EGG-01) │ +│ - status = "open" (or "planned" + starts tag, EGG-08) │ +│ - d = fresh room id, charset [A-Za-z0-9._-] (EGG-01 rule 9) │ +│ - first p-tag = ["p", , , "host"] │ +│ - relays / image / summary / theme tags as desired │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 3. Sign + publish to relays (EGG-01 rule 10) │ +│ Target set = `relays` tag ∪ host's NIP-65 outbox │ +│ The auth sidecar's inbound relay pool MUST overlap this set, │ +│ otherwise step 4 will 410 `unknown_room` indefinitely. │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 4. Mint publish JWT (EGG-02) │ +│ POST /auth, body = {namespace, "publish": true} │ +│ NIP-98 Authorization signed by host pubkey. │ +│ On 410 `unknown_room`: relay-propagation lag — retry up to 3 │ +│ times with backoff (1s / 2s / 4s) before surfacing. │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 5. Open WebTransport + run moq-lite Setup (EGG-02 step 3) │ +│ Same as listener flow. │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 6. Announce own broadcast (EGG-03) │ +│ Announce { suffix=, status=Active, hops=0 } │ +│ Then AnnouncePlease prefix="" to also receive other speakers. │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 7. Publish own presence (EGG-04) │ +│ publishing="1", onstage="1", muted="0"; heartbeat per EGG-04. │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ 8. Stream Opus (EGG-03) │ +│ One unidirectional QUIC stream per Group, one Opus packet per │ +│ Frame, 20 ms cadence. │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +Ongoing host duties: + +- **Add a speaker:** re-publish kind:30312 with the new `["p", _, _, + "speaker"]` tag (EGG-07). +- **Promote to admin / demote / remove:** re-publish kind:30312 with + updated role markers (EGG-07). +- **Kick:** sign and broadcast a kind:4312 ephemeral (EGG-07). +- **Edit metadata** (rename, image, theme): re-publish kind:30312 with + higher `created_at` (EGG-01 rule 1). +- **Close the room:** re-publish kind:30312 with `status="closed"` + (EGG-01 rule 6). The audio plane SHOULD be torn down server-side. +- **Publish recording** after close: re-publish closed event with + `["recording", url]` tag (EGG-11). + ## Filing changes Edit a single EGG per pull request. Include a wire-format example showing the