MoQT Draft-22: What Changed Since Draft-21
By moqtap team
MoQT Draft-22: What Changed Since Draft-21
Draft-ietf-moq-transport-22 was published on 1 October 2026, about three weeks after draft-21 (8 September). Draft-21 was a restructure that left every byte of draft-20 alone. Draft-22 changes the wire in exactly one place, the LOCATION_FILTER parameter. The rest of the revision is editorial, though a lot of text moves.
This post is checked against the published HTML of draft-21 and draft-22, and against the draft’s own change log (Appendix A.1).
At a glance
Wire change
LOCATION_FILTER(0x21) carries an explicit Location Filter Type that selects which fields follow. It replaces the length-inferred encoding of draft-20 and draft-21 (#1953).- As a result, a start of
{0, 0}is now an absolute start at the beginning of the track, and the Next Object has its own type,0x05.
Editorial, no wire effect
- Publisher and Namespace Discovery is restructured, Namespace Prefix Matching gets its own section, and
SUBSCRIBE_TRACKSmoves to Publishing and Receiving Tracks (#1946). - A new Fetch semantics section covers object delivery, gaps (including Descending Group Order) and relay handling (#1895).
- Forwarding Preference is renamed Delivery Mode (#1886, #1891, #1914).
- The Forward State is described as a subscription being paused (#1940).
- Per-request shorthand names such as
SUBSCRIBE_ERRORstand forREQUEST_OKandREQUEST_ERROR(#1912). - Each control message lists the parameters it allows (#1916).
- Smaller clarifications: Largest Object, the inclusive FETCH_OK End Location, extension support in SETUP, the auth token cache overflow rule, and relay-to-relay communication.
Every message type, parameter type, setup option, property, error code and object status keeps its value. Apart from LOCATION_FILTER, every message, data-stream header and datagram layout is byte-identical to draft-21. The ALPN is moqt-22.
1. LOCATION_FILTER gets a type, and loses its length
Draft-21 defined the parameter in §9.20.10 as length-prefixed. Draft-22 defines it in §9.20.9 (the parameter sections are renumbered, see below) with a type where the length used to be:
draft-21 draft-22LOCATION_FILTER Parameter { LOCATION_FILTER Parameter { Parameter Type (vi64) = 0x21, Parameter Type (vi64) = 0x21, Length (vi64), Location Filter Type (vi64), [StartGroup (vi64),] [StartGroup (vi64),] [StartObject (vi64),] [StartObject (vi64),] [EndGroupDelta (vi64),] [EndGroupDelta (vi64),] [EndObject (vi64),] [EndObject (vi64),]} }In draft-22, “The Location Filter Type dictates which optional variable-length integer fields follow, and how they are interpreted.” The new Table 6, Location Filter forms, gives the six types:
| Type | Name | Fields that follow | Start | End |
|---|---|---|---|---|
0x00 | None | none | no filter | no filter |
0x01 | Relative Start | StartGroup | {Largest Object.Group + 1 - StartGroup, 0} | open-ended |
0x02 | Absolute Start | StartGroup, StartObject | {StartGroup, StartObject} | open-ended |
0x03 | Absolute Start, Group End | StartGroup, StartObject, EndGroupDelta | {StartGroup, StartObject} | last Object of Group StartGroup + EndGroupDelta |
0x04 | Absolute Range | StartGroup, StartObject, EndGroupDelta, EndObject | {StartGroup, StartObject} | {StartGroup + EndGroupDelta, EndObject} |
0x05 | Next Object | none | Next Object | open-ended |
“Any other Location Filter Type is a PROTOCOL_VIOLATION.”
Several rules carry over unchanged: the relative-start formula and its examples (StartGroup = 0 starts at the Next Group), the clamp to [0, 2^64 - 1], the PROTOCOL_VIOLATION when StartGroup + EndGroupDelta exceeds 2^64 - 1, and an omitted end meaning open-ended on a subscription but Largest Object on a Fetch. So do the scope (FETCH, SUBSCRIBE, PUBLISH, REQUEST_UPDATE for a subscription, and PUBLISH_STATE_NOTIFY) and the omission rule: absent from FETCH or SUBSCRIBE means unfiltered, and absent from REQUEST_UPDATE or PUBLISH_STATE_NOTIFY means unchanged.
Four sentences of draft-21 are deleted:
- “Length (in bytes) determines how many optional vi64 fields are present.”
- “A length of 0 indicates no filter, for example to remove the filter in REQUEST_UPDATE.”
- “If only StartGroup and StartObject are present and both 0, the start Location is the Next Object (see Section 3.1.3).”
- “An open-ended filter that starts at absolute Location 0 is equivalent to unfiltered, so the subscriber need not include a Location filter.”
0 changes meaning
The third deletion is the semantic break. In draft-21, a two-field filter of {0, 0} meant the Next Object. In draft-22, type 0x02 with {0, 0} is an absolute start at the beginning of the track, and only type 0x05 means the Next Object.
| Meaning | draft-21 bytes | draft-22 bytes |
|---|---|---|
| Next Object | 21 02 00 00 | 21 05 |
Absolute start at {0, 0} | not expressible | 21 02 00 00 |
Absolute start at {10, 3}, StartGroup in two bytes | 21 03 80 0a 03 | 21 02 80 0a 03 |
The first row is the trap. 21 02 00 00 is valid in both drafts and means something different in each, so a draft-21 encoder carried over unchanged into a draft-22 implementation asks for the whole track when it meant “from now on”.
The same coincidence makes many filters byte-identical across the two drafts. When every field is a one-byte varint, draft-21’s Length of N equals draft-22’s type N for N = 0 to 4. Test data built only from small values therefore cannot tell a draft-21 decoder from a draft-22 one. The discriminators are type 0x05 (alone, or followed by another parameter, which a draft-21 parser would swallow as five bytes of fields), a multi-byte or non-minimal field, a type of 0x06 or above (a PROTOCOL_VIOLATION in draft-22, a length in draft-21), and the {0, 0} start, which decodes in both but means different things.
Why a parser has to know 0x21
Message Parameters were never Key-Value-Pairs. §9.20 frames one as a Type Delta followed by a Value whose “encoding is specified by each parameter definition”, and the block is “bounded by a parameter count rather than a length”. The draft-22 LOCATION_FILTER matches none of the generic encodings §9.20 lists (uint8, varint, Location, Length-prefixed, Track Namespace). It is self-delimiting: one type varint, then zero to four varints chosen by the type.
A parameter walker that treats 0x21 as length-prefixed will read the Filter Type as a byte count and lose its place. A type 0x05 filter, for example, would swallow the next five bytes. This applies inside FILL_PARAMETERS (0x23) too. That parameter is still length-prefixed, but the LOCATION_FILTER nested in it uses the new form.
One sentence in §3.4 Fill Semantics was not updated: it still says the fill range is the entire track “when … the LOCATION_FILTER inside FILL_PARAMETERS is zero-length”. Draft-22 has no length to be zero, so read it as a nested filter of type 0x00.
Removing a filter in REQUEST_UPDATE
Draft-21’s example of a zero length removing the filter in a REQUEST_UPDATE is gone. The rule around it is not new: drafts 20 and 21 already said “There is no mechanism to remove a parameter from a request”, and draft-22 §9.5 only adds a word, “There is no generic mechanism to remove a parameter from a request.” Type 0x00, defined as “no fields follow and there is no Location Filter”, is the parameter’s own way of saying no filter.
2. Editorial changes
None of these change a byte, but they move the text you cite.
- Renumbering. §9.20.2 Allowed Parameters By Control Message is deleted, so every later parameter section moves down one: FILL_TIMEOUT is §9.20.5,
LOCATION_FILTERis §9.20.9, FILL_PARAMETERS is §9.20.15, and FORWARD is §9.20.18. Figures are renumbered too, and draft-21’s Tables 6 to 21 become 7 to 22 around the new Table 6, Location Filter forms. - Per-message parameter lists (#1916). §9.20.1 now says that “each control message definition lists the parameters it allows”. For example, §9.14 states that “The only parameter that can appear in a PUBLISH_NAMESPACE is AUTHORIZATION_TOKEN.”
- Discovery restructured (#1946). Section 4 is now Publisher and Namespace Discovery: §4.1 Publishing Namespaces, §4.2 Subscribing to Namespaces, and a new §4.3 Namespace Discovery Example. Prefix matching moves to its own §2.4.2, and
SUBSCRIBE_TRACKSbehaviour is in §3.6 Subscribing to Tracks by Prefix. PUBLISH_NAMESPACE’s field is renamed Track Namespace Prefix, “matched as a prefix”, with the bytes unchanged. - Fetch semantics (#1895). New §3.2.1 Fetch Object Delivery, §3.2.2 Gaps in a Fetch Stream and §3.2.3 Relay Fetch Handling. Behaviour that used to live in the FETCH and FETCH_OK message sections now lives here.
- Delivery Mode (#1886, #1891, #1914). “Forwarding Preference” becomes “Delivery Mode”, established by the Original Publisher.
- Paused subscriptions (#1940). A new §3.1.1 Pausing Subscriptions describes the Forward State as a subscription being paused. FORWARD (
0x10) is unchanged on the wire. - Response shorthands (#1912). New §1.5 Response Message Naming defines names such as
SUBSCRIBE_ERRORandPUBLISH_OKfor aREQUEST_ERRORorREQUEST_OKsent in response to that request type. They are names only: no new message types. - Definitions and clarifications. Subscription and Fetch are defined (#1884, #1925). Largest Object can still be arriving (#1911, #1918). The FETCH_OK End Location is inclusive (#1910). Extensions declare support in SETUP (#1921). The auth token cache overflow rule applies only to registrations outside SETUP (#1927). Relay-to-relay communication is out of scope (#1931).
What this means for implementers
- Re-encode
LOCATION_FILTERfor draft-22. Write the type, not a length, and stop length-prefixing aLOCATION_FILTERnested inFILL_PARAMETERS. - Teach your parameter walker
0x21. It cannot be skipped as a length-prefixed value in draft-22. - Send
21 05for the Next Object. The draft-21 encoding,21 02 00 00, now asks for the start of the track. - Reject types
0x06and above withPROTOCOL_VIOLATION. - Re-derive your test data rather than relabelling it. Most small-valued draft-21 filters decode under draft-22 to the same filter, so they prove nothing, and the one that does not,
{0, 0}, changes meaning silently. - Update section citations. §9.20.N is now §9.20.(N-1) for the parameters, and behaviour that used to be cited to §9.11, §9.12, §9.15 or §9.18 now lives in Sections 3 and 4.
moqtap support
Draft-22 support ships in:
- @moqtap/codec 0.13.0: subpath
@moqtap/codec/draft22, drafts 07–22. - @moqtap/collector 0.3.0:
@moqtap/collector/draft22, drafts 07–22. - @moqtap/test-vectors 0.18.0: a new
draft22/directory of 335 vectors, 59 of them negative (4,099 and 815 across all drafts). TheLOCATION_FILTERvectors are re-derived for the new encoding, including the cases above that tell the two drafts apart. - Rust crates moqtap-codec, moqtap-client and moqtap-proxy 0.8.0: feature
draft22, drafts 07–22. - WebTransport Inspector 0.6.0: decodes drafts 07–22.
The moqtap CLI still covers drafts 07 through 20.
The rendered spec is at /spec/draft-22, and the per-draft code point pages are at /reference/message-types/draft-22/ and /reference/error-codes/draft-22/. For the history of the filter encoding across every draft, see Subscribing to tracks and the SUBSCRIBE message.