Commit Graph

4 Commits

Author SHA1 Message Date
Claude d65ab7b616 feat(nestsClient): MoQ SUBSCRIBE family + OBJECT_DATAGRAM codecs
Phase 3c-2 of the Clubhouse/nests integration. Extends the MoQ
control-plane codec with the subscribe lifecycle messages (SUBSCRIBE,
SUBSCRIBE_OK, SUBSCRIBE_ERROR, UNSUBSCRIBE) and adds the
OBJECT_DATAGRAM wire format the listener uses to receive low-latency
Opus audio. Pure codec layer — no session-pump or network changes;
the subscribe/object delivery Flow arrives in Phase 3c-3.

commonMain (nestsclient.moq):
- New message types in `MoqMessageType`: Subscribe (0x03),
  SubscribeOk (0x04), SubscribeError (0x05), Unsubscribe (0x0A).
- New data classes in MoqMessage.kt:
  * `TrackNamespace` — tuple of byte strings, with `of(vararg String)`
    helper for the common UTF-8 case.
  * `SubscribeFilter` enum. Phase 3c-2 supports LatestGroup /
    LatestObject; AbsoluteStart/AbsoluteRange throw at construction
    (they need extra wire fields we'll add when nests needs them).
  * `Subscribe`, `SubscribeOk`, `SubscribeError`, `Unsubscribe` data
    classes. `SubscribeOk` validates contentExists + largest-id
    coupling at construction.
- New codecs in `MoqCodec` (namespace tuple + all four messages).
  Unknown filter codes on the wire are rejected.
- `MoqObject` data class + `MoqObjectDatagram` encode/decode for the
  OBJECT_DATAGRAM wire format (track_alias, group_id, object_id,
  publisher_priority, status, payload).

Tests:
- `SubscribeCodecTest` — round-trips for each message with multi-
  segment namespaces + parameters, SUBSCRIBE_OK with and without
  contentExists, concatenated decode in sequence, unknown-filter
  rejection, construction-time validation.
- `MoqObjectDatagramTest` — Opus-sized payload round-trip, zero-
  length payload with OBJECT_DOES_NOT_EXIST status, 8-byte-varint
  boundary coverage, truncated datagram rejection, out-of-range
  priority rejection.

https://claude.ai/code/session_013nVLALALKaHVgHm9u5Cg8D
2026-04-22 03:26:53 +00:00
Claude 37e24ce4f3 feat(nestsClient): MoQ varint + SETUP handshake codec
Phase 3c-1 of the Clubhouse/nests integration. Lands a MoQ-transport
control-plane codec (draft-ietf-moq-transport) and a session wrapper
that runs the SETUP handshake over any WebTransportSession. Purely
commonMain + fully tested end-to-end against FakeWebTransport — no
network or Kwik dependency required.

commonMain (nestsclient.moq):
- `Varint` — QUIC variable-length integer codec per RFC 9000 §16.
  Encode/decode/size, with typed truncation handling (decode returns
  null so callers can buffer more and retry).
- `MoqWriter` / `MoqReader` — append-only byte writer + bounds-checked
  reader used by the message codec.
- `MoqCodecException` — typed error for malformed frames.
- `MoqMessage` sealed class + `MoqMessageType` enum + `SetupParameter`.
  Phase 3c-1 covers just `ClientSetup` / `ServerSetup` (message types
  0x40 / 0x41).
- `MoqCodec.encode/decode` — wraps payload with `type (varint) +
  length (varint)`. Rejects unknown types and trailing bytes inside a
  declared payload window.
- `MoqSession.client/server` — attaches to a WebTransportSession and
  runs the CLIENT_SETUP / SERVER_SETUP handshake with version
  negotiation + configurable timeout.
- `MoqVersion` constants for draft-11 and draft-17.

Tests:
- `VarintTest` — all four RFC 9000 §A.1 sample vectors (1/2/4/8 byte),
  boundary round-trips, negative/overflow rejection, short-buffer
  returns null, bytesConsumed accuracy.
- `MoqCodecTest` — CLIENT_SETUP / SERVER_SETUP round-trip (empty and
  multi-param), multi-version negotiation, exact wire layout for
  ServerSetup(1L), truncated-frame returns null, concatenated frames
  decode with offset, unknown type rejected, trailing bytes rejected,
  zero-length parameter values allowed.
- `MoqSessionTest` — full SETUP exchange over FakeWebTransport (both
  sides happy path, client falling back to older version, server
  rejecting when no version overlap + client timing out cleanly).

SUBSCRIBE / ANNOUNCE / OBJECT_STREAM / OBJECT_DATAGRAM land in
Phase 3c-2 on top of the same MoqSession.

https://claude.ai/code/session_013nVLALALKaHVgHm9u5Cg8D
2026-04-22 03:21:15 +00:00
Claude 4ead4ccd5c feat(nestsClient): WebTransport abstraction + fake + Kwik stub
Phase 3b-1 of the Clubhouse/nests integration. Lands the
[WebTransportSession] abstraction the MoQ layer (Phase 3c) will code
against, so MoQ framing + tests can develop in parallel with the real
Kwik-based transport integration (deferred to Phase 3b-2).

commonMain:
- `WebTransportSession` — bidi/uni stream access, datagrams, close.
- `WebTransportBidiStream` / `WebTransportReadStream` /
  `WebTransportWriteStream` — minimal read/write surface.
- `WebTransportFactory.connect(authority, path, bearerToken)` for
  opening sessions.
- `WebTransportException(kind, ...)` with four canonical failure modes
  (HandshakeFailed, ConnectRejected, PeerClosed, NotImplemented) so UI
  code doesn't need to know about library-specific exceptions.
- `FakeWebTransport.pair()` — in-memory, fully-wired client/server
  pair for unit-testing MoQ framing without touching a real QUIC stack.

jvmAndroid:
- `KwikWebTransportFactory` stub that throws
  `WebTransportException(NotImplemented)` at `connect()`. Doc spells
  out the handshake sequence (QUIC dial → H3 SETTINGS →
  `:method=CONNECT :protocol=webtransport` Extended CONNECT → 2xx) so
  Phase 3b-2 can drop the real implementation in without touching
  callers.

Tests:
- `FakeWebTransportTest` — datagram round-trip both directions,
  bidi-stream write visible on peer side, close() flips isOpen.
- `WebTransportExceptionTest` — kind + message + cause preserved.
- `KwikWebTransportFactoryTest` — `connect()` currently fails with the
  NotImplemented sentinel, so Phase 3b-2 can replace this test when
  the real handshake lands.

https://claude.ai/code/session_013nVLALALKaHVgHm9u5Cg8D
2026-04-22 03:10:28 +00:00
Claude 933b522273 feat(nestsClient): NIP-98 auth + nests room-info client
Phase 3a of the Clubhouse/nests integration. Adds a new KMP module
`nestsClient` (Android + JVM targets) that owns the HTTP control plane
for talking to a nests audio-room backend. No transport/audio yet —
that arrives in 3b with WebTransport + MoQ.

Surface:
- `NestsAuth.header(signer, url, method)` signs a kind 27235 event via
  Quartz's existing HTTPAuthorizationEvent and returns a ready-to-use
  `Authorization: Nostr <base64>` header value.
- `NestsRoomInfo` data class + tolerant JSON parser (ignores unknown
  fields so newer nests server revisions don't break older clients).
- `NestsClient.resolveRoom(serviceBase, roomId, signer)` calls
  `<serviceBase>/<roomId>` with the signed NIP-98 header and returns
  the MoQ endpoint + token the audio layer will need.
- `OkHttpNestsClient` (jvmAndroid) is the default implementation
  shared between Android and desktop JVM.

Tests:
- `NestsRoomInfoTest` (commonTest) covers full/minimal payloads,
  unknown-field tolerance, missing-endpoint rejection, and URL
  construction edge cases.
- `NestsAuthTest` (jvmTest) signs a real 27235 event, decodes the
  base64 back through Quartz's JacksonMapper, and asserts the
  signature verifies and the url+method tags bind correctly.

https://claude.ai/code/session_013nVLALALKaHVgHm9u5Cg8D
2026-04-22 02:58:24 +00:00