docs(audio-rooms): moq-lite listener landed — refresh status callouts

Phase 5d wrapped, so the doc set now reflects "listener path done,
speaker pending":

  - nestsClient/plans/2026-04-26-moq-lite-gap.md — new "Implementation
    status" section maps phases 5a → 5d to commits, calls out the
    speaker side as blocked on a small `WebTransportSession.acceptBidi`
    extension (since publisher.rs:40 uses Stream::accept), and points
    at the existing :quic primitive (QuicConnection.awaitIncomingPeerStream)
    that the bridge can lean on.
  - nestsClient/plans/2026-04-26-audio-rooms-completion.md — Phase M1
    is no longer "on hold for moq-lite"; manual nostrnests.com
    validation should now work end-to-end on the listener path.
  - .claude/CLAUDE.md — :nestsClient now hosts both IETF MoQ-transport
    and moq-lite Lite-03; production listener path uses moq-lite.
This commit is contained in:
Claude
2026-04-26 17:39:23 +00:00
parent 41f4dcd9ac
commit 5914e9e9fc
3 changed files with 56 additions and 16 deletions
+4 -3
View File
@@ -13,9 +13,10 @@ a non-interactive JVM command-line client that drives the same `quartz` + `commo
humans, agents, and interop tests. `quic` is a from-scratch pure-Kotlin QUIC v1 + HTTP/3 +
WebTransport client (no JNI, no BouncyCastle), built because no Android-compatible Java QUIC library
exists. `nestsClient` runs the audio-room protocol on top of `:quic` for the NIP-53
audio-rooms feature. It currently implements IETF `draft-ietf-moq-transport-17`; the
real nostrnests relay speaks **moq-lite** (kixelated's wire-incompatible variant),
so end-to-end interop is gated on a moq-lite codec — see
audio-rooms feature. It implements both IETF `draft-ietf-moq-transport-17` (under
`moq/`) and **moq-lite Lite-03** (kixelated's variant, under `moq/lite/`) — the
production listener path uses moq-lite to interop with the nostrnests reference
relay. Speaker-side moq-lite is still pending; see
`nestsClient/plans/2026-04-26-moq-lite-gap.md`.
## Architecture
@@ -2,17 +2,17 @@
What's left between today's code and shippable audio rooms in Amethyst.
> **STATUS UPDATE (2026-04-26 PM):** the interop test suite (phases 14 of
> the nostrnests work, see commits `3283d30` → `1887bd1`) uncovered that
> nostrnests's stack runs on **moq-lite** (kixelated's variant), NOT IETF
> `draft-ietf-moq-transport-17` — which is what `:nestsClient`'s MoQ layer
> currently implements. The two are wire-incompatible. Concrete impact:
> the listener path described under "Where we are" below works against
> any IETF MoQ-transport server but cannot exchange a single MoQ message
> with the real nostrnests relay until a moq-lite codec lands. Tracking
> doc: [`2026-04-26-moq-lite-gap.md`](2026-04-26-moq-lite-gap.md). Phase
> M1 below should be re-read with that gap in mind — manual validation
> against `nostrnests.com` is on hold until the moq-lite phase ships.
> **STATUS UPDATE (2026-04-26 PM):** the interop test suite (phases 15d
> of the nostrnests work, commits `3283d30` → `41f4dcd`) uncovered that
> nostrnests runs on **moq-lite** (kixelated's variant), not IETF
> `draft-ietf-moq-transport-17`. **Listener-side moq-lite is now landed**
> — `connectNestsListener` opens a `MoqLiteSession` and adapts frames
> back to `MoqObject` so `AudioRoomPlayer` / `AudioRoomViewModel` keep
> working unchanged. The Phase M1 manual validation against
> `nostrnests.com` should now actually work end-to-end (modulo Docker
> harness running). Speaker-side moq-lite is still pending — needs a
> small `WebTransportSession.acceptBidiStream` extension; tracked in
> [`2026-04-26-moq-lite-gap.md`](2026-04-26-moq-lite-gap.md).
## Where we are
+41 -2
View File
@@ -1,6 +1,9 @@
# Plan: bridge the moq-lite protocol gap
**Status:** wire spec known; implementation pending.
**Status:** **listener side done** (phase 5a → 5d, commits `fb47a4c`
`41f4dcd`); speaker side blocked on a `WebTransportSession` API
extension. Default `:nestsClient:jvmTest` suite (124 tests) passes.
**Origin:** discovered while writing the nostrnests interop test suite (phases 14).
## Discovery
@@ -192,7 +195,43 @@ range. `priority` is a plain byte. Strings = `varint length + UTF-8`.
| SUBSCRIBE.broadcast | `<speakerPubkey>` (single string) |
| SUBSCRIBE.track | `"catalog.json"` then `"audio/data"` |
## Implementation plan
## Implementation status (2026-04-26 PM)
**Landed (listener path complete end-to-end through `:nestsClient`):**
| Phase | Commit | Surface |
| ----- | --------- | ---------------------------------------------------------------------------------------------------- |
| 5a | `fb47a4c` | `MoqLitePath` (mandatory wire-boundary normalisation), `MoqLitePathTest` |
| 5b | `fb47a4c` | `MoqLiteCodec` + every Lite-03 message type + `MoqLiteCodecTest` (round-trip + negative paths) |
| 5c | `4e136ca` | `MoqLiteSession.client(...)` (no SETUP), `announce`, `subscribe`, group uni-stream demux, framing helpers, `MoqLiteSessionTest` |
| 5d | `41f4dcd` | `connectNestsListener` swap — `MoqLiteNestsListener` adapts `MoqLiteFrame``MoqObject` for downstream `AudioRoomPlayer` / `AudioRoomViewModel`. WT URL path = `/<namespace>?jwt=<token>`. |
**Pending (speaker path — phase 5c-speaker):**
The agent's clarifying lookup confirmed (publisher.rs:40 / connection.js:130)
that moq-lite *publishers* run via `Stream::accept(session)` — the **relay**
opens both Announce and Subscribe bidi streams *to* the publisher. The
publisher only initiates uni streams (one per group of audio data).
That requires `WebTransportSession.acceptBidiStream(): Flow<WebTransportBidiStream>`
which is **not** currently exposed by `:nestsClient`'s WT abstraction
(it has `incomingUniStreams` and `openBidiStream` but no
`incomingBidiStreams`). The underlying `:quic` stack already has
`QuicConnection.awaitIncomingPeerStream` (commonMain:397), so wiring
this through is mechanical — but it's a real API addition and worth a
separate phase.
Once that lands, the speaker side adds:
- `MoqLiteSession.runPublisher(suffix, onAnnouncePlease, onSubscribe)`
that loops on `acceptBidi` and dispatches by ControlType
- A new `MoqLiteNestsSpeaker` that wraps the session and feeds
Opus frames to one uni-stream-per-group writer
Then `connectNestsSpeaker` switches the same way `connectNestsListener`
just did, and the existing integration tests (round-trip, multi-peer)
should pass against the real Docker'd nostrnests stack.
## Implementation plan (original spec — kept for reference)
### Phase 5a — codec primitives (1 day)