diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 54dbea3c4f..6e1924027f 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -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 diff --git a/nestsClient/plans/2026-04-26-audio-rooms-completion.md b/nestsClient/plans/2026-04-26-audio-rooms-completion.md index dae5ab0518..99de1e17dc 100644 --- a/nestsClient/plans/2026-04-26-audio-rooms-completion.md +++ b/nestsClient/plans/2026-04-26-audio-rooms-completion.md @@ -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 1–4 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 1–5d +> 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 diff --git a/nestsClient/plans/2026-04-26-moq-lite-gap.md b/nestsClient/plans/2026-04-26-moq-lite-gap.md index 0c2676f11c..587cbd1145 100644 --- a/nestsClient/plans/2026-04-26-moq-lite-gap.md +++ b/nestsClient/plans/2026-04-26-moq-lite-gap.md @@ -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 1–4). ## Discovery @@ -192,7 +195,43 @@ range. `priority` is a plain byte. Strings = `varint length + UTF-8`. | SUBSCRIBE.broadcast | `` (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 = `/?jwt=`. | + +**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` +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)