joule-moq: draft-ietf-moq-transport-22 wire codec, with the crate moved onto it #283
Loading…
Reference in a new issue
No description provided.
Delete branch "joule-moq/draft-22"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
joule-moqnow speaksdraft-ietf-moq-transport-22(1 October 2026), negotiated by the ALPNmoqt-22. Its draft-08-style codec is gone: -22 changed the integer, the control streams, every message and the object framing, so no byte of the old wire form survives. The codec of -22 is a module of its own (src/draft22/), selected throughdraft::Draft. Another draft, later or earlier for interoperation, would be a sibling module and aDraftvariant, and the draft-neutral parts (tracks, objects, receipts, session state) would stay as they are. The crate moves to 0.3.0 because its public API changed.What -22 changed that mattered here
wire::encode_varintanddecode_varintare now -22's.moqt-NN) or byWT-Available-Protocols. Each endpoint opens a unidirectional control stream with a SETUP of its own, which carries Setup Options and no version or role; only GOAWAY may follow it there. Extensions declare themselves by Setup Options (§6.3.2).What is in this PR
draft22). It encodes and decodes every control message of Table 5 with its parameters, Key-Value-Pairs, Properties and Setup Options, the Authorization Token, namespaces and names (with §8.8's rendering), subgroup streams, datagrams, fetch streams and padding. It also covers Request IDs, the order of messages on control and request streams, and MAX_REQUEST_UPDATES credit, plus the four code registries.WireErrorwhoseconsequence()is the draft's: close the session with a code, reject the request, or a malformed track.session: the draft by ALPN, SETUP options, and Request IDs by end.mock: objects, receipt bundles and requests now cross the -22 codec. The relay reads an object's delivery timeout from its OBJECT_DELIVERY_TIMEOUT property (§10.2).quic) has a pair of control streams, a request stream per request, subgroup, fetch and padding streams, and datagrams.lockstep-quicand the standalone transport example build and pass unchanged.carriage).WAI_RECEIPTproperty (extensions/moq-streaming-format.md§5).WAI_RECEIPTvalue is laid out in the crate README: content hash, figure, class label, evidence, group link hash.max_mrl_layer, is now an OBJECT_PROPERTY_FILTER on the layer property.MOQT_DRAFT_VERSIONis0xff000016.Tests that asserted draft-08 bytes or behaviour, rewritten for -22
messages:subscribe_roundtrip,subscribe_roundtrip_with_mrl_capandannounce_roundtripbecamesubscribe_round_trips_with_and_without_a_layer_capandpublish_namespace_round_trips.subscribe_decode_handles_legacy_wire_without_mrl_byteshas no -22 counterpart, because -22 refuses bytes after a message's last field.session:handshake_round_tripnow checks SETUP options and the receipt extension's Setup Option.rejects_unsupported_versionnow refuses at the ALPN, plus SETUP rules for the sender's end.wire:varint_overflowbecameevery_u64_encodes, because -22's integer has no overflow. The boundary values were updated.transport_quic:quic_loopback_setup_handshakenow uses unidirectional control streams and a SUBSCRIBE on a request stream.quic_delivers_signed_receipt_objects_end_to_endnow sends a receipts-track stream and a media subgroup stream instead of the old JSON framing. Its assertions are unchanged.WAI_RECEIPT, a datagram, a Descending fetch with End of Range, and a padding stream.tests/staged_moq_trace.rsandtests/wai_jwp_corpus.rs.Vectors (
tests/vectors/draft-22/vectors.json)tests/draft22_vectors.rsgenerates the corpus deterministically from typed values, with expectations stated in the generator rather than read back from the decoder. The test fails if the committed file differs from what it generates. It holds the crate to every vector, whole and fed to the incremental decoder in pieces.WAI_RECEIPTvalueThe negative vectors cover:
Each negative vector states the refusal it must meet and the draft's consequence.
Independent check.
tests/vectors/draft-22/moqt22_check.pyuses the standard library only and is written from the draft's sections. It reads every good vector as stated, refuses every negative one with the stated kind and consequence, and fails on an empty corpus. I checked that it fails on a corrupted reading, a changed expected kind and a flipped byte.joule-code-conformance.ymlruns it after the joule-moq tests. Its path is underjoule-code/**, which the workflow already filters on.Round trips and hostile input.
tests/draft22_roundtrip.rsuses a seeded generator, so a failure reproduces. Encode → decode → encode is the identity for generated control messages of every type, subgroup and fetch streams in both orders, datagrams,WAI_RECEIPTvalues and every varint length. The encodings and the corpus are then mutated and fed to every decoder, whole and incremental; none may panic.The tests found two codec bugs, both fixed before this push:
Deferred
jwp-receipts§2.4.1 and §2.4.2. Today it is this crate's own JSON of the same receipts.moq-streaming-format.md§5 names no code point or byte layout forWAI_RECEIPT. This crate's choice, documented in its README, is for the extension to adopt or replace.How it was tested
Every build and test ran on the build host through
rb:rb <wt> -- 'cd joule-code/joule-code-rs && cargo test -p joule-moq --features quic --all-targets; cargo clippy -p joule-moq --features quic --all-targets'(lib,draft22_roundtrip,draft22_vectors,staged_moq_trace,wai_jwp_corpus, the example)rb --pull <wt> -- 'cd joule-code/joule-code-rs && JOULE_MOQ_WRITE_VECTORS=1 cargo test -p joule-moq --test draft22_vectors'rb <wt> -- 'cd joule-code/joule-code-rs && cargo test -p joule-moq --test draft22_roundtrip'rb <wt> -- 'bash scripts/check-lockfiles.sh joule-code/joule-code-rs joule-code/examples/transport-moqt; cd joule-code/joule-code-rs && cargo build --workspace --all-targets; cargo test -p lockstep-quic'rb <wt> -- 'cd joule-code/examples/transport-moqt && cargo test --locked'Local, light Python and shell:
python3 -I tests/vectors/draft-22/moqt22_check.py: every vector agrees.check-tests-in-ci.pyand its self-test.check-workflow-concurrency.shand its self-test, andpr-touches-paths-selftest.sh.check-spec-refs.pyand its self-test.check-no-vanity-metrics.sh,check-comparative-prose.shandtest-public-text-checks.sh.check-wai-section-refs.pyandcheck-orphan-sources.py.All pass. The full
cargo test --workspaceand the public-name check (which runs on the push to main) are left to CI.🤖 Generated with Claude Code