joule-moq: draft-ietf-moq-transport-22 wire codec, with the crate moved onto it #283

Merged
dcharlot merged 7 commits from joule-moq/draft-22 into main 2026-10-17 04:04:12 -04:00
Owner

joule-moq now speaks draft-ietf-moq-transport-22 (1 October 2026), negotiated by the ALPN moqt-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 through draft::Draft. Another draft, later or earlier for interoperation, would be a sibling module and a Draft variant, 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

  • The integer (§8.1). A leading-ones prefix gives the length, 1 to 9 bytes, and covers all 64 bits. A longer form than needed is valid. The crate used QUIC's integer before; wire::encode_varint and decode_varint are now -22's.
  • Version negotiation (§6.2) and SETUP (§9.1). The draft is fixed by ALPN (moqt-NN) or by WT-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).
  • Requests (§6.3, §6.4.2, §9.3-9.5). Every request opens a bidirectional stream of its own, and its responses come back on that stream without a Request ID. Requests take common responses (REQUEST_OK, REQUEST_ERROR), REQUEST_UPDATE replaces the update messages, and cancelling a request means resetting its stream. Request IDs are even from a client and odd from a server. The number of requests in flight is bounded by the transport's stream limits. What -22 does bound itself is MAX_REQUEST_UPDATES per stream.
  • Framing and parameters (§9, §9.20). A message is a type, a 16-bit Length and a body. Parameters are counted and their types delta-coded, and each has a value form of its own. An unknown parameter closes the session. Each message lists the parameters it allows, and most may not repeat. A publisher chooses the Track Alias, in SUBSCRIBE_OK or PUBLISH.
  • Filters (§3.3, §8.6, §9.20.9). A Location Filter has typed forms. Range Filters (subgroup, Object ID, priority, object and track property) have SetIDs. FILL_PARAMETERS replaced the joining FETCH.
  • Properties (§8.3, §8.4, §10). Properties are Key-Value-Pairs. They include Immutable Properties, Mandatory Track Properties and the Prior Group and Object ID Gaps, and two ranges are reserved for an application's own use, which this crate's carriage uses.
  • Data plane (§11). SUBGROUP_HEADER and OBJECT_DATAGRAM are flag fields. A fetch stream's objects are delta-coded against the prior entry, with three End of Range indicators. Object Status is Normal, End of Group or End of Track only. Padding streams and datagrams are new.
  • Limits. A namespace has at most 32 fields, none empty. A Full Track Name is at most 4,096 bytes, a property value at most 2^16-1, a reason phrase at most 1,024, a GOAWAY URI at most 8,192, and a message body at most 2^16-1. Status and error codes moved into registries with grease values (§12, §13, §16.11).

What is in this PR

  • The codec (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.
    • Decoders are bounded: they read no byte that is not there and allocate nothing from a stated length before the bytes exist.
    • Every refusal is a typed WireError whose consequence() is the draft's: close the session with a code, reject the request, or a malformed track.
    • Data streams have a whole-stream decoder and an incremental decoder for live streams; both share one strict parser.
  • Migration, behaviour kept.
    • 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).
    • Staged tracks: unchanged API.
    • The QUIC transport (feature quic) has a pair of control streams, a request stream per request, subgroup, fetch and padding streams, and datagrams.
    • The cross-machine example now uses the new transport API.
    • lockstep-quic and the standalone transport example build and pass unchanged.
  • Receipts under -22 (carriage).
    • The crate's objects ride -22 subgroup objects, with properties inside Immutable Properties at application-range code points: the object receipt, the layer fields, the envelope flag, and WAI's per-object WAI_RECEIPT property (extensions/moq-streaming-format.md §5).
    • The WAI_RECEIPT value is laid out in the crate README: content hash, figure, class label, evidence, group link hash.
    • A group's receipts ride its receipts track (§4.1 there).
    • A SUBSCRIBE layer cap, the old max_mrl_layer, is now an OBJECT_PROPERTY_FILTER on the layer property.
    • The receipt extension is declared by a Setup Option. -22 has no unregistered range for Setup Options, so the code point is this crate's choice until a provisional registration is made.
  • Where -22's text leaves an encoding open, one reading each, in the code, the README and the checker:
    • FILL_TIMEOUT and RENDEZVOUS_TIMEOUT are varints.
    • FILL_PARAMETERS holds parameters up to its length, with no count.
    • An End of Range's Group ID and Object ID are the delta fields its flag bits 0x08 and 0x04 mark.
    • An unknown message, stream or datagram type closes the session with PROTOCOL_VIOLATION.
  • Docs. The crate README (new) and the crate doc say which draft is implemented, what is not yet, the carriage, and the corpus's JSON view. There is a changelog entry, and MOQT_DRAFT_VERSION is 0xff000016.

