You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make payload compression a per-hop, per-frame decision
Rework compression in both moq-lite and the MoQ Payload Compression
Extension so the publisher only signals *whether* a track is worth
compressing, while the algorithm is negotiated per hop and recorded
per frame/object. A single frame can opt out (e.g. a small JSON
merge-patch delta that DEFLATE would enlarge), and different hops can
use different algorithms.
moq-lite:
- Publisher Compression in TRACK_INFO becomes a boolean hint (reserved
values >1 are treated as 1).
- New SETUP Compression parameter: each endpoint advertises the
algorithms it can decompress, negotiated per hop.
- New per-frame Compression field in FRAME and datagram bodies, present
only when the hint is set, naming the algorithm used (none/deflate).
- New Compression section defining the algorithm IDs (shared with the
extension) and relay behavior, plus compression security notes.
moq-compression:
- COMPRESSION track property becomes a boolean hint.
- Algorithms are advertised in preference order; the hop default is the
receiver's most-preferred mutually-supported algorithm.
- New COMPRESSION_ALGORITHM object property overrides the hop default
per object (typically none, to opt out), keeping the common case free
of per-object signaling.
- Relay, security, and IANA updated (new Object-scope property).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8bLV6vHzucLNvDhPk3bMP
Copy file name to clipboardExpand all lines: draft-lcurley-moq-compression.md
+55-29Lines changed: 55 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -25,9 +25,9 @@ informative:
25
25
--- abstract
26
26
27
27
This document defines a payload compression extension for MoQ Transport {{moqt}}.
28
-
A track-level Compression property lets the original publisher signal that a track's object payloads are worth compressing, and with which algorithm.
29
-
Compression is then applied independently on each hop: a payload is compressed only on a hop that has negotiated the extension and whose receiver supports the algorithm, and is sent verbatim otherwise.
30
-
Each object is compressed independently so objects remain individually decodable, and the decompressed bytes — the actual object — are unchanged end to end.
28
+
A track-level Compression property is a boolean hint by which the original publisher signals that a track's object payloads are worth compressing.
29
+
The algorithm is negotiated independently on each hop, and compression is applied per hop: an object is compressed only on a hop that has negotiated the extension and a shared algorithm, and is sent verbatim otherwise.
30
+
Each object is compressed independently so objects remain individually decodable, an object can opt out when compression would not help, and the decompressed bytes — the actual object — are unchanged end to end.
31
31
32
32
--- middle
33
33
@@ -43,10 +43,10 @@ But MoQ also carries non-media tracks — JSON, text, telemetry, captions, uncom
43
43
For these tracks there is no standard, transport-visible way to compress payloads, so each application reinvents it, and relays cannot help.
44
44
45
45
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.
46
-
What this extension adds on top is an end-to-end *signal*: a track property by which the original publisher marks the content as worth compressing and names the algorithm. The signal travels end-to-end; the compression happens per hop.
46
+
What this extension adds on top is an end-to-end *signal*: a boolean track property by which the original publisher marks the content as worth compressing. The signal travels end to end; the choice of algorithm and the compression itself happen per hop.
47
47
48
-
- **Publisher signals, hops apply**: the COMPRESSION track property is set by the original publisher and carried end-to-end, but a payload is only compressed on a hop that negotiated the extension and whose receiver supports the algorithm. Where the extension is not negotiated, the same payload travels verbatim.
49
-
- **Per object, independently**: each object payload is an independent compressed stream with no shared dictionary or state between objects. This keeps every object individually decodable and avoids head-of-line decoding within a group.
48
+
- **Publisher signals, hops apply**: the COMPRESSION track property is set by the original publisher and carried end to end, but a payload is only compressed on a hop that negotiated the extension and a shared algorithm. Where the extension is not negotiated, the same payload travels verbatim.
49
+
- **Per object, independently**: each object payload is an independent compressed stream with no shared dictionary or state between objects. This keeps every object individually decodable, avoids head-of-line decoding within a group, and lets an individual object opt out of compression when it would not benefit.
50
50
51
51
52
52
# Setup Negotiation
@@ -65,44 +65,69 @@ COMPRESSION Setup Option {
65
65
66
66
**Algorithm**:
67
67
One or more Algorithm identifiers (see [Compression Algorithms](#compression-algorithms)) that the sender can decompress, each a varint, filling the Option Value.
68
+
They are listed in the sender's order of preference, most-preferred first.
68
69
The identifier `none` (0) MUST NOT be listed (it requires no negotiation).
69
70
70
71
A sender MUST NOT compress with an algorithm the receiver did not advertise in its SETUP.
71
-
This makes the on-wire state unambiguous on every hop without any per-object signaling: a receiver decompresses a track's payloads **if and only if** the COMPRESSION track property is present and the receiver advertised that algorithm in its own SETUP. In every other case — the property absent, 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.
72
+
The negotiated algorithm for a hop — the **hop default** — is the first algorithm in the receiver's advertised list that the sender can also produce; if the lists do not intersect, the hop has no default and every payload travels verbatim.
73
+
This keeps the common case free of per-object signaling: where the COMPRESSION track property is present and the hop has a default, a receiver decompresses each object with the hop default unless that object carries a [per-object override](#per-object-override) naming a different algorithm (in particular `none`, to send it verbatim). Where the property is absent, the extension is not negotiated, or the algorithm lists do not intersect, the sender was not permitted to compress, so every payload is verbatim.
72
74
73
75
74
76
# COMPRESSION Track Property
75
-
The COMPRESSION property is the original publisher's signal that a track's object payloads are worth compressing, and which algorithm to use.
77
+
The COMPRESSION property is the original publisher's signal that a track's object payloads are worth compressing.
76
78
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.
77
79
Because the value is a single integer, COMPRESSION uses an even Type so the value is a bare varint:
78
80
79
81
~~~
80
82
COMPRESSION Track Property {
81
83
Type (vi64) = 0xC03D0
82
-
Value (vi64) ; Algorithm identifier
84
+
Value (vi64) ; boolean hint
83
85
}
84
86
~~~
85
87
86
88
**Value**:
87
-
The Algorithm identifier the publisher recommends for this track's payloads.
88
-
The absence of the property, or a value of `none` (0), means the track is not marked for compression and its payloads are always transmitted verbatim.
89
+
A boolean hint: `1`means the track's payloads are worth compressing; `0`, or absence of the property, means they are not and are always transmitted verbatim.
90
+
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.
91
+
The property names no algorithm: which algorithm is used, if any, is the per-hop negotiated [hop default](#setup-negotiation), overridable [per object](#per-object-override).
89
92
90
93
The property is fixed for the lifetime of the track and MUST NOT change.
91
94
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.
92
95
93
-
Compression is enabled only by the combination of this track property and the extension being negotiated on a hop.
94
-
A publisher MUST NOT compress object payloads on a track that does not carry the COMPRESSION property, and there is no way to enable compression on a per-object basis: the property governs the whole track, and on a compressing hop every non-empty payload is compressed.
96
+
Compression is enabled only by the combination of a non-zero hint and the extension being negotiated with a shared algorithm on a hop.
97
+
A publisher MUST NOT compress object payloads on a track that does not carry a non-zero COMPRESSION hint.
95
98
96
-
Whether payloads are actually compressed is decided per hop:
99
+
Whether a given object is actually compressed is decided per hop and per object:
97
100
98
-
- On a hop where the extension is negotiated and the receiver advertised the property's algorithm, every non-empty object payload MUST be compressed with that algorithm, and the receiver decompresses it.
99
-
- On any other hop — the extension not negotiated, or the receiver did not advertise that algorithm — payloads are sent verbatim. The receiver either never sees the property (an ignored unknown Key-Value-Pair) or sees it but knows the sender was not permitted to compress for it, so it treats the payloads as verbatim either way.
101
+
- On a hop where the extension is negotiated and a [hop default](#setup-negotiation) exists, each non-empty object payload is compressed with the hop default and the receiver decompresses it — unless the object carries a [per-object override](#per-object-override), which names the algorithm actually used (including `none`, to send that object verbatim).
102
+
- On any other hop — the extension not negotiated, or the algorithm lists do not intersect — payloads are sent verbatim. The receiver either never sees the property (an ignored unknown Key-Value-Pair) or sees it but knows the sender was not permitted to compress for it, so it treats the payloads as verbatim either way.
100
103
104
+
A sender SHOULD send an object verbatim (via a `none` override) whenever the hop default would not make that object smaller — for example a small JSON merge-patch delta that DEFLATE would enlarge.
101
105
Compression applies to the object payload only; object properties and message framing are never compressed.
102
-
An empty payload (size 0) MUST NOT be compressed and remains empty on the wire.
106
+
An empty payload (size 0) MUST NOT be compressed and remains empty on the wire; it needs no override.
103
107
104
108
A publisher SHOULD set COMPRESSION only for payload types that benefit from it.
105
-
Already-compressed media SHOULD omit it (or use `none`).
109
+
Already-compressed media SHOULD omit it (or use `0`).
110
+
111
+
112
+
# Per-Object Override {#per-object-override}
113
+
The COMPRESSION_ALGORITHM property is an optional object-level Key-Value-Pair that overrides, for a single object, the algorithm a hop would otherwise apply.
114
+
Because the value is a single integer, it uses an even Type so the value is a bare varint:
115
+
116
+
~~~
117
+
COMPRESSION_ALGORITHM Object Property {
118
+
Type (vi64) = 0xC03D2
119
+
Value (vi64) ; Algorithm identifier
120
+
}
121
+
~~~
122
+
123
+
**Value**:
124
+
The [algorithm](#compression-algorithms) actually used for this object's payload on this hop.
125
+
`none`(0) means the object is carried verbatim; any other identifier names the algorithm whose output the payload is.
126
+
A sender MUST NOT name an algorithm the receiver did not advertise in its SETUP.
127
+
128
+
The property is meaningful only on a hop that has a [hop default](#setup-negotiation); where present it replaces the hop default for that object alone, and elsewhere objects are always verbatim and any COMPRESSION_ALGORITHM property MUST be ignored.
129
+
Unlike the boolean COMPRESSION hint, it is not the publisher's end-to-end signal: because it records what a hop actually did, a relay rewrites or removes it to reflect what it did on each downstream hop, exactly as it does for the payload bytes.
130
+
Its typical use is a `none` override that keeps an incompressible or tiny object verbatim while the rest of the track is compressed.
@@ -119,33 +144,33 @@ There is no shared dictionary or state between objects, so each object decompres
119
144
120
145
121
146
# Relay Behavior
122
-
A relay forwards the COMPRESSION track property unchanged — it is the publisher's end-to-end signal — and applies compression independently on each hop.
147
+
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.
123
148
124
-
On its upstream subscription, the relay receives payloads compressed if and only if that hop compressed them (the extension negotiated and the relay advertised the algorithm); it decompresses them as needed.
125
-
On each downstream subscription the relay serves, it compresses payloads with the track's algorithm when that downstream negotiated the extension and advertised the algorithm, and sends them verbatim otherwise.
149
+
On its upstream subscription, the relay receives each object compressed with that hop's default unless the object carried a per-object override; it reads the [COMPRESSION_ALGORITHM](#per-object-override) property, or the hop default in its absence, to decompress as needed.
150
+
On each downstream subscription the relay serves, it compresses each object with that downstream's hop default when one exists and sends objects verbatim otherwise, rewriting or removing the per-object COMPRESSION_ALGORITHM property to reflect what it actually did on that hop.
126
151
127
-
Compression is thus driven by the publisher's track property, not by the relay: a relay does not compress a track the publisher did not mark.
152
+
Compression is thus driven by the publisher's hint and each hop's negotiation, not by the relay's own initiative: a relay does not compress a track the publisher did not mark.
128
153
In every case the decompressed bytes delivered to the application MUST be identical to what the origin published.
129
154
130
155
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.
131
156
132
157
133
158
# Security Considerations
134
159
Compressing data that mixes attacker-controlled and secret content in the same object can leak the secret through compressed size, as in the CRIME and BREACH attacks.
135
-
A publisher MUST NOT set COMPRESSION on a track whose object payloads combine secret material with attacker-influenced material.
160
+
A publisher MUST NOT set a non-zero COMPRESSION hint on a track whose object payloads combine secret material with attacker-influenced material.
136
161
Because compression here is per-object with no cross-object dictionary, the exposure is bounded to within a single object, but it is not eliminated.
137
162
138
163
A malicious sender could emit a small compressed payload that decompresses to a very large buffer (a "decompression bomb").
139
164
A receiver MUST bound the size of a decompressed object payload. If the bound is exceeded it MUST reset the affected Subscribe/Fetch 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 object does not tear down unrelated subscriptions.
140
165
141
-
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`).
166
+
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`).
142
167
143
168
144
169
# IANA Considerations
145
170
146
171
This document requests the following registrations.
147
172
High, distinctive values are requested to avoid the low ranges reserved by {{moqt}} and to minimize collisions with provisional registrations by other extensions; they also avoid the greasing pattern (`0x7f * N + 0x9D`).
148
-
The parameter Type is even so that its value is a bare varint with no length prefix (see {{moqt}} Section 2.5).
173
+
Each Type is even so that its value is a bare varint with no length prefix (see {{moqt}} Section 2.5).
149
174
150
175
## MOQT Setup Options
151
176
@@ -157,11 +182,12 @@ This document requests a registration in the "MOQT Setup Options" registry ({{mo
157
182
158
183
## MOQT Properties
159
184
160
-
This document requests a registration in the "MOQT Properties" registry ({{moqt}} Section 15.8), used for object and track properties.
185
+
This document requests registrations in the "MOQT Properties" registry ({{moqt}} Section 15.8), used for object and track properties.
0 commit comments