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-03: Audio plane (moq-lite)
|
|
|
|
`status: draft`
|
|
`requires: EGG-02`
|
|
`category: required`
|
|
|
|
## Summary
|
|
|
|
Audio is carried over [moq-lite](https://github.com/kixelated/moq) 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 `Frame`s 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 `Frame`s 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
|
|
|
|
1. A speaker MUST emit an `Announce` with `status=Active` immediately after
|
|
the WebTransport session reaches a usable state, and emit `status=Ended`
|
|
when stopping the broadcast cleanly.
|
|
2. A speaker MUST cap their broadcast to one concurrent `audio/data` track
|
|
per session. Multi-quality stacks are reserved for a future EGG.
|
|
3. Listeners MUST tolerate gaps in the group sequence (frames dropped due to
|
|
network loss). They MUST NOT request retransmits.
|
|
4. Listeners SHOULD subscribe with `ordered=true`. Out-of-order delivery
|
|
would re-introduce reorderable jitter the Opus decoder is not equipped to
|
|
handle.
|
|
5. A speaker MUST encode in mono. Stereo broadcasts are reserved for a future
|
|
EGG.
|
|
6. Listeners MUST NOT subscribe to their own broadcast (would create an audio
|
|
loopback through the relay).
|
|
7. The relay MUST drop a publishing peer's `Announce` and any subsequent
|
|
stream data if the JWT's `put` claim 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`.
|