Skip to content

Commit 361df8b

Browse files
committed
Merge remote-tracking branch 'origin/main' into bench-tooling-reorg
2 parents b287e23 + 3446252 commit 361df8b

34 files changed

Lines changed: 5770 additions & 27 deletions
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# MOQ Lite 04 protocol layer
2+
3+
Status: done
4+
5+
## Problem Statement
6+
7+
`moqx` has a protocol-neutral QUIC transport foundation and now has initial MOQ
8+
Lite draft-04 message structs plus generic `MOQX.Codec` contracts. The next
9+
work is to turn those building blocks into a real MOQ Lite protocol layer.
10+
11+
The transport PRD intentionally keeps "Full MOQ Lite implementation" out of
12+
scope. This PRD tracks the upper-layer work: MOQ Lite wire codecs, stream
13+
framing, and the protocol state machine that runs above `MOQX.Transport`.
14+
15+
Relevant draft facts for this scope:
16+
17+
- MOQ Lite draft-04 uses ALPN `moq-lite-04` for bare QUIC.
18+
- The session is active immediately after connection establishment; there is no
19+
CLIENT_SETUP/SERVER_SETUP exchange.
20+
- Bidirectional transaction streams are used for Announce, Subscribe, Fetch,
21+
Probe, and Goaway.
22+
- Publisher-created unidirectional Group Streams start with GROUP and then
23+
carry FRAME messages.
24+
- Unknown stream types are reset for extension probing rather than treated as a
25+
connection-level protocol failure.
26+
- Most messages carry a message length field, but that framing field is not
27+
part of the semantic message struct.
28+
29+
## Solution
30+
31+
Build `MOQX.MOQLite04` as its own protocol variant on top of `MOQX.Transport`.
32+
33+
The implementation should proceed in narrow slices:
34+
35+
1. Define semantic message structs and stream type lookup helpers.
36+
2. Add shared binary helpers in `MOQX.Codec` for the integer/string/bytes
37+
primitives needed by MOQ Lite and future draft-14 work.
38+
3. Implement MOQ Lite payload encoders and decoders for every message struct.
39+
4. Add a MOQ Lite stream codec that handles stream type prefixes, message
40+
length fields, buffering, and payload decoder selection.
41+
5. Add a MOQ Lite session/stream state machine that enforces stream roles,
42+
message ordering, graceful finish, abort sending, abort receiving, and
43+
connection close behavior using `MOQX.Transport`.
44+
6. Add a URI-first `MOQX.MOQLite04.connect/2` facade that establishes a
45+
transport connection and starts a role-neutral MOQ Lite client.
46+
7. Add a client runner that feeds normalized transport events into Session and
47+
applies returned transport actions through `MOQX.Transport`.
48+
8. Add subscriber-style client operations, with role implied by each operation
49+
rather than by a whole-connection mode.
50+
9. Add publisher-style client operations and a client-level
51+
subscribe/group/frame smoke flow over `MOQX.Transport.Support`.
52+
53+
## User Stories
54+
55+
1. As a MOQ Lite client implementer, I want typed message structs so protocol
56+
code can be written against Elixir values rather than ad hoc maps or raw
57+
binaries.
58+
2. As a codec implementer, I want shared `MOQX.Codec` helpers so draft-14 and
59+
MOQ Lite do not duplicate variable-length integer, string, and byte payload
60+
parsing.
61+
3. As a protocol implementer, I want payload encoders and decoders to be
62+
independent of stream buffering so they can be tested with simple binaries.
63+
4. As a session implementer, I want a stream codec that owns message lengths
64+
and incomplete buffers so transport events can be converted into complete
65+
messages deterministically.
66+
5. As a MOQ Lite endpoint, I want transaction stream state to reject invalid
67+
message order, such as SUBSCRIBE_DROP before the first SUBSCRIBE_OK.
68+
6. As a subscriber, I want Fetch Streams to return FRAME messages directly on
69+
the same bidirectional stream without a GROUP header.
70+
7. As a publisher, I want Group Streams to start with GROUP and then emit
71+
ordered FRAME payloads until Finish Sending or Abort Sending.
72+
8. As a future draft-14 implementer, I want this work to avoid a common
73+
protocol behaviour that would force draft-14 into MOQ Lite's stream model.
74+
75+
## Implementation Decisions
76+
77+
- `MOQX.Transport` remains the only transport boundary used by the protocol
78+
layer.
79+
- Protocol code must not match raw `quicer` messages.
80+
- `MOQX.Codec` is generic. It must not contain MOQ Lite stream roles, message
81+
atom tables, or session state.
82+
- `MOQX.MOQLite04` owns MOQ Lite draft-04 message structs, stream type lookup,
83+
stream codec, and session state.
84+
- The upper protocol state machine is called a Session. Connection remains
85+
transport vocabulary for `MOQX.Transport.Connection`.
86+
- Session APIs are pure reducers. `handle_transport/2` consumes normalized
87+
`MOQX.Transport` events and `handle_command/2` consumes local application
88+
intent.
89+
- Session reducers return protocol events and transport actions as data. They
90+
do not call `MOQX.Transport`, `quicer`, or process APIs directly.
91+
- Transport stream identity is preserved. Stream-derived protocol events remain
92+
tagged with a stream identity or ref, and each transport stream keeps its own
93+
`MOQX.MOQLite04.StreamCodec` state.
94+
- `MOQX.MOQLite04.Error` is the protocol error boundary. Session code should
95+
use structured errors and convert to integer transport application error
96+
codes only when producing transport actions.
97+
- `StreamType` remains a typespec plus lookup helpers, not a struct.
98+
- Message structs contain semantic payload fields only. Message length is a
99+
stream/message framing concern.
100+
- Decoder selection is stream-state dependent. A single decode-anything
101+
function should not guess message type without context.
102+
- The first implementation targets native QUIC and ALPN `moq-lite-04`;
103+
WebTransport remains out of scope.
104+
- Session APIs must receive explicit options or structs. Do not use
105+
`Application` env as a test seam or mutable global configuration.
106+
- The future `MOQX.MOQLite04.connect/2` boundary should accept a URI
107+
string or `URI.t()` directly. The module name already fixes the MOQ Lite
108+
draft-04 variant, so a separate `MOQX.Endpoint` wrapper would duplicate
109+
protocol/profile information at this layer.
110+
- Transport backend selection and backend options belong in explicit client
111+
options, such as `transport: {MOQX.Transport.Quicer, opts}`, not in endpoint
112+
data.
113+
114+
## Testing Decisions
115+
116+
Default tests should stay fast and deterministic.
117+
118+
Initial coverage should include:
119+
120+
- `MOQX.Codec` primitive round-trip and boundary tests;
121+
- payload encoder/decoder round-trip tests for every MOQ Lite message struct;
122+
- invalid enum, invalid length, incomplete buffer, and trailing-bytes errors;
123+
- stream codec tests for stream type prefixes and message length stripping;
124+
- pure state-machine tests for Announce, Subscribe, Fetch, Probe, Goaway, and
125+
Group stream transitions;
126+
- support-transport tests showing the state machine consumes normalized
127+
`MOQX.Transport` events rather than raw `quicer` events.
128+
129+
Real QUIC or interop tests should stay tagged `:integration` and remain
130+
explicitly invoked.
131+
132+
## Out of Scope
133+
134+
- MOQT draft-14 protocol implementation.
135+
- WebTransport-over-HTTP/3 support.
136+
- Media payload parsing or codec/container awareness.
137+
- Production scheduling, caching, and relay fanout policy beyond the minimal
138+
protocol state needed to exchange messages.
139+
- External MOQ Lite relay interoperability as a default unit-test requirement.
140+
141+
## Progress
142+
143+
Initial building block commit:
144+
145+
- `3dc2a14 Add moq-lite message model and codec contracts`
146+
147+
Delivered so far:
148+
149+
- `MOQX.Codec`
150+
- `MOQX.Codec.Encoder`
151+
- `MOQX.Codec.Decoder`
152+
- `MOQX.Codec` varint, string, and byte payload helpers
153+
- `MOQX.MOQLite04` stream type lookup helpers
154+
- `MOQX.MOQLite04` subscribe response discriminator lookup helpers
155+
- `MOQX.MOQLite04` message structs for Announce, Subscribe, Fetch, Probe,
156+
Goaway, Group, and Frame messages
157+
- `MOQX.MOQLite04` payload encoders and decoders for all message structs
158+
- `MOQX.MOQLite04.Error` structured protocol errors and transport application
159+
code mapping
160+
- `MOQX.MOQLite04.StreamCodec` incremental receive and send framing for
161+
opener and responder stream sides
162+
- `MOQX.MOQLite04.Session` pure reducer for normalized transport input,
163+
local protocol commands, per-stream codec state, protocol events, and
164+
transport action output
165+
- `MOQX.MOQLite04.connect/2` URI-first client facade over explicit transport
166+
options, native QUIC ALPN `moq-lite-04`, and a role-neutral
167+
`MOQX.MOQLite04.Client` value
168+
- `MOQX.MOQLite04.command/2` and `MOQX.MOQLite04.recv/2` client runner APIs
169+
that thread the connected client value, keep `Session` pure, and apply
170+
returned transport actions through `MOQX.Transport`
171+
- Role-neutral subscriber operations on `MOQX.MOQLite04` for
172+
AnnounceInterest, Subscribe, SubscribeUpdate, Fetch, Probe, repeated Probe,
173+
and Goaway transaction streams
174+
- Role-neutral publisher operations on `MOQX.MOQLite04` for accepting
175+
peer-opened streams, sending Announce, SubscribeOk, and SubscribeDrop
176+
responses, and publishing Group plus Frame messages on publisher-created
177+
unidirectional streams
178+
- A client-level support-transport smoke flow that subscribes, accepts the
179+
subscription, sends SubscribeOk, publishes a Group, and delivers the original
180+
Frame payload through public client APIs
181+
- Session tests for Announce, Subscribe, Fetch, Probe, Goaway, Group,
182+
stream lifecycle, support transport normalized events, and the
183+
reference-style subscribe/group/frame smoke flow
184+
185+
Follow-up integration work:
186+
187+
- issue 09: real QUIC MOQ Lite 04 integration tests over
188+
`MOQX.Transport.Quicer`
189+
190+
## References
191+
192+
- <https://datatracker.ietf.org/doc/html/draft-lcurley-moq-lite-04>
193+
- <https://datatracker.ietf.org/doc/draft-ietf-moq-transport/14/>
194+
- `docs/adr/0001-transport-boundary-support-transport-and-benchmark-harness.md`
195+
- `docs/adr/0002-native-quic-first-webtransport-out-of-scope.md`
196+
- `docs/adr/0003-validated-endpoints-above-raw-transport.md`
197+
- `docs/adr/0006-protocol-variants-own-session-state-codec-stays-generic.md`
198+
- `docs/adr/0007-protocol-sessions-are-pure-reducers.md`
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
> *This was generated by AI during triage.*
2+
3+
# Add MOQ Lite 04 message model and generic codec contracts
4+
5+
Status: closed
6+
Type: enhancement
7+
8+
## Parent
9+
10+
`.scratch/moq-lite-04-protocol/PRD.md`
11+
12+
## What to build
13+
14+
Add the initial protocol building blocks needed before implementing MOQ Lite
15+
wire encoding or session state.
16+
17+
## Acceptance criteria
18+
19+
- [x] `MOQX.Codec` exists as a generic namespace for shared codec helpers.
20+
- [x] `MOQX.Codec.Encoder` exists as an Elixir protocol for typed
21+
value-to-iodata encoding.
22+
- [x] `MOQX.Codec.Decoder` exists as a generic behaviour for complete payload
23+
decoding.
24+
- [x] `MOQX.MOQLite04` exists as the MOQ Lite draft-04 namespace.
25+
- [x] MOQ Lite draft-04 stream type lookup works in both directions.
26+
- [x] Stream type is modeled as a typespec plus lookup tables, not a struct.
27+
- [x] MOQ Lite draft-04 messages are represented as semantic structs.
28+
- [x] Message length is kept out of the message structs.
29+
- [x] Tests cover the codec contracts, stream type lookup, and message structs.
30+
31+
## Progress
32+
33+
Implemented in commit `3dc2a14 Add moq-lite message model and codec contracts`.
34+
35+
Validation completed before commit:
36+
37+
- `mix format`
38+
- `mix test`
39+
- `mix credo --strict`
40+
41+
## Comments
42+
43+
- 2026-06-05: Closed after landing the generic `MOQX.Codec` contracts and the
44+
`MOQX.MOQLite04` message model.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
> *This was generated by AI during triage.*
2+
3+
# Add shared binary codec helpers
4+
5+
Status: closed
6+
Type: enhancement
7+
8+
## Parent
9+
10+
`.scratch/moq-lite-04-protocol/PRD.md`
11+
12+
## What to build
13+
14+
Add protocol-neutral binary helpers under `MOQX.Codec` for the primitive wire
15+
formats needed by MOQ Lite draft-04 payload and stream codecs.
16+
17+
The helpers should be small pure functions, not a process or protocol-specific
18+
dispatcher.
19+
20+
## Acceptance criteria
21+
22+
- [x] Variable-length integer encode/decode helpers exist and return explicit
23+
`{:ok, value, rest}` or `{:error, reason}` results for decode.
24+
- [x] Boundary tests cover every encoded integer size class required by the
25+
referenced MOQT-family drafts.
26+
- [x] Incomplete integer inputs return a distinguishable incomplete-data error.
27+
- [x] Length-prefixed UTF-8 string helpers exist for MOQ Lite `(s)` fields.
28+
- [x] Length-prefixed byte payload helpers exist for MOQ Lite `(b)` fields.
29+
- [x] Helpers reject malformed lengths instead of reading past available data.
30+
- [x] The module remains protocol-neutral: no MOQ Lite message atoms, stream
31+
roles, or session state in `MOQX.Codec`.
32+
- [x] Tests are pure unit tests and do not require live QUIC transport.
33+
34+
## Notes
35+
36+
Consult MOQ Lite draft-04 encoding sections before implementing the exact
37+
integer, string, and byte helper semantics. Keep helper names generic enough to
38+
serve future MOQT draft-14 codec work.
39+
40+
## Progress
41+
42+
Implemented in the current working tree:
43+
44+
- `MOQX.Codec.encode_varint/1`
45+
- `MOQX.Codec.decode_varint/1`
46+
- `MOQX.Codec.encode_string/1`
47+
- `MOQX.Codec.decode_string/1`
48+
- `MOQX.Codec.encode_bytes/1`
49+
- `MOQX.Codec.decode_bytes/1`
50+
51+
Validation:
52+
53+
- `mix test test/moqx/codec/binary_test.exs`
54+
- `mix test test/moqx/codec/binary_test.exs test/moqx/moq_lite_04_test.exs test/moqx/moq_lite_04/payload_codec_test.exs test/moqx/codec/encoder_decoder_test.exs`
55+
56+
## Comments
57+
58+
- 2026-06-05: Closed after adding generic QUIC varint and length-prefixed
59+
string/bytes helpers under `MOQX.Codec`.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
> *This was generated by AI during triage.*
2+
3+
# Implement MOQ Lite 04 payload encoders and decoders
4+
5+
Status: closed
6+
Type: enhancement
7+
8+
## Parent
9+
10+
`.scratch/moq-lite-04-protocol/PRD.md`
11+
12+
## What to build
13+
14+
Implement MOQ Lite draft-04 payload encoding and decoding for the message
15+
structs under `MOQX.MOQLite04`.
16+
17+
This slice should produce payload bytes for already-selected message shapes and
18+
parse complete payload bytes back into typed structs. Stream type prefixes,
19+
message length fields, buffering, and stream-state dispatch are handled by a
20+
later stream codec slice.
21+
22+
## Acceptance criteria
23+
24+
- [x] `MOQX.Codec.Encoder` implementations exist for all MOQ Lite draft-04
25+
message structs.
26+
- [x] Concrete decoders exist for all MOQ Lite draft-04 message payload shapes.
27+
- [x] Round-trip tests cover AnnounceInterest, Announce, Subscribe,
28+
SubscribeUpdate, SubscribeOk, SubscribeDrop, Fetch, Probe, Goaway, Group,
29+
and Frame.
30+
- [x] Invalid status/order/discriminator values return explicit errors.
31+
- [x] Decoders reject trailing bytes when the caller expects a complete payload.
32+
- [x] Message length remains outside the message structs and outside payload
33+
encoder implementations unless the stream codec explicitly adds it.
34+
- [x] Subscribe response type discriminators are handled without adding a
35+
synthetic `type` field to `SubscribeOk` or `SubscribeDrop` structs.
36+
- [x] Tests do not depend on `Application` env or live QUIC sockets.
37+
38+
## Notes
39+
40+
Use `MOQX.Codec` helpers for primitive parsing. Keep decoder selection
41+
contextual: the stream codec or state machine should decide which decoder is
42+
valid next.
43+
44+
## Progress
45+
46+
Implemented in the current working tree:
47+
48+
- `MOQX.Codec.Encoder` implementations for every `MOQX.MOQLite04` message
49+
struct.
50+
- `MOQX.Codec.Decoder` implementations through each message module's
51+
`decode/2` callback.
52+
- Subscribe response discriminator lookup helpers on `MOQX.MOQLite04`.
53+
54+
Validation:
55+
56+
- `mix test test/moqx/moq_lite_04/payload_codec_test.exs`
57+
- `mix test test/moqx/codec/binary_test.exs test/moqx/moq_lite_04_test.exs test/moqx/moq_lite_04/payload_codec_test.exs test/moqx/codec/encoder_decoder_test.exs`
58+
59+
## Comments
60+
61+
- 2026-06-05: Closed after adding payload-only encoders/decoders. Stream type
62+
prefixes, message length fields, buffering, and stream-state dispatch remain
63+
for issue 04.

0 commit comments

Comments
 (0)