Skip to content

Commit 63798e0

Browse files
committed
compression: publisher names the algorithm in the track property
Per implementation feedback, drop the boolean hint + first-intersection selection. The track property (Publisher Compression / COMPRESSION) now names the algorithm the publisher used (none/deflate/zstd); SETUP advertises the decoders each endpoint supports, and the publisher MUST pick an algorithm its peer advertised (deflate mandatory, so always safe). Flagless inference is now off the property: a receiver decompresses iff the property names a non-none algorithm it advertised, else verbatim. Reverts the per-direction selection and the "compress and decompress" wording (the list is decode capability again). Group/subgroup-scoped sliced-stream mechanics, deflate+zstd, and the RFC 7692 / magicless framing trims are unchanged. Relays forward the property unchanged and may recompress only with the same algorithm. Added an explicit "Open issue" note: an immutable algorithm-naming property means a relay can't transcode (e.g. zstd-> deflate) for a downstream that supports only a different algorithm without rewriting the property, which moq-transport forbids — flagged for the working group. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8bLV6vHzucLNvDhPk3bMP
1 parent 3bf34e5 commit 63798e0

2 files changed

Lines changed: 61 additions & 56 deletions

File tree

draft-lcurley-moq-compression.md

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ informative:
2727
--- abstract
2828

2929
This document defines a payload compression extension for MoQ Transport {{moqt}}.
30-
A track-level Compression property is a boolean hint by which the original publisher marks a track's object payloads as good candidates for compression.
31-
The algorithm is negotiated independently on each hop, and compression is applied only on a hop where the publisher's hint is set and the sender and receiver share an algorithm; otherwise payloads travel verbatim.
30+
A track-level Compression property names the algorithm the original publisher used for a track's object payloads.
31+
Endpoints advertise the algorithms they can decode during SETUP, and a payload is compressed on a hop only when the receiver supports the named algorithm; otherwise it is sent verbatim.
3232
Compression is scoped to a subgroup: the object payloads of a subgroup form one compressed stream, sliced back into the individual object payloads so the object framing stays in the clear and the decompressed bytes — the actual objects — are unchanged end to end.
3333

3434
--- middle
@@ -45,17 +45,16 @@ But MoQ also carries non-media tracks — JSON, text, telemetry, captions, uncom
4545
For these tracks there is no standard, transport-visible way to compress payloads, so each application reinvents it, and relays cannot help.
4646

4747
Like HTTP Transfer-Encoding, the on-wire compression is a hop-by-hop optimization: it does not conceptually change the object payload — the decompressed bytes *are* the object — it only changes how those bytes are carried over a single hop.
48-
What this extension adds is an end-to-end *signal* — a boolean track property by which the original publisher marks the content as worth compressing — plus a per-hop negotiation of the algorithm.
4948

50-
- **Publisher signals, hops apply**: the COMPRESSION track property is a boolean set by the original publisher and carried end to end, but a payload is only compressed on a hop that has negotiated a shared algorithm. Which algorithm is used is decided per hop, not by the publisher; a hop that has not negotiated compression forwards the property unchanged so a further-downstream hop can still act on it.
51-
- **Per subgroup, sliced into objects**: within a subgroup the object payloads form one compressed stream, flushed at each object boundary so every object still carries its own payload slice, while the object headers and framing stay in the clear. This keeps the subgroup — the unit a receiver already takes as one ordered, reliable stream — as the unit of compression, while letting relays and caches store payloads compressed and re-frame them without recompressing.
49+
- **Publisher names, hops apply**: the COMPRESSION track property names the algorithm the original publisher used; it is carried end to end and forwarded unchanged. A payload is compressed on a hop only when the receiver advertised that algorithm; otherwise it travels verbatim. Each hop's behavior is fixed by the publisher's algorithm and that hop's negotiation, with no per-object signal.
50+
- **Per subgroup, sliced into objects**: within a subgroup the object payloads form one compressed stream, flushed at each object boundary so every object still carries its own payload slice, while the object headers and framing stay in the clear. This keeps the subgroup — already one ordered, reliable stream — as the unit of compression, and lets relays and caches store payloads compressed and re-frame them without recompressing.
5251

5352

