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_TRACKS moves 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_ERROR stand for REQUEST_OK and REQUEST_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-22
LOCATION_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:

TypeNameFields that followStartEnd
0x00Nonenoneno filterno filter
0x01Relative StartStartGroup{Largest Object.Group + 1 - StartGroup, 0}open-ended
0x02Absolute StartStartGroup, StartObject{StartGroup, StartObject}open-ended
0x03Absolute Start, Group EndStartGroup, StartObject, EndGroupDelta{StartGroup, StartObject}last Object of Group StartGroup + EndGroupDelta
0x04Absolute RangeStartGroup, StartObject, EndGroupDelta, EndObject{StartGroup, StartObject}{StartGroup + EndGroupDelta, EndObject}
0x05Next ObjectnoneNext Objectopen-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.

Meaningdraft-21 bytesdraft-22 bytes
Next Object21 02 00 0021 05
Absolute start at {0, 0}not expressible21 02 00 00
Absolute start at {10, 3}, StartGroup in two bytes21 03 80 0a 0321 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_FILTER is §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_TRACKS behaviour 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_ERROR and PUBLISH_OK for a REQUEST_ERROR or REQUEST_OK sent 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

  1. Re-encode LOCATION_FILTER for draft-22. Write the type, not a length, and stop length-prefixing a LOCATION_FILTER nested in FILL_PARAMETERS.
  2. Teach your parameter walker 0x21. It cannot be skipped as a length-prefixed value in draft-22.
  3. Send 21 05 for the Next Object. The draft-21 encoding, 21 02 00 00, now asks for the start of the track.
  4. Reject types 0x06 and above with PROTOCOL_VIOLATION.
  5. 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.
  6. 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:

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.