Skip to main content

MoQ Cluster Extension
draft-lcurley-moq-cluster-00

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]