MoQ Cluster Extension
draft-lcurley-moq-cluster-00
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Luke Curley | ||
| Last updated | 2026-08-03 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-lcurley-moq-cluster-00
moq L. Curley
Internet-Draft 3 August 2026
Intended status: Informational
Expires: 4 February 2027
MoQ Cluster Extension
draft-lcurley-moq-cluster-00
Abstract
This document defines a clustering extension for MoQ Transport
[moqt], used to build a mesh of relays. Each namespace advertisement
carries the ordered list of Hop IDs it has traversed, starting with
the original publisher, plus the accumulated cost of that path. A
receiver uses the list to detect routing loops and to identify which
advertisements come from the same publisher, and the cost to choose
between paths. Each endpoint declares its own Hop ID during setup,
and the peer uses it to avoid advertising or serving a path that
already passed through that endpoint.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 4 February 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
Curley Expires 4 February 2027 [Page 1]
Internet-Draft moq-cluster August 2026
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Conventions and Definitions . . . . . . . . . . . . . . . . . 2
2. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2
3. Setup Negotiation . . . . . . . . . . . . . . . . . . . . . . 3
3.1. Relay Hops . . . . . . . . . . . . . . . . . . . . . . . 3
3.2. Relay Cost . . . . . . . . . . . . . . . . . . . . . . . 3
4. Hop IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.1. The Reserved Hop ID 0 . . . . . . . . . . . . . . . . . . 4
5. Namespace Advertisements . . . . . . . . . . . . . . . . . . 5
5.1. HOP_PATH Parameter . . . . . . . . . . . . . . . . . . . 5
5.2. ROUTE_COST Parameter . . . . . . . . . . . . . . . . . . 6
6. Relay Behavior . . . . . . . . . . . . . . . . . . . . . . . 6
6.1. Accumulating Cost . . . . . . . . . . . . . . . . . . . . 6
6.2. Updating an Advertisement . . . . . . . . . . . . . . . . 7
7. Path Selection . . . . . . . . . . . . . . . . . . . . . . . 7
8. Security Considerations . . . . . . . . . . . . . . . . . . . 8
9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 9
9.1. MOQT Setup Options . . . . . . . . . . . . . . . . . . . 9
9.2. MOQT Message Parameters . . . . . . . . . . . . . . . . . 9
10. Normative References . . . . . . . . . . . . . . . . . . . . 9
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 10
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 10
1. Conventions and Definitions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
*Upstream* and *downstream* are relative to the flow of an
advertisement, not to the endpoints: the peer that sends an
advertisement is upstream, the one that receives it is downstream.
The same pair of relays can be upstream of each other for different
namespaces.
2. Introduction
[moqt] is designed to deliver content through a mesh of relays, but
is deliberately vague about how that mesh is built, and the base
transport does not carry enough information to build one.
Curley Expires 4 February 2027 [Page 2]
Internet-Draft moq-cluster August 2026
Relays that gossip namespaces with PUBLISH_NAMESPACE quickly break
down: advertisements loop between relays forever, and when two
connections advertise the same namespace, a relay has no basis for
deciding which one to route a SUBSCRIBE toward.
This extension adds the HOP_PATH parameter to PUBLISH_NAMESPACE and
NAMESPACE. It lists every node an advertisement has passed through,
starting with the original publisher, which is enough to break loops
and to compare paths. Each connection also declares its own Hop ID
at SETUP, so loops are avoided even across multiple connections
between the same pair of relays.
Not every route is equal: one crossing a metered backbone costs more
than one inside a datacenter. The RELAY_COST Setup Option prices a
link, defaulting to 1 so an unpriced mesh simply ranks by hop count.
The ROUTE_COST parameter carries the accumulated price per namespace,
and a relay may lower it to advertise that it already has the content
cached, steering subscribers toward a warm copy.
3. Setup Negotiation
3.1. Relay Hops
The extension is negotiated during the SETUP exchange ([moqt]
Section 10.3). An endpoint indicates support with the following
Setup Option, whose value is its own Hop ID:
RELAY_HOPS Setup Option {
Option Key (vi64) = 0x40B55
Option Value Length (vi64)
Hop ID (vi64)
}
Negotiation is per session; a relay MUST NOT assume that because one
of its sessions negotiated Relay Hops, another did. It also enables
the extended NAMESPACE message (Section 5), which is what lets a
NAMESPACE carry these parameters at all.
On a session that negotiated the extension, an endpoint MUST include
HOP_PATH on every PUBLISH_NAMESPACE and NAMESPACE it sends, and a
receiver MUST close the session with a PROTOCOL_VIOLATION if one
arrives without it.
3.2. Relay Cost
The client MAY declare what this link costs to cross; a server MUST
NOT send this option, so one side owns the price:
Curley Expires 4 February 2027 [Page 3]
Internet-Draft moq-cluster August 2026
RELAY_COST Setup Option {
Option Key (vi64) = 0x40B56
Option Value (vi64)
}
Both endpoints add the client's value to the ROUTE_COST of every
advertisement they receive over the connection, so the link is priced
the same in both directions. An absent option means 1, under which
the accumulated cost equals the hop count. 0 is meaningful and
distinct from absent: it makes the link free, which is how a
deployment describes two relays in the same datacenter.
4. Hop IDs
A *Hop ID* is a variable-length integer identifying one endpoint
within an advertisement's path.
Hop IDs SHOULD be unique among the endpoints an advertisement can
traverse. An endpoint MAY generate one randomly, since collisions
across a 64-bit space are unlikely, or use a stable configured
identifier that survives restarts.
Loop detection and origin identification compare Hop IDs for
equality, so two endpoints sharing a Hop ID are indistinguishable.
Redundant publishers producing interchangeable content MAY share one
deliberately, so a receiver treats their paths as failover options
for the same content (Section 7).
4.1. The Reserved Hop ID 0
*0 means "no identity"* and is reserved. It is used for an endpoint
that did not negotiate this extension, and an endpoint MAY also
declare 0 to withhold its identity.
Because any number of endpoints can be 0, it identifies nothing,
which constrains all three uses:
* *Loop detection*: 0 in a HOP_PATH is never a loop. A receiver
whose own Hop ID is 0 cannot detect loops through itself, and MUST
NOT discard an advertisement merely because the path contains 0.
* *Origin identity*: an advertisement whose first entry is 0 has an
unknown origin. A receiver MUST NOT treat two such advertisements
as interchangeable (Section 7).
* *Filtering*: a peer that declared 0 excludes nothing, so the
sender applies no filter to that session.
Curley Expires 4 February 2027 [Page 4]
Internet-Draft moq-cluster August 2026
Duplicate _non-zero_ Hop IDs in one HOP_PATH are a loop; duplicate
zeros are not. Declaring 0 therefore trades loop detection,
failover, and update continuity (Section 6.2) for anonymity.
5. Namespace Advertisements
This extension carries HOP_PATH and ROUTE_COST as Key-Value-Pair
parameters ([moqt] Section 2.5). PUBLISH_NAMESPACE ([moqt]
Section 10.15) already has a Parameters field.
NAMESPACE ([moqt] Section 10.16) does not, and a subscriber-driven
mesh propagates advertisements as NAMESPACE messages, so this
extension defines an extended form used only on a session that
negotiated Relay Hops:
NAMESPACE Message (Relay Hops) {
Type (vi64) = 0x8,
Length (16),
Track Namespace Suffix (..),
Number of Parameters (vi64),
Parameters (..) ...
}
The appended fields are encoded exactly as in PUBLISH_NAMESPACE. An
endpoint MUST NOT append them on a session that did not negotiate the
extension.
NAMESPACE_DONE ([moqt] Section 10.17) carries no state from this
extension and is not extended.
5.1. HOP_PATH Parameter
HOP_PATH is the ordered list of Hop IDs an advertisement has
traversed, from the original publisher to the relay immediately
upstream of the receiver:
HOP_PATH Parameter {
Type (vi64) = 0x40B57
Length (vi64)
Hop ID (vi64) ...
}
The list always has at least one entry, the original publisher, which
is 0 if that publisher is unknown (Section 4.1). A receiver MUST
close the session with a PROTOCOL_VIOLATION if the entries do not
exactly fill Length, if the list is empty, or if a non-zero Hop ID
appears twice.
Curley Expires 4 February 2027 [Page 5]
Internet-Draft moq-cluster August 2026
5.2. ROUTE_COST Parameter
ROUTE_COST is the marginal cost of subscribing via this
advertisement: the price of the transfers a new subscription would
actually cause.
ROUTE_COST Parameter {
Type (vi64) = 0x40B58
Value (vi64)
}
It is OPTIONAL and absent means 0, so an endpoint that prices nothing
sends nothing. Costs still accumulate across such a mesh, because
each receiver adds its own link's price (Section 3.2) regardless.
The original publisher seeds the value with its production cost: 0
for content it is already producing, higher for content it would have
to spin up on demand, such as a standby transcoder advertising
everything it _could_ serve.
6. Relay Behavior
When forwarding an advertisement downstream, a relay MUST append its
own Hop ID to the HOP_PATH it received, so its own ID is always the
last entry. An advertisement arriving from an upstream that did not
negotiate the extension has no HOP_PATH; the relay creates one
containing a single 0 for that upstream (Section 4.1), then appends
its own.
On receipt, a relay MUST discard an advertisement whose HOP_PATH
already contains its own non-zero Hop ID: forwarding it would extend
a loop, and subscribing through it would route the relay back to
itself. This receiver-side check catches loops of any length and is
the only loop defense required.
6.1. Accumulating Cost
A relay MUST add the session's link cost (Section 3.2) to the
ROUTE_COST it received before forwarding or acting on an
advertisement. The addition MUST saturate rather than wrap, so an
absurd upstream value ranks last instead of overflowing to best.
A relay actively carrying the namespace (a live subscription exists
for at least one of its tracks) SHOULD advertise 0 instead of the
accumulated value: its ingress is already paid for, so one more
subscriber costs only the links below it. This is what lets a
cluster deduplicate onto a warm copy. The discount applies only to
the advertisement for the path it actually serves from; a standby
Curley Expires 4 February 2027 [Page 6]
Internet-Draft moq-cluster August 2026
path keeps its accumulated value, since serving from it means opening
a fresh ingest. When it stops carrying the namespace it SHOULD
restore the accumulated value, optionally after a grace period so
brief churn does not flap routing.
Two relays that independently begin carrying the same namespace would
each see the other's 0 as cheaper than its own source, and both
switching at once would leave the namespace with no source. Before
re-parenting onto a 0-cost advertisement from another actively-
carrying relay (one whose HOP_PATH has two or more entries), a relay
SHOULD apply a deterministic tie-break, such as comparing a hash of
the namespace and each Hop ID, so exactly one side moves. Equal Hop
IDs (including two relays that both declared 0) cannot be ordered,
and neither side SHOULD move. Cheaper advertisements from anything
else carry no such hazard and SHOULD be adopted immediately.
6.2. Updating an Advertisement
An endpoint updates an advertisement by re-sending it with new
parameters *on the stream that already carries it*: the original
PUBLISH_NAMESPACE request stream, or the SUBSCRIBE_NAMESPACE response
stream the NAMESPACE arrived on. A receiver MUST NOT treat the
repeat as a duplicate or a protocol violation.
In [moqt] an advertisement lives for the lifetime of its stream, so
an update on a _new_ stream would leave two streams claiming one
namespace and let the superseded one retract its replacement. An
endpoint MUST NOT open a second stream for a namespace it already
advertises on this session.
Replacement is atomic, so a receiver MUST NOT tear down subscriptions
or drop cached state merely because an update arrived. What it means
for existing subscriptions follows the first HOP_PATH entry
(Section 7): unchanged and non-zero, the content is continuous and
subscriptions MAY resume on the new route at a group boundary;
changed or 0 (Section 4.1), a different publisher may have taken over
and they do not carry over.
The expected case is a ROUTE_COST-only change, which is how a relay
signals that it started or stopped carrying the namespace.
7. Path Selection
A receiver holding advertisements for the same namespace over several
sessions SHOULD prefer the lowest ROUTE_COST, breaking ties toward
the shorter HOP_PATH and then toward the most recently received.
This is advisory: a receiver MAY apply local policy such as measured
RTT instead.
Curley Expires 4 February 2027 [Page 7]
Internet-Draft moq-cluster August 2026
Two advertisements whose HOP_PATH begins with the same non-zero Hop
ID share a publisher and carry interchangeable content, so a receiver
MAY hold them as redundant paths and fail an active subscription over
to the survivor. If the first entries differ, or either is 0, they
are distinct publishers reusing a namespace: a receiver MUST NOT
treat them as interchangeable and SHOULD treat the later as replacing
the earlier.
A publisher SHOULD advertise, per session, the best path whose
HOP_PATH does not contain the Hop ID that peer declared, and SHOULD
advertise nothing when every known path contains it. Because
selection is per session, a peer that the serving path flows through
still receives the best standby, which is what lets it fail over if
its own copy dies.
When serving a subscription, a publisher MUST select the source by
that same rule. If only excluded sources remain the subscription is
unroutable, since serving it would hand the subscriber data that
already flowed through itself. Applying one rule to both
advertisement and dispatch keeps advertised paths truthful and
prevents subscription cycles of any length.
8. Security Considerations
A Hop ID reveals nothing beyond what its operator encodes in it, and
a deployment that considers its identifiers sensitive can use random
values or declare 0 (Section 4.1). A HOP_PATH does expose how many
hops an advertisement crossed, which hints at the size of a
deployment; a relay MAY coalesce its internal hops into one entry, or
strip HOP_PATH, before forwarding across a trust boundary.
Because a relay only appends to HOP_PATH, it cannot make a competing
path look shorter than it is; the worst it can do is under-report its
own upstream portion to win an advisory tie-break. ROUTE_COST has no
such structural protection: it is a single value the sender chooses,
so a relay can advertise 0 for content it is not carrying and attract
subscriptions it then has to fetch. Both cost only a suboptimal path
choice, and the latter is self-limiting, since the traffic won this
way must then be served.
A receiver MUST NOT make security decisions based on Hop IDs, and a
deployment spanning a trust boundary SHOULD treat a peer's ROUTE_COST
as a hint to clamp or ignore rather than an accounting figure.
Curley Expires 4 February 2027 [Page 8]
Internet-Draft moq-cluster August 2026
9. IANA Considerations
This document requests the following registrations. High,
distinctive values are requested to avoid the low ranges reserved by
[moqt] and to minimize collisions with provisional registrations by
other extensions.
9.1. MOQT Setup Options
This document requests two registrations in the "MOQT Setup Options"
registry ([moqt] Section 15.4), whose policy is Specification
Required.
+=========+============+===============+
| Value | Name | Reference |
+=========+============+===============+
| 0x40B55 | RELAY_HOPS | This Document |
+---------+------------+---------------+
| 0x40B56 | RELAY_COST | This Document |
+---------+------------+---------------+
Table 1
9.2. MOQT Message Parameters
This document requests two registrations in the "MOQT Message
Parameters" registry ([moqt] Section 15.7). Both are carried in
PUBLISH_NAMESPACE and in the extended NAMESPACE message (Section 5).
+=========+============+==============================+===========+
| Value | Name | Carried In | Reference |
+=========+============+==============================+===========+
| 0x40B57 | HOP_PATH | PUBLISH_NAMESPACE, NAMESPACE | This |
| | | | Document |
+---------+------------+------------------------------+-----------+
| 0x40B58 | ROUTE_COST | PUBLISH_NAMESPACE, NAMESPACE | This |
| | | | Document |
+---------+------------+------------------------------+-----------+
Table 2
The Key-Value-Pair parity is load-bearing: HOP_PATH and RELAY_HOPS
are odd, so their values are length-prefixed byte strings, while
ROUTE_COST and RELAY_COST are even, so their values are bare varints.
10. Normative References
Curley Expires 4 February 2027 [Page 9]
Internet-Draft moq-cluster August 2026
[moqt] Nandakumar, S., Vasiliev, V., Swett, I., and A. Frindell,
"Media over QUIC Transport", Work in Progress, Internet-
Draft, draft-ietf-moq-transport-19, 6 July 2026,
<https://datatracker.ietf.org/doc/html/draft-ietf-moq-
transport-19>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/rfc/rfc2119>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/rfc/rfc8174>.
Acknowledgments
This document was drafted with the assistance of Claude, an AI
assistant by Anthropic.
Author's Address
Luke Curley
Email: kixelated@gmail.com
Curley Expires 4 February 2027 [Page 10]