bd12d5ef72
Edits across all 13 EGGs to remove implementer guesswork. After this an
implementer can build a listener and speaker without reading our source.
README:
- Conventions section: hex casing rule (lowercase, 64 chars, no 0x),
NIP-01 foundations, `a`-tag form, created_at tie-break, JSON / time.
- Joining sequence: numbered end-to-end walkthrough from `kind:30312`
to first audio frame, referencing each EGG.
EGG-01 (room event):
- `relays` tag is one tag with multiple values (not multi-tag).
- `service` URL trailing-slash normalization.
- `private` status: gate is implementation-defined, not "render as open".
- `d`-tag charset locked to [A-Za-z0-9._-] so it interpolates safely
into the moq-auth namespace.
- Relay-discovery rule: publish to `relays` tag ∪ NIP-65 outbox.
- Tie-break on identical created_at via smallest event id.
EGG-02 (auth):
- JWT signing pinned: ES256 over P-256, JWKS at /.well-known/jwks.json,
5-minute relay cache.
- NIP-98 tags pinned: u / method / payload, base64 RFC 4648 standard
(not base64url).
- Error taxonomy: full HTTP status + `error` slug table for /auth, plus
WebTransport CONNECT 200/401/403/404 table.
EGG-03 (audio):
- Pin moq-lite Lite-03 to kixelated/moq-rs `rs/moq-lite/src/lite/`.
- One Opus packet per moq Frame (no container, no timestamp).
- Pubkey hex casing reaffirmed at the suffix / broadcast slots.
- AnnouncePlease prefix="" for speaker discovery.
- Mute = stop publishing (not silence frames, not Announce Ended).
- Mid-stream join: discard pre-skip per RFC 7845.
EGG-04 (presence):
- Heartbeat jitter ±5 s required (anti-thundering-herd).
- "0"/"1" are strings, not booleans.
- Single-room rule via replaceable-event semantics.
EGG-05 (chat): 8 KB suggested, 64 KB hard cap, 3 msg/s render rate.
EGG-06 (reactions): 30s window measured against created_at, drop-on-arrival
if already stale.
EGG-07 (moderation): replay protection — dedupe kicks by id within 120 s.
EGG-08 (scheduling): planned rooms MUST 403 at the auth sidecar.
EGG-10 (theming): bg image caps (1 MB / 4096 px soft, 8 MB / 8192 px hard).
Deferred (per review): EGG-00 (Conventions as a standalone spec), EGG-13
(capability advertisement), EGG-14 (discovery), test vectors corpus.
The "Conventions" section in README covers EGG-00's most urgent content
inline.
https://claude.ai/code/session_01RDpuki4t8StSg1CZcXnV5b
3.3 KiB
3.3 KiB
EGG-08: Scheduling
status: draft
requires: EGG-01
category: optional
Summary
A host can announce a room before it opens by publishing a kind:30312
with status="planned" and a ["starts", <unix-seconds>] tag. Listing
clients render these as upcoming-room cards; the audio plane stays cold
until the host transitions the room to open.
Wire format
{
"kind": 30312,
"pubkey": "<host pubkey hex>",
"tags": [
["d", "<room d>"],
["room", "<room name>"],
["status", "planned"],
["starts", "<unix seconds>"],
["service", "<...>"],
["endpoint","<...>"],
["p", "<host pubkey>", "<relay hint>", "host"]
// ...other EGG-01 tags
],
"content": "",
...
}
The starts value is a base-10 ASCII unix timestamp in seconds. Clients
that need finer-than-second precision are out of scope; round to the
nearest second.
Behavior
- A planned room MUST carry
status="planned"AND astartstag with a non-empty numeric value. A planned room without astartsvalue MUST be treated as un-renderable (rooms with unknown start times pollute the listing UI). - The
startsvalue SHOULD be in the future at the time the event is published. Receivers MUST tolerate past values: a planned room whosestartsis in the past renders as "starts soon" rather than surfacing the past time. - To start a planned room, the host MUST re-publish the same
(kind, d)withstatus="open"and a highercreated_at. The host MAY drop thestartstag at this point (no longer informative). - To cancel a planned room, the host MUST re-publish the same
(kind, d)withstatus="closed". Thestartstag SHOULD be preserved so receivers can label the cancellation ("Cancelled — was scheduled for ..."). - Planned rooms MUST NOT mint moq-auth tokens (EGG-02). The auth sidecar
MUST reject
POST /authrequests targeting a(host, room d)whose most-recentkind:30312isstatus="planned", returning HTTP 403 with{"error":"room_closed"}per the EGG-02 error taxonomy. (The auth path becomes available the moment the host re-publishes withstatus="open"; clients SHOULD NOT pre-mint tokens againststarts.) - Listing UIs SHOULD sort planned rooms ahead of live ones until 5
minutes before
starts, then promote the room to the same surface as live rooms (the host is unlikely to be more than 5 minutes late). - A peer MUST NOT use
["expiration", <ts>](NIP-40) on a plannedkind:30312. The implicit 8-hour staleness window from EGG-01 is the only auto-close mechanism.
Example
{
"kind": 30312,
"pubkey": "abc...host",
"created_at": 1714000000,
"tags": [
["d", "monthly-q-and-a"],
["room", "Monthly Q&A"],
["summary", "Bring your questions"],
["status", "planned"],
["starts", "1714172400"],
["service", "https://moq.nostrnests.com"],
["endpoint", "https://moq.nostrnests.com"],
["p", "abc...host", "wss://relay.example", "host"]
],
"content": "",
"id": "...",
"sig": "..."
}
Compatibility
Receivers without EGG-08 see a planned room as status="planned" and
SHOULD treat it as un-joinable per EGG-01 rule 6 (status-not-open
means non-renderable for live UIs). The room becomes joinable as soon
as the host re-publishes with status="open".