5453
# Setup Negotiation
5554
The Payload Compression extension is negotiated during the SETUP exchange as defined in {{moqt}} Section 10.3.
5655
Unlike a purely additive property, compression MUST be negotiated: a receiver that does not understand the algorithm would otherwise pass the compressed bytes to the application as if they were plaintext.
5756

58-
Each endpoint advertises the algorithms it can decompress, in preference order (most-preferred first), by including the following Setup Option:
57+
Each endpoint advertises the algorithms it can decompress by including the following Setup Option:
5958

6059
~~~
6160
COMPRESSION Setup Option {
@@ -66,60 +65,61 @@ COMPRESSION Setup Option {
6665
~~~
6766

6867
**Algorithm**:
69-
One or more Algorithm identifiers (see [Compression Algorithms](#compression-algorithms)) that the sender can compress and decompress, each a varint, filling the Option Value, most-preferred first.
68+
One or more Algorithm identifiers (see [Compression Algorithms](#compression-algorithms)) that the sender can decompress, each a varint, filling the Option Value.
7069
An endpoint that includes this option MUST list `deflate` (1); the identifier `none` (0) MUST NOT be listed (it requires no negotiation).
7170
An endpoint that does not support the extension omits the option.
7271

73-
Negotiation is per direction and per hop. For a given direction sender-to-receiver, the **selected algorithm** is the first identifier in the receiver's advertised list that also appears in the sender's advertised list; if the lists do not intersect, that direction is verbatim.
74-
Because each endpoint holds both advertised lists once SETUP has been exchanged, both compute the same selection with no further handshake — the receiver's preference governs its own inbound direction, the two directions are independent and MAY select different algorithms, and a simultaneous SETUP exchange creates no ambiguity.
75-
Since `deflate` is mandatory for any endpoint that advertises the option, two endpoints that both support the extension always share at least one algorithm.
76-
Each endpoint lists only algorithms it can both produce and consume, so either side can compute the selection for either direction without a per-object signal.
77-
A sender MUST NOT compress with an algorithm the receiver did not advertise, and MUST NOT compress at all before it has received the receiver's COMPRESSION option.
72+
A sender MUST NOT compress with an algorithm the receiver did not advertise, and MUST NOT compress before it has received the receiver's COMPRESSION option.
73+
This makes the on-wire state unambiguous with no per-object signaling: a receiver decompresses a track's object payloads **if and only if** the COMPRESSION property names a non-`none` algorithm and the receiver advertised that algorithm in its own SETUP.
74+
In every other case — the property absent or `none`, the extension not negotiated, or the algorithm not advertised by the receiver — the sender was not permitted to compress, so the receiver treats the payloads as verbatim.
7875

7976

8077
# COMPRESSION Track Property
81-
The COMPRESSION property is the original publisher's end-to-end signal that a track's object payloads are good candidates for compression.
78+
The COMPRESSION property names the algorithm the original publisher applied to a track's object payloads.
8279
It is a track-level Key-Value-Pair carried with the track's properties (see {{moqt}} Section 2.5 and Section 12), set by the original publisher and forwarded unchanged by relays.
8380
Because the value is a single integer, COMPRESSION uses an even Type so the value is a bare varint:
8481

8582
~~~
8683
COMPRESSION Track Property {
8784
Type (vi64) = 0xC03D0
88-
Value (vi64) ; boolean hint
85+
Value (vi64) ; Algorithm identifier
8986
}
9087
~~~
9188

9289
**Value**:
93-
A boolean hint: `1` means the track's payloads are good candidates for compression, `0` (or absence of the property) means they are not and are always transmitted verbatim.
94-
Values greater than `1` are reserved for future use and MUST be treated as `1` by a receiver that does not understand them, so the hint stays additive.
95-
The property names no algorithm; which algorithm is used, if any, is the per-hop [selected algorithm](#setup-negotiation).
90+
The Algorithm identifier the publisher used for this track's payloads (see [Compression Algorithms](#compression-algorithms)).
91+
The absence of the property, or a value of `none` (0), means the track is uncompressed and its payloads are always transmitted verbatim.
92+
The publisher MUST choose an algorithm that its peer advertised in the [COMPRESSION Setup Option](#setup-negotiation); since `deflate` is mandatory to implement, it is always a safe choice.
9693

9794
The property is fixed for the lifetime of the track and MUST NOT change.
98-
A relay MUST forward it unchanged on every hop, including a hop that has not negotiated the extension: there it is simply an ignored unknown Key-Value-Pair, but forwarding it lets a further-downstream hop that does negotiate the extension still act on the publisher's signal.
95+
A relay MUST forward it unchanged on every hop, including a hop that has not negotiated the extension: there it is simply an ignored unknown Key-Value-Pair, but forwarding it lets a further-downstream hop that does negotiate the extension still act on the publisher's algorithm.
9996

100-
Compression is enabled only by the combination of a non-zero hint and a shared algorithm being negotiated on a hop; there is no per-object or per-subgroup signal on the wire.
101-
A receiver decompresses a track's object payloads **if and only if** the COMPRESSION hint is non-zero and the hop selected an algorithm for that direction, in which case it uses the selected algorithm.
102-
In every other case — the hint absent or zero, the extension not negotiated, or the lists not intersecting — payloads are verbatim.
97+
Whether a payload is actually compressed is decided per hop:
10398

104-
A publisher SHOULD set COMPRESSION only for payload types that benefit from it (e.g. JSON, text, uncompressed binary structures).
105-
Already-compressed media SHOULD omit it (or use `0`).
99+
- On a hop where the receiver advertised the property's algorithm, each non-empty object payload is compressed with that algorithm, and the receiver decompresses it.
100+
- On any other hop — the extension not negotiated, or the receiver did not advertise that algorithm — payloads are sent verbatim, and the receiver treats them as such.
101+
102+
Compression applies to the object payload only; object headers, properties, and message framing are never compressed.
103+
An empty payload (size 0) MUST NOT be compressed and remains empty on the wire.
104+
105+
A publisher SHOULD set COMPRESSION only for payload types that benefit from it.
106+
Already-compressed media SHOULD omit it (or use `none`).
106107

107108

108109
# Compression {#compression}
109-
Compression is applied to object payloads only — object headers, properties, and message framing are never compressed — and is **scoped to a subgroup**.
110-
Within a subgroup the object payloads form a single compressed stream in the [selected algorithm](#setup-negotiation), reset at each subgroup boundary.
110+
Compression is **scoped to a subgroup**.
111+
Within a subgroup the object payloads form a single compressed stream in the algorithm named by the [COMPRESSION property](#compression-track-property), reset at each subgroup boundary.
111112
The stream's output is partitioned at object boundaries: the compressor flushes at the end of each object so that object's slice is exactly the bytes carried as its payload, and the payload length in the object header gives the on-wire (compressed) slice size.
112113
Both algorithms provide a window-retaining flush (DEFLATE's sync flush; Zstandard's `ZSTD_e_flush`), so later objects in a subgroup reuse the compression context and retain cross-object redundancy.
113114

114115
A receiver maintains a single decoder per subgroup, reset at each subgroup boundary, and feeds each object's payload through it in order: the first object of a subgroup starts the decoder fresh — so a receiver joining at a group boundary needs nothing earlier — while later objects build on it.
115-
There is no shared state between subgroups; an empty payload (size 0) contributes nothing to the stream and remains empty on the wire.
116+
There is no shared state between subgroups; an empty payload contributes nothing to the stream.
116117
An object delivered as a datagram is a single-object stream, compressed on its own.
117118

118119
Because the object framing already delimits each slice, an algorithm's own redundant boundary and container bytes are omitted: for `deflate`, the trailing four `00 00 FF FF` bytes a sync flush emits are removed from each payload and the decoder re-inserts them (as in {{RFC7692}}); for `zstd`, the per-subgroup stream uses the magicless frame format and omits the content checksum.
119120

120121
Leaving the framing uncompressed is deliberate.
121122
A relay or cache can hold the object payloads compressed in memory and forward them without inflating, and can re-frame a subgroup — for example to bridge a transport version that changes the subgroup or object headers — without touching the compressed payloads.
122-
Neither is possible if the framing is buried inside the compressed stream.
123123

124124
## Compression Algorithms {#compression-algorithms}
125125
This document defines the following algorithms.
@@ -130,29 +130,32 @@ This document defines the following algorithms.
130130
| 1 | deflate | mandatory | Raw DEFLATE {{RFC1951}}, with no zlib or gzip framing. |
131131
| 2 | zstd | optional | Zstandard {{RFC8878}}. |
132132

133-
Every endpoint that advertises this extension MUST implement `deflate`, so a common algorithm always exists; `zstd` is optional.
133+
Every endpoint that advertises this extension MUST implement `deflate`, so the publisher always has a safe choice; `zstd` is optional.
134134
Further algorithms MAY be registered (see [IANA Considerations](#iana-considerations)).
135135

136136

137137
# Relay Behavior
138-
A relay forwards the boolean COMPRESSION track property unchanged — it is the publisher's end-to-end signal — and applies compression independently on each hop, driven by each hop's negotiation rather than by its own initiative; a relay does not compress a track the publisher did not mark.
138+
A relay forwards the COMPRESSION track property unchanged — it is the publisher's end-to-end signal — and applies compression independently on each hop, driven by each hop's negotiation rather than by its own initiative; a relay does not compress a track the publisher did not mark.
139+
140+
On its upstream subscription the relay receives each subgroup compressed with the property's algorithm (if it advertised that algorithm) or verbatim, and decompresses as needed.
141+
On a downstream subscription that advertised the property's algorithm, it sends each subgroup compressed with that algorithm (recompressing as needed); on one that did not, it sends the subgroup verbatim.
142+
A relay MUST NOT recompress with an algorithm other than the one the property names, because the property tells the receiver how to decode and a relay MUST NOT rewrite it.
143+
In every case the decompressed bytes delivered to the application MUST be identical to what the origin published, and a relay or generic library MUST NOT inspect or modify the decompressed contents unless otherwise negotiated.
139144

140-
On its upstream subscription the relay receives each subgroup compressed with that hop's selected algorithm (or verbatim) and decompresses as needed.
141-
On each downstream subscription it (re)compresses each subgroup with that downstream's selected algorithm, or sends it verbatim when no algorithm is shared.
142-
A relay MAY transcode between algorithms.
143-
In every case the decompressed bytes delivered to the application MUST be identical to what the origin published.
144-
A relay or generic library MUST NOT inspect or modify the decompressed contents unless otherwise negotiated; only recompression that preserves the decompressed bytes exactly is permitted.
145+
Open issue:
146+
because the COMPRESSION property both names the algorithm and is immutable, a downstream that supports a *different* algorithm than the publisher chose (for example only `deflate` when the publisher used `zstd`) receives the payloads verbatim rather than transcoded — a relay cannot offer it the algorithm it does support without rewriting the property, which {{moqt}} forbids.
147+
Whether to relax this — by permitting a relay to rewrite this property for a downstream subscription, or by carrying the per-hop algorithm as transport metadata rather than an end-to-end track property — is an open question for the working group.
145148

146149

147150
# Security Considerations
148151
Compressing data that mixes attacker-controlled and secret content can leak the secret through compressed size, as in the CRIME and BREACH attacks.
149-
A publisher MUST NOT set a non-zero COMPRESSION hint on a track whose object payloads combine secret material with attacker-influenced material.
152+
A publisher MUST NOT set COMPRESSION on a track whose object payloads combine secret material with attacker-influenced material.
150153
Because compression is scoped to a subgroup, the exposure is bounded to within a single subgroup — which may combine several objects, a wider window than a single object — but it is not eliminated.
151154

152155
A malicious sender could emit a small compressed payload that decompresses to a very large buffer (a "decompression bomb").
153156
Because compression is subgroup-scoped, a receiver MUST bound the cumulative decompressed size of a subgroup — not merely each object's payload, since many small payloads can otherwise accumulate without limit. If the bound is exceeded it MUST reset the affected stream (rather than allocate unbounded memory) and MAY close the session with a PROTOCOL_VIOLATION if it considers the peer abusive; the reset is stream-scoped so a single bad subgroup does not tear down unrelated subscriptions.
154157

155-
Compression is orthogonal to {{moqt}} end-to-end encryption: an encrypted payload is effectively incompressible, so a publisher using end-to-end encryption SHOULD omit COMPRESSION (or use `0`).
158+
Compression is orthogonal to {{moqt}} end-to-end encryption: an encrypted payload is effectively incompressible, so a publisher using end-to-end encryption SHOULD omit COMPRESSION (or use `none`).
156159

157160

158161
# IANA Considerations

0 commit comments

Comments
 (0)