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-03: Audio plane (moq-lite)
status: draft
requires: EGG-02
category: required
Summary
Audio is carried over moq-lite Lite-03 on
top of the WebTransport session opened in EGG-02. Each speaker publishes one
broadcast keyed by their pubkey hex, on a single track named audio/data,
encoded as Opus.
This EGG defines the broadcast/track naming convention and the audio codec
parameters. It does not redefine moq-lite framing — refer to the upstream
spec for the wire layout of Subscribe, Announce, Group, and Frame
control messages.
Wire format
Speaker → relay (publish)
A speaker MUST send a moq-lite Announce with:
prefix = "" (empty — relative to the JWT's `root` namespace)
suffix = <pubkey hex>
status = Active
After the announce, the speaker opens a unidirectional QUIC stream per group,
prefixed with a moq-lite Group header followed by Frames carrying Opus
payloads.
Listener → relay (subscribe)
A listener MUST send a moq-lite Subscribe with:
broadcast = <speaker pubkey hex>
track = "audio/data"
priority = 128 (recommended)
ordered = true
maxLatency = 0 (unlimited)
The relay forwards each subsequent Group and its Frames to the listener
in subscribe-id order.
Codec
| Parameter | Value |
|---|---|
| Codec | Opus (RFC 6716) |
| Sample rate | 48 kHz |
| Channels | 1 (mono) |
| Frame duration | 20 ms (960 samples) |
| Bit-rate target | 32 kbit/s (configurable, no upper cap) |
| VBR | Allowed |
A new Group MUST be opened on every Opus reset (e.g. mute/unmute).
Behavior
- A speaker MUST emit an
Announcewithstatus=Activeimmediately after the WebTransport session reaches a usable state, and emitstatus=Endedwhen stopping the broadcast cleanly. - A speaker MUST cap their broadcast to one concurrent
audio/datatrack per session. Multi-quality stacks are reserved for a future EGG. - Listeners MUST tolerate gaps in the group sequence (frames dropped due to network loss). They MUST NOT request retransmits.
- Listeners SHOULD subscribe with
ordered=true. Out-of-order delivery would re-introduce reorderable jitter the Opus decoder is not equipped to handle. - A speaker MUST encode in mono. Stereo broadcasts are reserved for a future EGG.
- Listeners MUST NOT subscribe to their own broadcast (would create an audio loopback through the relay).
- The relay MUST drop a publishing peer's
Announceand any subsequent stream data if the JWT'sputclaim does not contain<pubkey hex>.
Example
A two-speaker room:
A's session: ANNOUNCE prefix="" suffix="speakerA" status=Active
GROUP subscribe-id=…, sequence=0
FRAME <opus payload, 20ms>
FRAME <opus payload, 20ms>
…
Listener's session:
SUBSCRIBE broadcast="speakerA" track="audio/data"
← GROUP sequence=0
← FRAME <opus payload, 20ms>
← …
SUBSCRIBE broadcast="speakerB" track="audio/data"
← GROUP sequence=0
← FRAME <opus payload, 20ms>
← …
Compatibility
EGG-12 adds an OPTIONAL catalog.json track per broadcast carrying codec
metadata. EGG-12 MUST NOT change the audio/data track parameters defined
here.
A future "video plane" EGG MAY introduce additional tracks; their names MUST
NOT collide with audio/data or catalog.json.