Tests that asserted draft-08 bytes or behaviour, rewritten for -22

  • messages:
    • subscribe_roundtrip, subscribe_roundtrip_with_mrl_cap and announce_roundtrip became subscribe_round_trips_with_and_without_a_layer_cap and publish_namespace_round_trips.
    • subscribe_decode_handles_legacy_wire_without_mrl_bytes has no -22 counterpart, because -22 refuses bytes after a message's last field.
  • session:
    • handshake_round_trip now checks SETUP options and the receipt extension's Setup Option.
    • rejects_unsupported_version now refuses at the ALPN, plus SETUP rules for the sender's end.
  • wire: varint_overflow became every_u64_encodes, because -22's integer has no overflow. The boundary values were updated.
  • transport_quic:
    • quic_loopback_setup_handshake now uses unidirectional control streams and a SUBSCRIBE on a request stream.
    • quic_delivers_signed_receipt_objects_end_to_end now sends a receipts-track stream and a media subgroup stream instead of the old JSON framing. Its assertions are unchanged.
    • A new test covers WAI_RECEIPT, a datagram, a Descending fetch with End of Range, and a padding stream.
  • Unchanged and passing: the mock and relay tests, the staged, receipt and track tests, tests/staged_moq_trace.rs and tests/wai_jwp_corpus.rs.

Vectors (tests/vectors/draft-22/vectors.json)

tests/draft22_vectors.rs generates 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.

kind good negative
control (every message type) 29 34
uni-stream (control, subgroup, fetch, padding) 10 16
datagram 7 8
varint (Table 4 of the draft and a longer form) 9 1
WAI_RECEIPT value 4 2

The negative vectors cover:

  • input cut short, and a varint running past its frame;
  • a SUBGROUP_HEADER type of 128 or above written in two bytes;
  • unknown and reserved message, stream and datagram types;
  • repeated parameters, a repeated filter key and a repeated Setup Option;
  • a parameter a message does not allow, and an unknown parameter;
  • lengths past the draft's limits (reason phrase, GOAWAY URI, property value, Full Track Name, 33 namespace fields), and an empty namespace field;
  • forbidden flags, unknown statuses, and properties beside a non-Normal status;
  • delta and ID overflows, and a first fetch object that refers back;
  • malformed tokens;
  • malformed tracks (nested Immutable Properties, a Mandatory Track Property on an object, a gap past its ID);
  • control-stream order (GOAWAY twice, SUBSCRIBE after SETUP).

Each negative vector states the refusal it must meet and the draft's consequence.

Independent check. tests/vectors/draft-22/moqt22_check.py uses 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.yml runs it after the joule-moq tests. Its path is under joule-code/**, which the workflow already filters on.

Round trips and hostile input. tests/draft22_roundtrip.rs uses 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_RECEIPT values 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:

  • an 8-byte varint overflowed a shift in debug builds (a panic on input);
  • the incremental decoder refused a padding stream it had not consumed.

Deferred

  • To the next slice: WebTransport (§6.2.1), a relay with priority fan-out and per-object receipts, and a browser client.
  • The authorization token cache (§8.9 aliases and MAX_AUTH_TOKEN_CACHE_SIZE). The codec reads and writes tokens; the session keeps no cache.
  • Filters at a publisher. Nothing evaluates filters yet, and nothing holds a peer to its MAX_FILTER_RANGES; the codec reads and writes every filter and counts its ranges.
  • QUIC transport behaviour: GOAWAY migration, delivery timeouts on real streams, and §5.1 scheduling.
  • LOC and MSF semantics. Their properties ride like any other property.
  • The receipts-track payload in the JSON form of jwp-receipts §2.4.1 and §2.4.2. Today it is this crate's own JSON of the same receipts.
  • WAI's extension text. moq-streaming-format.md §5 names no code point or byte layout for WAI_RECEIPT. This crate's choice, documented in its README, is for the extension to adopt or replace.
  • A provisional registration of the receipt extension's Setup Option.

How it was tested

Every build and test ran on the build host through rb:

command exit
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) 0 (test 0, clippy 0)
rb --pull <wt> -- 'cd joule-code/joule-code-rs && JOULE_MOQ_WRITE_VECTORS=1 cargo test -p joule-moq --test draft22_vectors' 0
rb <wt> -- 'cd joule-code/joule-code-rs && cargo test -p joule-moq --test draft22_roundtrip' 0
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' 0 (each step 0)
rb <wt> -- 'cd joule-code/examples/transport-moqt && cargo test --locked' 0

Local, light Python and shell:

  • python3 -I tests/vectors/draft-22/moqt22_check.py: every vector agrees.
  • check-tests-in-ci.py and its self-test.
  • check-workflow-concurrency.sh and its self-test, and pr-touches-paths-selftest.sh.
  • check-spec-refs.py and its self-test.
  • check-no-vanity-metrics.sh, check-comparative-prose.sh and test-public-text-checks.sh.
  • check-wai-section-refs.py and check-orphan-sources.py.

All pass. The full cargo test --workspace and the public-name check (which runs on the push to main) are left to CI.

🤖 Generated with Claude Code

`joule-moq` now speaks `draft-ietf-moq-transport-22` (1 October 2026), negotiated by the ALPN `moqt-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 through `draft::Draft`. Another draft, later or earlier for interoperation, would be a sibling module and a `Draft` variant, 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 - **The integer (§8.1).** A leading-ones prefix gives the length, 1 to 9 bytes, and covers all 64 bits. A longer form than needed is valid. The crate used QUIC's integer before; `wire::encode_varint` and `decode_varint` are now -22's. - **Version negotiation (§6.2) and SETUP (§9.1).** The draft is fixed by ALPN (`moqt-NN`) or by `WT-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). - **Requests (§6.3, §6.4.2, §9.3-9.5).** Every request opens a bidirectional stream of its own, and its responses come back on that stream without a Request ID. Requests take common responses (REQUEST_OK, REQUEST_ERROR), REQUEST_UPDATE replaces the update messages, and cancelling a request means resetting its stream. Request IDs are even from a client and odd from a server. The number of requests in flight is bounded by the transport's stream limits. What -22 does bound itself is MAX_REQUEST_UPDATES per stream. - **Framing and parameters (§9, §9.20).** A message is a type, a 16-bit Length and a body. Parameters are counted and their types delta-coded, and each has a value form of its own. An unknown parameter closes the session. Each message lists the parameters it allows, and most may not repeat. A publisher chooses the Track Alias, in SUBSCRIBE_OK or PUBLISH. - **Filters (§3.3, §8.6, §9.20.9).** A Location Filter has typed forms. Range Filters (subgroup, Object ID, priority, object and track property) have SetIDs. FILL_PARAMETERS replaced the joining FETCH. - **Properties (§8.3, §8.4, §10).** Properties are Key-Value-Pairs. They include Immutable Properties, Mandatory Track Properties and the Prior Group and Object ID Gaps, and two ranges are reserved for an application's own use, which this crate's carriage uses. - **Data plane (§11).** SUBGROUP_HEADER and OBJECT_DATAGRAM are flag fields. A fetch stream's objects are delta-coded against the prior entry, with three End of Range indicators. Object Status is Normal, End of Group or End of Track only. Padding streams and datagrams are new. - **Limits.** A namespace has at most 32 fields, none empty. A Full Track Name is at most 4,096 bytes, a property value at most 2^16-1, a reason phrase at most 1,024, a GOAWAY URI at most 8,192, and a message body at most 2^16-1. Status and error codes moved into registries with grease values (§12, §13, §16.11). ## What is in this PR - **The codec (`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. - Decoders are bounded: they read no byte that is not there and allocate nothing from a stated length before the bytes exist. - Every refusal is a typed `WireError` whose `consequence()` is the draft's: close the session with a code, reject the request, or a malformed track. - Data streams have a whole-stream decoder and an incremental decoder for live streams; both share one strict parser. - **Migration, behaviour kept.** - `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). - Staged tracks: unchanged API. - The QUIC transport (feature `quic`) has a pair of control streams, a request stream per request, subgroup, fetch and padding streams, and datagrams. - The cross-machine example now uses the new transport API. - `lockstep-quic` and the standalone transport example build and pass unchanged. - **Receipts under -22 (`carriage`).** - The crate's objects ride -22 subgroup objects, with properties inside Immutable Properties at application-range code points: the object receipt, the layer fields, the envelope flag, and WAI's per-object `WAI_RECEIPT` property (`extensions/moq-streaming-format.md` §5). - The `WAI_RECEIPT` value is laid out in the crate README: content hash, figure, class label, evidence, group link hash. - A group's receipts ride its receipts track (§4.1 there). - A SUBSCRIBE layer cap, the old `max_mrl_layer`, is now an OBJECT_PROPERTY_FILTER on the layer property. - The receipt extension is declared by a Setup Option. -22 has no unregistered range for Setup Options, so the code point is this crate's choice until a provisional registration is made. - **Where -22's text leaves an encoding open**, one reading each, in the code, the README and the checker: - FILL_TIMEOUT and RENDEZVOUS_TIMEOUT are varints. - FILL_PARAMETERS holds parameters up to its length, with no count. - An End of Range's Group ID and Object ID are the delta fields its flag bits 0x08 and 0x04 mark. - An unknown message, stream or datagram type closes the session with PROTOCOL_VIOLATION. - **Docs.** The crate README (new) and the crate doc say which draft is implemented, what is not yet, the carriage, and the corpus's JSON view. There is a changelog entry, and `MOQT_DRAFT_VERSION` is `0xff000016`. ## Tests that asserted draft-08 bytes or behaviour, rewritten for -22 - `messages`: - `subscribe_roundtrip`, `subscribe_roundtrip_with_mrl_cap` and `announce_roundtrip` became `subscribe_round_trips_with_and_without_a_layer_cap` and `publish_namespace_round_trips`. - `subscribe_decode_handles_legacy_wire_without_mrl_bytes` has no -22 counterpart, because -22 refuses bytes after a message's last field. - `session`: - `handshake_round_trip` now checks SETUP options and the receipt extension's Setup Option. - `rejects_unsupported_version` now refuses at the ALPN, plus SETUP rules for the sender's end. - `wire`: `varint_overflow` became `every_u64_encodes`, because -22's integer has no overflow. The boundary values were updated. - `transport_quic`: - `quic_loopback_setup_handshake` now uses unidirectional control streams and a SUBSCRIBE on a request stream. - `quic_delivers_signed_receipt_objects_end_to_end` now sends a receipts-track stream and a media subgroup stream instead of the old JSON framing. Its assertions are unchanged. - A new test covers `WAI_RECEIPT`, a datagram, a Descending fetch with End of Range, and a padding stream. - Unchanged and passing: the mock and relay tests, the staged, receipt and track tests, `tests/staged_moq_trace.rs` and `tests/wai_jwp_corpus.rs`. ## Vectors (`tests/vectors/draft-22/vectors.json`) `tests/draft22_vectors.rs` generates 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. | kind | good | negative | |---|---|---| | control (every message type) | 29 | 34 | | uni-stream (control, subgroup, fetch, padding) | 10 | 16 | | datagram | 7 | 8 | | varint (Table 4 of the draft and a longer form) | 9 | 1 | | `WAI_RECEIPT` value | 4 | 2 | The negative vectors cover: - input cut short, and a varint running past its frame; - a SUBGROUP_HEADER type of 128 or above written in two bytes; - unknown and reserved message, stream and datagram types; - repeated parameters, a repeated filter key and a repeated Setup Option; - a parameter a message does not allow, and an unknown parameter; - lengths past the draft's limits (reason phrase, GOAWAY URI, property value, Full Track Name, 33 namespace fields), and an empty namespace field; - forbidden flags, unknown statuses, and properties beside a non-Normal status; - delta and ID overflows, and a first fetch object that refers back; - malformed tokens; - malformed tracks (nested Immutable Properties, a Mandatory Track Property on an object, a gap past its ID); - control-stream order (GOAWAY twice, SUBSCRIBE after SETUP). Each negative vector states the refusal it must meet and the draft's consequence. **Independent check.** `tests/vectors/draft-22/moqt22_check.py` uses 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.yml` runs it after the joule-moq tests. Its path is under `joule-code/**`, which the workflow already filters on. **Round trips and hostile input.** `tests/draft22_roundtrip.rs` uses 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_RECEIPT` values 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: - an 8-byte varint overflowed a shift in debug builds (a panic on input); - the incremental decoder refused a padding stream it had not consumed. ## Deferred - **To the next slice:** WebTransport (§6.2.1), a relay with priority fan-out and per-object receipts, and a browser client. - **The authorization token cache** (§8.9 aliases and MAX_AUTH_TOKEN_CACHE_SIZE). The codec reads and writes tokens; the session keeps no cache. - **Filters at a publisher.** Nothing evaluates filters yet, and nothing holds a peer to its MAX_FILTER_RANGES; the codec reads and writes every filter and counts its ranges. - **QUIC transport behaviour:** GOAWAY migration, delivery timeouts on real streams, and §5.1 scheduling. - **LOC and MSF semantics.** Their properties ride like any other property. - **The receipts-track payload** in the JSON form of `jwp-receipts` §2.4.1 and §2.4.2. Today it is this crate's own JSON of the same receipts. - **WAI's extension text.** `moq-streaming-format.md` §5 names no code point or byte layout for `WAI_RECEIPT`. This crate's choice, documented in its README, is for the extension to adopt or replace. - **A provisional registration** of the receipt extension's Setup Option. ## How it was tested Every build and test ran on the build host through `rb`: | command | exit | |---|---| | `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) | 0 (test 0, clippy 0) | | `rb --pull <wt> -- 'cd joule-code/joule-code-rs && JOULE_MOQ_WRITE_VECTORS=1 cargo test -p joule-moq --test draft22_vectors'` | 0 | | `rb <wt> -- 'cd joule-code/joule-code-rs && cargo test -p joule-moq --test draft22_roundtrip'` | 0 | | `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'` | 0 (each step 0) | | `rb <wt> -- 'cd joule-code/examples/transport-moqt && cargo test --locked'` | 0 | Local, light Python and shell: - `python3 -I tests/vectors/draft-22/moqt22_check.py`: every vector agrees. - `check-tests-in-ci.py` and its self-test. - `check-workflow-concurrency.sh` and its self-test, and `pr-touches-paths-selftest.sh`. - `check-spec-refs.py` and its self-test. - `check-no-vanity-metrics.sh`, `check-comparative-prose.sh` and `test-public-text-checks.sh`. - `check-wai-section-refs.py` and `check-orphan-sources.py`. All pass. The full `cargo test --workspace` and the public-name check (which runs on the push to main) are left to CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The codec of -22 as a module of its own (draft22), chosen by a Draft enum
whose ALPN is moqt-22: the leading-ones varint of §8.1, Key-Value-Pairs,
Properties and Setup Options, the Authorization Token, every control
message of Table 5 with its parameters, request IDs and stream rules, and
the data plane (subgroup streams, datagrams, fetch streams, padding).
Decoders are bounded and refuse with a typed error carrying the draft's
consequence. The crate does not use it yet.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
- messages: the control plane is -22's; ObjectHeader stays draft-neutral, and
  a layer cap is an OBJECT_PROPERTY_FILTER on the MRL_LAYER property.
- carriage: the crate's objects ride -22 subgroup objects, their receipt,
  the WAI_RECEIPT property (moq-streaming-format §5) and the MRL fields in
  Immutable Properties at application-range code points; a group's receipts
  ride its receipts track (§4.1).
- session: the draft by ALPN, SETUP options, the JWP extension declared by a
  Setup Option, Request IDs per end.
- mock: objects, receipt bundles and requests cross the -22 codec; the relay
  reads an object's delivery timeout from its OBJECT_DELIVERY_TIMEOUT property.
- transport_quic: ALPN moqt-22, a pair of unidirectional control streams,
  a request stream per request, subgroup and fetch streams, datagrams.
- wire: MOQT's integer is the -22 varint; every u64 encodes.
- MOQT_DRAFT_VERSION is 0xff000016.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/vectors/draft-22/vectors.json: varints, every control message type,
control, subgroup, fetch and padding streams, datagrams, and the
WAI_RECEIPT property's value, each with its stated reading; and negative
vectors (cut short, a varint past its frame, unknown types, repeated
parameters, lengths past the draft's limits, flags it forbids, delta and
ID overflows), each with the refusal and consequence it must meet.
tests/draft22_vectors.rs generates it and holds the crate to it, whole and
fed in pieces to the incremental decoder.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/vectors/draft-22/moqt22_check.py, standard library only, written from
the draft's sections: it reads every good vector as the corpus states and
refuses every negative one with the stated kind and consequence, and fails
on an empty corpus.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/draft22_roundtrip.rs: a seeded generator builds valid control
messages of every type, subgroup and fetch streams in both Group Orders,
datagrams and WAI_RECEIPT values; encode, decode, encode is the identity.
The encodings and the corpus, mutated, are fed to every decoder, whole and
incrementally; none may panic.

joule-code-conformance runs the independent decoder over the corpus.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The crate README says which draft is implemented, the readings taken where
-22 leaves an encoding open, the carriage code points and the WAI_RECEIPT
layout, what is not implemented yet, and the vector corpus's JSON view.
The version moves to 0.3.0 because the public API changed; both lockfiles
that record it follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq: clippy's style notes on the draft-22 code
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 46s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 1m27s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m20s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m3s
b247c5a701
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!283
No description provided.