<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-ramadan-moq-multicast-01" submissionType="IETF" category="std" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" indexInclude="true">

<front>
<title abbrev="MoQ Multicast">Multicast Delivery and Endpoint Discovery for Media over QUIC</title><seriesInfo value="draft-ramadan-moq-multicast-01" stream="IETF" status="standard" name="Internet-Draft"></seriesInfo>
<author initials="O." surname="Ramadan" fullname="Omar Ramadan"><organization>Blockcast</organization><address><postal><street></street>
</postal><email>omar@blockcast.net</email>
</address></author><date year="2026" month="October" day="26"></date>
<area>wit</area>
<workgroup>moq</workgroup>
<keyword>multicast</keyword>
<keyword>MoQ</keyword>
<keyword>AMT</keyword>
<keyword>SSM</keyword>
<keyword>TreeDN</keyword>

<abstract>
<t>This document specifies multicast delivery mechanisms and catalog-based
endpoint discovery for Media over QUIC (MoQ).  It defines how MoQ
sessions integrate with IP multicast (SSM, ASM), Automatic Multicast
Tunneling (AMT), and TreeDN for scalable live streaming.  The
specification includes a multicast catalog extension for endpoint
discovery and multi-path delivery across TV, mobile, and browser
platforms.  All multicast delivery uses MPEG Media Transport Protocol
(MMTP) packets, the same packet format used on MoQ QUIC streams and
datagrams.  A manifest-based content-authentication profile protects
multicast delivery.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>Unicast delivery, including Media over QUIC (MoQ)
<xref target="I-D.ietf-moq-transport"></xref>, replicates a stream per receiver; IP
multicast replicates it in the network.</t>
<t>This document specifies how MoQ integrates with IP multicast to
combine the scalability of multicast with the reliability and
signaling of QUIC:</t>

<ol spacing="compact">
<li><strong>Delivery paths</strong>: Platform-specific multicast reception for
TV, mobile, and browser clients</li>
<li><strong>Catalog extension</strong>: A multicast endpoint discovery mechanism
for MoQ catalogs <xref target="I-D.ietf-moq-msf"></xref>.  Endpoints carry mmtp tracks
and their repair tracks, and a loc or cmaf track reaches multicast
through an mmtp rendition track (Section 4.1).</li>
<li><strong>MMTP wire format</strong>: All multicast delivery uses MMTP packets --
the same packet format that MoQ objects carry on QUIC streams
(Section 5).</li>
<li><strong>Multi-path delivery</strong>: Combining MoQ unicast and multicast for
failover and FEC symbol deduplication</li>
</ol>
<t>These mechanisms follow TreeDN <xref target="RFC9706"></xref>, which gives an
incremental path to native multicast.  A network that supports
multicast delivers the stream to its receivers as native SSM.  A
receiver on a unicast-only network reaches the same stream through an
Automatic Multicast Tunneling (AMT) relay <xref target="RFC7450"></xref> that it finds
in its own network, from a relay service hostname (Section 3.1), or
with DNS Reverse IP AMT Discovery (DRIAD) <xref target="RFC8777"></xref>.  Section 4.2.1
describes how a catalog signals both.  Each network can therefore
turn on multicast when it is ready.  Until then, its receivers that
have UDP socket access (Section 3) use AMT, and the others stay on
MoQ unicast.  An AMT relay can replicate packets in router forwarding
hardware or in a kernel data plane, so relay fan-out does not cost a
server per receiver.</t>

<section anchor="multicast-as-supplement-to-moq-unicast"><name>Multicast as Supplement to MoQ Unicast</name>
<t>Multicast delivery as defined in this document is a supplement to
MoQ/QUIC unicast.  Subscribers MUST establish a MoQ session first
to receive the catalog, negotiate parameters, and receive initial
media.  Multicast MAY then be used as an additional delivery path
for ongoing media data.  This ensures that codec configuration is
available before multicast reception begins.</t>
<t>Exception: MMTP-packaged streams <xref target="MOQ-MMT"></xref> delivered via ATSC 3.0
broadcast <xref target="ATSC-A331"></xref> or native SSM MAY operate as unidirectional
data streams without a MoQ session.  MMTP carries per-packet routing
(packet_id), timing (timestamp), sequencing (Packet Sequence Number),
and FEC framing (FEC Type, FEC payload IDs) natively, and its
signaling messages identify the Assets in band: every Asset sent to a
multicast endpoint is named by a PA message on its sending entity's
packet_id 0 signaling sub-flow, whose MP table locates the Asset by
source address, group address, port and packet_id (Section 5).  A
receiver without the catalog
takes the FEC configuration from the AL-FEC message
(<xref target="MOQ-MMT"></xref> Section 8.3) when the flow carries one.  A receiver
that holds the catalog takes both from the catalog, which remains
authoritative for media and FEC parameters (Section 4.1;
<xref target="MOQ-FEC"></xref> Section 5.2).  A catalog-provided service name or relay
address is only a discovery input; it is not proof that an upstream
is reachable or healthy.
ATSC 3.0 and ARIB STD-B60 <xref target="ARIB-B60"></xref> receivers consume MMTP over
SSM as their native delivery path.</t>
</section>
</section>

<section anchor="terminology"><name>Terminology</name>
<t>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;,
&quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;,
and &quot;OPTIONAL&quot; in this document are to be interpreted as described
in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref> when, and only when, they appear
in all capitals, as shown here.</t>
<t><strong>SSM</strong>: Source-Specific Multicast <xref target="RFC4607"></xref></t>
<t><strong>AMT</strong>: Automatic Multicast Tunneling <xref target="RFC7450"></xref></t>
<t><strong>DRIAD</strong>: DNS Reverse IP AMT Discovery <xref target="RFC8777"></xref></t>
<t><strong>TreeDN</strong>: Tree-based Content Delivery Network <xref target="RFC9706"></xref></t>
<t><strong>Service hostname</strong>: A stable DNS name identifying an upstream MoQ
relay or AMT relay service.  It is the receiver's connection identity;
the A and AAAA records returned for it are candidate addresses, not
stable endpoint identities.</t>
<t><strong>Protocol health</strong>: Successful completion of the protocol-specific
handshake and capability checks required before a receiver selects an
upstream.  A DNS response, a routable address, or a transport handshake
alone is not protocol health.</t>
<t><strong>MMTP</strong>: MMT Protocol -- the packet layer of MPEG Media Transport
(<xref target="ISO.23008-1"></xref> Clause 9; <xref target="MOQ-MMT"></xref> Section 3).</t>
<t><strong>MMT sending entity</strong>: The entity that packetizes media into MMTP
packets and sends them on one or more multicast endpoints.  It is
the scope over which Section 4.1 assigns each <tt>packetId</tt> value to
exactly one MMTP packet sub-flow, and it serves exactly one MoQ
namespace (Section 4.1).</t>
</section>

<section anchor="delivery-paths"><name>Delivery Paths</name>
<t>MoQ content can reach receivers via multiple delivery paths depending
on platform capabilities:</t>
<table>
<thead>
<tr>
<th>Client Type</th>
<th>Multicast Path</th>
</tr>
</thead>

<tbody>
<tr>
<td>Native (TV, mobile)</td>
<td>SSM direct via OS multicast API</td>
</tr>

<tr>
<td>Native (no multicast)</td>
<td>AMT tunneling over UDP <xref target="RFC7450"></xref></td>
</tr>

<tr>
<td>Browser with UDP socket access (e.g., <xref target="WICG-DirectSockets"></xref>)</td>
<td>SSM/AMT</td>
</tr>

<tr>
<td>Browser (standard)</td>
<td>MoQ/WebTransport (unicast)</td>
</tr>
</tbody>
</table><t>Both native SSM and AMT tunneling require UDP socket access.
MoQ/WebTransport is the fallback that every client supports and
requires no special platform capabilities.</t>
<t>Broadcast receivers (ATSC 3.0, ARIB STD-B60) consume MMTP streams
natively over RF tuner hardware.  MoQ integration occurs at the
gateway/head-end level via TreeDN <xref target="RFC9706"></xref> and AMT <xref target="RFC7450"></xref> /
DRIAD <xref target="RFC8777"></xref>.</t>
<t>Several transports can be available for a given stream.  When more
than one delivery path is available, receivers SHOULD prefer, in
order: tuner-based broadcast reception (where tuner hardware exists),
native SSM, AMT tunneling, and finally MoQ/QUIC unicast.  Local
policy MAY override this order.  Within tuner-based reception,
receivers prefer Single Frequency Network (SFN) diversity reception,
where deployed, over single-transmitter reception.</t>

<section anchor="receiver-owned-dns-candidate-discovery"><name>Receiver-Owned DNS Candidate Discovery</name>
<t>When a receiver is given a service hostname for a MoQ upstream or an
AMT relay, it MUST perform discovery from that hostname at connection
time.  The resolver's A and AAAA answers form a set of candidate
addresses.  DNS answer order, address reachability, and an address's
geographic or topological appearance do not by themselves establish
health.  Local, anycast, or GeoDNS policy MAY use the resolver to steer
the candidate set toward a nearby upstream, but the receiver MUST
validate the selected candidate at the protocol layer.</t>
<t>For a MoQ/QUIC upstream, a receiver MUST retain the service hostname
for TLS certificate and SNI validation and MUST use a bounded attempt
for each candidate.  A candidate is healthy only after the QUIC
handshake, supported MoQ ALPN negotiation, MoQ session setup, and the
capability or catalog check needed by the requested service have
succeeded.  Receivers MUST bound both the number of concurrent candidate
attempts and the duration of each attempt.  They SHOULD race a bounded
batch of candidates in parallel (or use an equivalent bounded strategy),
select the first healthy candidate, and cancel the losing attempts.  A candidate that
completes QUIC but fails MoQ setup or capability validation MUST NOT
prevent another candidate from being tried.</t>
<t>For an AMT relay, a receiver MUST complete the AMT Discovery,
Advertisement, Request, and Membership Query exchange when those
messages are used, and MUST wait for either the first data packet for
the requested source and group or a configured finite
post-Membership-Query/Update wait to expire.  A receiver MUST NOT wait indefinitely for
data: for a source declared sparse or idle by local policy, expiry of
that wait MAY permit selection after the control-plane exchange, but
the result is control-plane health rather than proof of data-plane
delivery.  A relay that answers DNS or sends an Advertisement but does
not complete the exchange is not healthy.  After selection, the
receiver MUST use bounded liveness and membership-refresh timers and
MUST repeat DNS resolution and the candidate race after tunnel failure
or liveness expiry.</t>
<t>Receivers SHOULD honor DNS TTLs for new connection attempts and SHOULD
apply only bounded negative caching.  An established connection MAY
continue after its DNS answer expires, but a reconnect MUST NOT pin a
previous address indefinitely.  Retries MUST use bounded backoff and
MUST cancel stale attempts before starting another bounded batch; at no
time may candidate attempts accumulate without a bound.  If no candidate
becomes healthy, the receiver uses the next permitted delivery path or
remains in retry state according to local policy; it MUST NOT declare a
path healthy solely because DNS returned an answer.</t>
<t>This procedure applies equally to service hostnames obtained from
configuration, an operator provisioning system, or a multicast catalog.
An operator provisioning system is a control-plane service that publishes
the service hostname, transport metadata, and policy; it is not a
substitute for receiver-side protocol health validation.  A catalog MUST
NOT be interpreted as an authority to bypass that validation.  A literal
IP address is permitted for an explicitly configured static deployment,
but it does not provide DNS locality steering and is not the preferred
form for a service that has multiple upstreams.</t>
</section>
</section>

<section anchor="multicast-catalog-extension"><name>Multicast Catalog Extension</name>
<t>For tracks available via multicast, the MoQ catalog includes a
top-level <tt>multicast</tt> field containing an <tt>endpoints</tt> array for
endpoint discovery.</t>
<t>The catalog is the MSF catalog track, named <tt>catalog</tt>
(<xref target="I-D.ietf-moq-msf"></xref> Section 5; <xref target="MOQ-MMT"></xref> Section 12).</t>

<section anchor="multicast-endpoint-format"><name>Multicast Endpoint Format</name>
<t>The <tt>multicast</tt> field is a root-level extension member of the MSF
catalog; the catalog envelope for this suite is described in
<xref target="MOQ-MMT"></xref> Section 12.  <xref target="I-D.ietf-moq-msf"></xref> Section 5 permits
producers to add fields and requires parsers to ignore fields they
do not understand: parsers that do not support multicast MUST
ignore it.</t>
<t>The <tt>multicast</tt> field contains an <tt>endpoints</tt> array listing one or
more multicast groups.  A single-endpoint deployment uses a
one-element array:</t>

<sourcecode type="json"><![CDATA[{
  "multicast": {
    "endpoints": [{
      "sourceAddress": "198.51.100.10",
      "groupAddress": "232.0.10.1",
      "port": 8000,
      "tracks": [
        { "name": "video",        "packetId": 1 },
        { "name": "video/repair", "packetId": 2 },
        { "name": "audio",        "packetId": 3 },
        { "name": "audio/repair", "packetId": 4 }
      ],
      "bandwidth": 6500000
    }]
  }
}
]]></sourcecode>
<t>Multi-group deployments (e.g., per-quality ABR tiers or separate
audio/video groups) use multiple elements:</t>

<sourcecode type="json"><![CDATA[{
  "multicast": {
    "endpoints": [
      {
        "sourceAddress": "192.0.2.1",
        "groupAddress": "232.1.1.1",
        "port": 5004,
        "tracks": [
          { "name": "video",        "packetId": 1 },
          { "name": "video/repair", "packetId": 2 }
        ]
      },
      {
        "sourceAddress": "192.0.2.1",
        "groupAddress": "232.1.1.2",
        "port": 5004,
        "tracks": [
          { "name": "audio", "packetId": 3 }
        ]
      }
    ],
    "networkSource": [{
      "type": "amt",
      "relay": "amt.example.com",
      "discovery": "dns"
    }]
  }
}
]]></sourcecode>
<t>The <tt>multicast</tt> object contains the following members:</t>
<t><strong>endpoints</strong> (array of objects, REQUIRED): One or more multicast
  endpoint objects as defined below.  The array MUST NOT be empty.</t>
<t><strong>networkSource</strong> (array of objects, OPTIONAL): Network delivery
  configuration applying to all endpoints; see Section 4.2.</t>
<t><strong>auth</strong> (object, OPTIONAL): Content authentication configuration
  for the bc-provenance profile; see Section 7.2.</t>
<t>Endpoint field definitions:</t>
<t><strong>sourceAddress</strong> (string, OPTIONAL): SSM source IP address per
  <xref target="RFC4607"></xref>.  Its presence selects SSM and its absence Any-Source
  Multicast (ASM).</t>
<t><strong>protocol</strong> (string, OPTIONAL): &quot;ssm&quot; or &quot;asm&quot;, restating that
  mode.  An endpoint where it disagrees with <tt>sourceAddress</tt> is
  ignored (see below).</t>
<t><strong>groupAddress</strong> (string, REQUIRED): Multicast group address.</t>

<ul spacing="compact">
<li>SSM range: 232.0.0.0/8 (IPv4), ff3x::/32 (IPv6)</li>
<li>ASM range: 224.0.0.0/4 (IPv4), ff00::/8 outside the SSM range
ff3x::/32 (IPv6)</li>
</ul>
<t><strong>port</strong> (integer, REQUIRED): UDP port number.</t>
<t><strong>tracks</strong> (array, REQUIRED): Tracks available on this endpoint.
  Each element is an object with the following fields:</t>

<ul spacing="compact">
<li><strong>name</strong> (string, REQUIRED): Track name corresponding to the
track identifier used in the MoQ catalog.  The name is resolved
in the namespace of the catalog track (<xref target="I-D.ietf-moq-msf"></xref>
Section 5.2.2); an endpoint MUST NOT list a track whose catalog
entry declares another namespace.  The named track MUST
be a track whose <tt>packaging</tt> is <tt>mmtp</tt> (<xref target="MOQ-MMT"></xref> Section 12.1)
or a <tt>fec-repair</tt> track that repairs such a track (<xref target="MOQ-FEC"></xref>
Section 5.1.1).</li>
<li><strong>packetId</strong> (integer, REQUIRED): MMTP packet_id used for
packet-level track routing on multicast.  Maps directly to the
Packet ID field in the MMTP header (Section 3.1 of
<xref target="MOQ-MMT"></xref>).  The value is an integer in the range 1..65535.
Each value MUST denote exactly one MMTP packet sub-flow across
all endpoints of the MMT sending entity (Section 2) for the
lifetime of the delivery session, not merely within one <tt>(S, G,
port)</tt> multicast tuple (below); two distinct sub-flows MUST NOT
share a value (<xref target="ISO.23008-1"></xref> Clause 9.2.3).  A sub-flow
is one sequence of packets with one packet_sequence_number space.
An endpoint that carries
another endpoint's sub-flow packet for packet therefore carries it
under the same value, and every other sub-flow takes a value of
its own.  The rungs of a ladder never share a value.  A catalog
track denotes one sub-flow: every endpoint that lists a track
carries that track's one sub-flow, under one value.  Media that
the sending entity packetizes separately for several endpoints,
such as audio packetized once per rung, MUST be published as a
separate track for each packetization, with its own name, value,
<tt>fec</tt> object and repair tracks, because the copies differ in the
bytes that FEC protects (<xref target="MOQ-FEC"></xref> Section 7.3).  packet_id 0 is
reserved for the sending entity's MMTP signaling sub-flow
(<xref target="MOQ-MMT"></xref> Section 3.1; Section 5), which arrives on the same
tuples as the media; a publisher MUST NOT advertise it for a
track.</li>
</ul>
<t><strong>bandwidth</strong> (integer, RECOMMENDED): Aggregate bandwidth of this
  endpoint in bits per second, defined as the sum of the UDP payload
  bitrates of all tracks carried on the endpoint, including repair
  tracks, plus the MMTP signaling sub-flow on packet_id 0
  (Section 5) where the endpoint carries it; IP/UDP header overhead
  is excluded.  Publishers SHOULD
  include bandwidth to enable capacity-aware join decisions.
  Subscribers SHOULD check available network capacity before joining
  high-bandwidth groups.</t>
<t><strong>auth</strong> (object, OPTIONAL when the <tt>multicast</tt> object has no <tt>auth</tt>
  member, REQUIRED otherwise): Content authentication of this
  endpoint's flow; its presence makes the endpoint a protected flow.
  Receivers MUST ignore an endpoint that lacks <tt>auth</tt> when the
  <tt>multicast</tt> object carries <tt>auth</tt>, and an endpoint that carries
  <tt>auth</tt> when the <tt>multicast</tt> object does not (Section 7.2.6).
  See Section 7.2.</t>
<t><strong>networkSource</strong> (array of objects, OPTIONAL): Network delivery
  configuration for this endpoint; see Section 4.2.</t>
<t>Each <tt>(S, G, port)</tt> multicast tuple, with <tt>S</tt> the endpoint's
<tt>sourceAddress</tt> on an SSM endpoint, its <tt>auth.senderAddress</tt> on a
protected ASM endpoint (<xref target="digest"></xref>) and absent on an unprotected ASM
endpoint, MUST be associated with at most one MoQ namespace and at
most one endpoint object, and so MUST a protected endpoint's <tt>(S, G,
manifestPort)</tt> tuple (Section 7.2.6).  Publishers requiring
multiple independent streams MUST use distinct multicast groups or
ports.  An MMT sending entity (Section 2) serves exactly one MoQ
namespace, which is one MMT Package (Section 5); a publisher that
serves several namespaces acts as several sending entities, each with
its own endpoints, its own <tt>packetId</tt> assignment and its own
packet_id 0 signaling sub-flow.  A track listed on an endpoint MUST
NOT declare a <tt>namespace</tt> other than the catalog track's
(<xref target="I-D.ietf-moq-msf"></xref> Section 5.2.2); a receiver MUST ignore an
endpoint that lists one.</t>
<t>Receivers MUST ignore endpoints whose fields are mutually
inconsistent -- for example, a <tt>protocol</tt> of &quot;ssm&quot; with
<tt>sourceAddress</tt> omitted, a <tt>sourceAddress</tt> present with <tt>protocol</tt>
of &quot;asm&quot;, or a <tt>groupAddress</tt> outside the multicast address space.
Receivers MUST likewise ignore an endpoint that lists a track
<tt>packetId</tt> outside the range 1..65535, including 0, the MMTP
signaling flow.  They MUST also ignore an endpoint that lists a track
that the <tt>name</tt> field does not permit.</t>
<t>This document defines multicast carriage only for mmtp tracks
(Section 5).  To offer a track packaged as loc <xref target="I-D.ietf-moq-loc"></xref>
or cmaf <xref target="I-D.ietf-moq-cmsf"></xref> on multicast, a publisher also
publishes an mmtp rendition of the same media: a separate track whose
<tt>packaging</tt> is <tt>mmtp</tt>, with its own name, <tt>packetId</tt>, <tt>fec</tt> object
and repair tracks, in the same <tt>altGroup</tt> as the loc or cmaf track
(Section 6.1).  The endpoint lists the mmtp rendition track.  A
receiver that holds the loc or cmaf track over MoQ receives the mmtp
rendition track on multicast in its place.  The two tracks are
separate FEC instances (<xref target="MOQ-FEC"></xref> Section 6.4.1), so symbols of
one never repair the other, and the multi-path combining of
Section 6 applies within the mmtp rendition track only.</t>
<t>Subscribers receiving a catalog with multicast endpoints MAY
auto-connect to the multicast group when multicast APIs are
available and multicast delivery is preferred (UDP socket access,
native or AMT).  Multicast joins create
IGMP/MLD state on intermediate routers; subscribers SHOULD only
join when multicast provides a concrete benefit over unicast.</t>
<t>Joining an SSM endpoint requires source-specific group membership
signaling: receivers MUST use IGMPv3 <xref target="RFC9776"></xref> (IPv4) or MLDv2
<xref target="RFC9777"></xref> (IPv6) to convey (S,G) joins.  Earlier protocol
versions (IGMPv2/MLDv1) cannot express source-specific joins; if
the receiver's network segment operates at an earlier version (for
example, due to an IGMPv2 querier or snooping switch on the
segment), SSM joins fail silently.  Receivers SHOULD detect the
absence of multicast data following a join and fall back to
MoQ/QUIC unicast (Section 6).</t>
<t>During multicast join (which may take 1-3 seconds for IGMP/MLD),
subscribers SHOULD continue receiving via MoQ/QUIC.  Once multicast
data arrives, subscribers switch to the multicast path.  Section 6.3
describes how they then pause or end the unicast subscriptions that
the multicast path replaces.  What a
subscriber receives over MoQ/QUIC for a consumer that has not yet
admitted a datagram of a protected flow is outside the profile of
<xref target="content-authentication"></xref>: the MoQ session protects it, and the
receiver MAY verify it against the records of a <tt>manifestTrack</tt>
(<xref target="emission-and-cadence"></xref>); the profile governs a consumer from its
first admitted datagram (step 2 of
<xref target="receiver-enforcement-and-state"></xref>), at which the receiver discards
what the consumer still holds of the flow's tracks that it did not
admit.</t>
<t>If multicast reception fails or degrades, subscribers fall back
to MoQ/QUIC unicast per Section 6.</t>
<t>Receivers SHOULD implement hysteresis to prevent flapping between
multicast and unicast paths.  Switch away from multicast after
sustained loss or consecutive FEC block failures; switch back only
after multicast reception is stable for a sufficient period.
Specific thresholds are implementation-defined and configurable.</t>
</section>

<section anchor="network-source-types"><name>Network Source Types</name>
<t>The <tt>networkSource</tt> field is an array of network-source objects
describing how subscribers can reach the multicast stream when
native IP multicast routing is not available.  It MAY appear at the
<tt>multicast</tt> level (applying to all endpoints) or on individual
endpoints; a single source is expressed as a one-element array.</t>
<t>Each network-source object contains:</t>
<t><strong>type</strong> (string, REQUIRED): Delivery technology identifier.</t>
<t>Defined types:</t>

<section anchor="amt-automatic-multicast-tunneling"><name>AMT (Automatic Multicast Tunneling)</name>

<sourcecode type="json"><![CDATA[{
  "networkSource": [{
    "type": "amt",
    "relay": "amt.example.com",
    "discovery": "dns"
  }]
}
]]></sourcecode>
<t><strong>relay</strong> (string, OPTIONAL): AMT relay service name.  With <tt>dns</tt>, it
MUST be a hostname whose A and AAAA answers are candidate addresses.
With <tt>manual</tt>, it MUST be the literal address of the explicitly
configured relay.  It MUST be omitted with <tt>driad</tt>, because DRIAD
supplies the relay candidates.  A literal address does not provide DNS
locality steering.</t>
<t><strong>port</strong> (integer, OPTIONAL): UDP port on which the relay named by
  the <tt>relay</tt> field accepts requests.  When absent, the AMT port (2268,
  <xref target="RFC7450"></xref> Section 7.2) is used.  A value other than 2268 departs
  from <xref target="RFC7450"></xref> Sections 5.1.1 and 5.1.3 and works only with
  gateways and relays configured for it.  The field never applies to
  a relay found by DRIAD, whose AMTRELAY record carries no port
  (<xref target="RFC8777"></xref> Section 4.2).</t>
<t><strong>discovery</strong> (string, OPTIONAL): AMT relay discovery method.</t>

<ul spacing="compact">
<li>&quot;dns&quot;: Resolve the <tt>relay</tt> service hostname to A and AAAA
candidates and validate each candidate with the AMT protocol
(Section 3.1).
This is the RECOMMENDED method when a relay service hostname is
available.</li>
<li>&quot;driad&quot;: DNS Reverse IP AMT Discovery per <xref target="RFC8777"></xref>.
Subscribers query the source IP's reverse DNS for AMT relay
records; <tt>relay</tt> MUST be omitted.  Each discovered relay is still a
candidate and MUST be validated with the AMT protocol before
selection.</li>
<li>&quot;manual&quot;: Relay address is provided in the <tt>relay</tt> field.
This is intended for explicitly configured static deployments.</li>
</ul>
<t>For <tt>dns</tt>, subscribers MUST resolve the hostname at each connection
and reconnect attempt, bound both the number and duration of AMT
validation attempts, and race a bounded batch for the returned A and
AAAA candidates.  The receiver MUST wait for either first <tt>(S,G)</tt> data
or the configured finite post-Membership-Query/Update wait.  It MUST
select only a candidate that completes the AMT control-plane exchange;
for a sparse or idle source, selection after the wait expires records
control-plane health without claiming data-plane proof.  DNS answer
order is not a health signal.</t>
<t>When cancelling a losing AMT attempt that has reached the Query/Update
state, the receiver MUST send a Membership Update carrying the IGMP/MLD
leave (withdrawal) for the requested <tt>(S,G)</tt> <xref target="RFC7450"></xref> Section 5.1.5,
then close that candidate's socket.  An attempt that has not reached
that state is cancelled by closing its socket; a failed cancellation is
bounded and MUST NOT keep the candidate attempt alive.</t>
<t>Subscribers SHOULD look for a relay in this order, which keeps the
preference of <xref target="RFC8777"></xref> Section 3.1.2 for a relay in the
subscriber's own network:</t>

<ol spacing="compact">
<li>Look for a relay in the subscriber's own network, with DNS-based
Service Discovery (DNS-SD) <xref target="RFC6763"></xref> for <tt>_amt._udp</tt> and then
through the AMT anycast addresses of <xref target="RFC7450"></xref> Section 7.</li>
<li>Use receiver-owned DNS candidate discovery when <tt>discovery</tt> is
&quot;dns&quot;, or when <tt>discovery</tt> is omitted and <tt>relay</tt> is a hostname.</li>
<li>Use DRIAD discovery on the SSM source IP when <tt>discovery</tt> is
&quot;driad&quot;, or when both <tt>discovery</tt> and <tt>relay</tt> are omitted and the
source IP has DRIAD records.</li>
<li>Use a literal <tt>relay</tt> directly when <tt>discovery</tt> is &quot;manual&quot;, or when
<tt>discovery</tt> is omitted and <tt>relay</tt> is a literal under an explicitly
permitted static-deployment policy.</li>
<li>Fall back to MoQ/QUIC unicast.</li>
</ol>
<t>The <tt>discovery</tt> and <tt>relay</tt> fields select at most one of steps 2 to 4
for an endpoint.  A relay named by the <tt>relay</tt> field or found by DRIAD
is one that the sender knows, and is not necessarily near the
subscriber (<xref target="RFC8777"></xref> Section 3.1.1).  A local relay keeps more of
the path on native multicast.  Every relay found in steps 1 to 4 is a
candidate that the subscriber validates with the AMT protocol before
selection (Section 3.1).  DRIAD requires AMTRELAY (TYPE260) reverse
DNS records on the source IP.  Publishers without reverse DNS control
SHOULD provide a stable <tt>relay</tt> service hostname.</t>
</section>

<section anchor="atsc-3-0-broadcast-television"><name>ATSC 3.0 (Broadcast Television)</name>

<sourcecode type="json"><![CDATA[{
  "networkSource": [{
    "type": "atsc3",
    "frequency": 533000,
    "plpId": 0,
    "serviceId": 1,
    "slsUri": "https://example.com/atsc3/sls/service1.xml"
  }]
}
]]></sourcecode>
<t><strong>frequency</strong> (integer, REQUIRED): RF center frequency in kHz.</t>
<t><strong>plpId</strong> (integer, OPTIONAL): Physical Layer Pipe ID
  (<xref target="ATSC-A322"></xref>).  Defaults to 0.</t>
<t><strong>serviceId</strong> (integer, OPTIONAL): Service ID within the
  broadcast multiplex (<xref target="ATSC-A331"></xref>).</t>
<t><strong>slsUri</strong> (string, OPTIONAL): URI for Service Layer Signaling (SLS)
  bootstrap.  Enables receivers joining via IP (not RF) to acquire
  the full ATSC 3.0 SLS of an MMTP service, its MP table included
  (<xref target="ATSC-A331"></xref> Section 7.2).</t>
<t>Receivers with ATSC 3.0 tuner hardware can receive the stream
directly over RF.  MoQ subscribers without tuner hardware ignore
this networkSource type.</t>
</section>

<section anchor="multiple-network-sources"><name>Multiple Network Sources</name>
<t>When a stream is available via multiple delivery technologies, the
<tt>networkSource</tt> array lists one element per technology:</t>

<sourcecode type="json"><![CDATA[{
  "networkSource": [
    { "type": "amt", "discovery": "driad" },
    { "type": "atsc3", "frequency": 533000, "plpId": 0 }
  ]
}
]]></sourcecode>
<t>Subscribers select the highest-priority available source per the
transport hierarchy defined in Section 3.</t>
</section>
</section>
</section>

<section anchor="multicast-packet-format"><name>Multicast Packet Format</name>
<t>All multicast delivery uses MMTP packets.  Each UDP datagram carries
one MMTP packet -- the same packet format that MoQ objects carry on
QUIC streams.  MMTP provides track routing (packet_id), send
timestamps, sequencing (packet_sequence_number), FEC framing (FEC
Type and the Source/Repair FEC Payload IDs) and random access
signaling (RAP flag) in its packet header, and fragmentation (FI) in
the MPU-mode payload header (<xref target="MOQ-MMT"></xref> Sections 3.1 and 4.2).
For a MoQ receiver, FEC configuration (the RaptorQ OTI, or the
Reed-Solomon parameters) comes from the catalog (<xref target="MOQ-FEC"></xref>
Sections 4.2, 4.5 and 5.2); a receiver without the catalog relies on
the in-band signaling below.</t>
<t>Frame-sized LOC <xref target="I-D.ietf-moq-loc"></xref> or CMAF <xref target="I-D.ietf-moq-cmsf"></xref>
objects exceed a UDP datagram, while the MMTP MPU-mode payload format
fragments media into MTU-sized packets (<xref target="ISO.23008-1"></xref>
Clause 9.3.2; <xref target="MOQ-MMT"></xref> Section 5.2).  Multicast carriage therefore
uses MMTP: an endpoint lists only mmtp tracks and their repair
tracks, and a loc or cmaf track reaches multicast through an mmtp
rendition track (Section 4.1).</t>
<t>No additional multicast framing, encapsulation, or header format is
needed.  The MMTP packet format is defined in <xref target="ISO.23008-1"></xref>
Clause 9.2, as profiled in <xref target="MOQ-MMT"></xref> Section 3.1.</t>
<t>This design means the same MMTP packet can be delivered via three
transports without modification:</t>
<table>
<thead>
<tr>
<th>Transport</th>
<th>Encapsulation</th>
</tr>
</thead>

<tbody>
<tr>
<td>Reliable QUIC stream</td>
<td>MMTP packet as MoQ stream object</td>
</tr>

<tr>
<td>Multicast QUIC</td>
<td>MMTP packet as multicast QUIC DATAGRAM object</td>
</tr>

<tr>
<td>Multicast UDP</td>
<td>MMTP packet as UDP datagram</td>
</tr>
</tbody>
</table><t>Receivers on multicast demultiplex packets using the MMTP packet_id
field, which maps to the <tt>packetId</tt> assigned in the multicast
catalog endpoint (Section 4.1).  A receiver MUST NOT deliver to any
media or repair decoder a packet whose packet_id is not advertised
for a track, for the <tt>(S, G, port)</tt> multicast tuple (Section 4.1) on
which the packet arrived, <tt>S</tt> being the packet's source address as
this section matches it, by an endpoint that the receiver has not
ignored under Section 4.1.  Packets of the MMTP signaling flow
(packet_id 0) are handled per <xref target="MOQ-MMT"></xref> Section 3.1.  FEC source
and repair packets are distinguished by the MMTP FEC Type field
(<xref target="MOQ-MMT"></xref> Section 3.1).</t>
<t>A publisher MUST identify in band the Assets of every MMTP packet
flow that it sends to a multicast endpoint, whether a receiver
obtains the flow over native SSM or ASM or through an AMT relay
(Section 4.2.1).  It does so with one MMTP signaling sub-flow per MMT
sending entity (Section 2): a single sequence of packets on
packet_id 0, with one packet_sequence_number space, that every
endpoint listing a track other than a <tt>fec-repair</tt> track carries
packet for packet, as Section 4.1 requires of any sub-flow carried on
several endpoints (<xref target="ISO.23008-1"></xref> Clause 9.2.3).  All endpoints of
the sending entity carry one Package.  The sub-flow carries a PA
message (message_id 0x0000; <xref target="ISO.23008-1"></xref> Clause 10.3.2,
Table 21), in the signaling message mode of <xref target="MOQ-MMT"></xref> Section 3.1,
with the tables that <xref target="ISO.23008-1"></xref> Clause 10.3.1 requires of a PA
message, among them a PA table (Clause 10.3.7, Table 26) and the
complete MP table of the Package (table_id 0x20, Clause 10.3.9,
Table 29), never a subset.  The MP table MUST describe as an Asset
each track, other than a <tt>fec-repair</tt> track (<xref target="MOQ-FEC"></xref>
Section 5.1), that any endpoint of the sending entity lists, and MUST
locate it at each endpoint that carries it, and at no other, by an
MMT_general_location_info of location_type 0x01 (IPv4) or 0x02 (IPv6)
that gives the endpoint's group address and port, the track's
<tt>packetId</tt>, and, as <tt>ipv4_src_addr</tt> or <tt>ipv6_src_addr</tt>, the address
from which the sending entity transmits that endpoint's flow, which is
the source address of its IP packets (<xref target="ISO.23008-1"></xref>
Clause 10.6.1).  On an SSM endpoint that address equals the
endpoint's <tt>sourceAddress</tt> (Section 4.1); an ASM endpoint, whose
catalog entry has no <tt>sourceAddress</tt>, still carries the sending
entity's own address there.  A receiver matches a location by
destination address, destination port, packet_id and source address:
on an SSM endpoint the source address is the endpoint's
<tt>sourceAddress</tt>, and on an ASM endpoint it is the source address of
the received packets, which tells this sending entity's flow apart
from any other sender on the group.  A receiver behind an AMT relay
compares against the encapsulated IP header, which keeps the original
source address (<xref target="RFC7450"></xref> Section 5.1.6).  Because the sending
entity knows its own source address for every endpoint, the PA
message is identical on every endpoint, and its one version sequence
applies everywhere, so a newer version overrides an older one on all
endpoints alike (<xref target="ISO.23008-1"></xref> Clauses 10.2.3 and 10.3.9.3).
Repair flows are not Assets, and the MP table does not list them;
their in-band signaling is the AL-FEC message of <xref target="MOQ-MMT"></xref>
Section 8.3, which travels on the same sub-flow.</t>
<t>The publisher MUST send a PA message on the sub-flow ahead of the MPU
metadata (FT=0) data unit of every MPU of every track that it sends
to an endpoint, later than the FT=0 data unit of the previous MPU of
the same track, and ahead of any other signaling message sent for
that MPU, such as the AL-FEC message, so that a receiver joining any
endpoint at an MPU boundary holds the mapping before that MPU's media
and other signaling; <xref target="ISO.23008-1"></xref> Clause 10.3.2.1 requires a
receiver to process a PA message before any other signaling message.
A receiver joined to several endpoints receives each packet of the
sub-flow once per endpoint, as identical copies with the same
packet_sequence_number; it acts on one copy of each packet, whether a
PA message or an AL-FEC message, and ignores the rest.  An endpoint
that lists only <tt>fec-repair</tt> tracks (a repair-only endpoint,
<xref target="MOQ-FEC"></xref> Section 6.3.6) carries no Asset and need not carry the
sub-flow.  A receiver that holds the catalog routes a repair-only
endpoint's <tt>packetId</tt> values from the catalog (<xref target="MOQ-FEC"></xref>
Section 6.3.6).  A receiver without the catalog cannot discover a
repair-only endpoint, because the MP table locates only Assets and
the AL-FEC message names a repair flow by <tt>packetId</tt> without a
location, so repair carried on a repair-only endpoint reaches only
catalog-holding receivers.</t>
<t>For a receiver that holds the catalog, the endpoint's <tt>packetId</tt>
values remain authoritative: it routes packets by them as above,
needs no PA message, and handles packet_id 0 per <xref target="MOQ-MMT"></xref>
Section 3.1.  A PA message is never carried as a MoQ object
(<xref target="MOQ-MMT"></xref> Section 4.2).</t>
<t>The Multicast QUIC row corresponds to <xref target="QUIC-MULTICAST"></xref>, which adds
QUIC's per-packet AEAD and integrity.  The mapping of MMTP packets
onto its DATAGRAM frames, and the use of <xref target="MOQ-FEC"></xref> on that channel,
are described, informatively, in <xref target="MOQ-FEC"></xref> Section 11.3.</t>
</section>

<section anchor="multi-path-delivery"><name>Multi-Path Delivery</name>
<t>The same media content (source and repair) can be transmitted over
both MoQ unicast (QUIC/WebTransport) and multicast (UDP/SSM/AMT)
paths simultaneously.  Because the same MMTP packets are used on all
transports, receivers can combine symbols from any path for FEC
recovery <xref target="MOQ-FEC"></xref>.</t>
<t>When symbols arrive from multiple paths simultaneously, receivers:</t>

<ol spacing="compact">
<li>MUST deduplicate symbols within one FEC instance (<xref target="MOQ-FEC"></xref>
Section 6.4.1) -- a source track's base instance together with all
of its repair layers, or its keyframe overlay -- using SS_ID as the
unique key of a source symbol and (SS_Start, RS_ID) as that of a
repair symbol (<xref target="MOQ-FEC"></xref> Section 8.3)</li>
<li>MAY combine source and repair symbols received on different
paths in either direction: source symbols via multicast with
repair symbols via MoQ/QUIC, or source symbols via reliable
MoQ/QUIC with repair symbols via lossy multicast -- skipping
FEC decoding when all source symbols arrive reliably</li>
</ol>
<t>Multicast-to-unicast failover: if multicast reception fails, a
subscriber falls back to MoQ/QUIC unicast.  It resumes a unicast
subscription that it paused when it switched to multicast, as
Section 6.3 describes.  Otherwise it subscribes again, with a
SUBSCRIBE whose LOCATION_FILTER parameter (<xref target="I-D.ietf-moq-transport"></xref>
Section 9.20.9) sets where delivery starts, using one of the joins of
<xref target="I-D.ietf-moq-transport"></xref> Section 3.5:</t>

<ul spacing="compact">
<li>Next Object (Location Filter Type 0x05) starts at the live edge.
Delivery can begin in the middle of a group.</li>
<li>Next Object together with a FILL_PARAMETERS parameter
(<xref target="I-D.ietf-moq-transport"></xref> Section 9.20.15) whose LOCATION_FILTER
is Relative Start (0x01) with StartGroup 1 also delivers the
current group from its start, on a fill fetch stream
(<xref target="I-D.ietf-moq-transport"></xref> Section 3.4).  This is the join of
<xref target="MOQ-MMT"></xref> Section 6.1, and the one to use when the receiver
cannot decode from the middle of a group.</li>
<li>Relative Start (0x01) with StartGroup 0 starts at the next group.</li>
</ul>
<t>No coordinate mapping between multicast SS_IDs and MoQ Group IDs is
needed, because the publisher resolves each of these filters against
its Largest Object (<xref target="I-D.ietf-moq-transport"></xref> Section 3.1.4).
Failover replaces the multicast path; it is distinct from per-block
unicast repair (Section 6.2), which leaves the multicast subscription
in place.</t>

<section anchor="packaging-negotiation"><name>Packaging Negotiation</name>
<t>Packaging is advertised per track in the catalog, and a subscriber
subscribes by name to the tracks whose packaging it supports.  The
<tt>altGroup</tt> catalog field (<xref target="I-D.ietf-moq-msf"></xref> Section 5.2.12,
profiled in <xref target="MOQ-MMT"></xref> Section 4.4) groups alternatives of the same
content in different packaging formats.</t>
</section>

<section anchor="per-block-unicast-repair"><name>Per-Block Unicast Repair</name>
<t>A multicast or AMT receiver repairs every FEC block in-band from the
repair symbols it receives on its multicast paths.  For a block that
in-band FEC leaves unrecovered at its FEC deadline, a receiver that
holds a MoQ session (Section 1.1) applies the receiver repair policy
of <xref target="MOQ-FEC"></xref> Section 11.4, which repairs a keyframe-bearing block
by a FETCH <xref target="I-D.ietf-moq-transport"></xref> of the block's repair-track
group.  That group's ID is the block's extended SS_Start
(<xref target="MOQ-FEC"></xref> Section 7.5): the SS_Start from the Repair FEC Payload
ID of a multicast repair packet of the block, extended with the wrap
epoch to a value in [0, 2^62).  MoQ Transport Group IDs range up to
2^64 - 1 (<xref target="I-D.ietf-moq-transport"></xref> Section 8.1); repair Group IDs
use only [0, 2^62), as <xref target="MOQ-FEC"></xref> Section 7.5 specifies.
A receiver that takes its repair from multicast has usually received
no repair-track Group ID over MoQ.  Before its first FETCH it
therefore establishes the wrap epoch, as <xref target="MOQ-FEC"></xref> Section 7.5
specifies, unless it has received such a Group ID or has observed the
FEC instance from its first block.  To do so, it sends a TRACK_STATUS
request for the repair track (<xref target="I-D.ietf-moq-transport"></xref>
Section 9.13).  The epoch comes from the Group of the Largest Object
(<xref target="I-D.ietf-moq-transport"></xref> Section 3.1.4) carried in the
LARGEST_OBJECT parameter (<xref target="I-D.ietf-moq-transport"></xref> Section 9.20.17)
of the REQUEST_OK (TRACK_STATUS_OK) that answers it.  For
lost source packets whose block it has not delimited, because it
holds no repair packet of that block, the receiver instead fetches
the Group range that <xref target="MOQ-FEC"></xref> Section 7.5 derives from a lost
SS_ID.  No other coordinate mapping is needed, and the returned
symbols are deduplicated per item 1 of Section 6.  A receiver without
a MoQ session cannot request unicast repair; it relies on in-band FEC
and, where published, the keyframe overlay (<xref target="MOQ-FEC"></xref> Section 6.4).</t>
<t>A relay that does not hold the requested objects fetches them
upstream (<xref target="I-D.ietf-moq-transport"></xref> Sections 3.2.3 and 7.6).  A
FETCH for a repair-track group that neither the relay nor any
publisher upstream of it still holds fails or returns none of the
group's objects, and the receiver then treats the block as
unrecoverable.  How long relays and the original publisher keep
repair-track groups is their own policy.</t>
</section>

<section anchor="unicast-subscriptions-after-a-switch-to-multicast"><name>Unicast Subscriptions After a Switch to Multicast</name>
<t>After a receiver switches to multicast (Section 4.1), it still holds
the unicast subscriptions that delivered the same media before the
switch: to the tracks it now receives on multicast or to their loc
or cmaf alternatives (Section 4.1), to the repair tracks of such a
loc or cmaf alternative, and to the repair tracks that its multicast
endpoints now carry.
While these subscriptions forward objects, the receiver gets the
media twice, and the network still carries the unicast copy that the
multicast path replaces.  Once multicast delivery of a track is
verified, for example by the receipt on multicast of a complete group
or a complete FEC block of the track, the receiver SHOULD pause these
subscriptions or end them:</t>

<ul spacing="compact">
<li>To pause a subscription, the receiver sends a REQUEST_UPDATE with
the FORWARD parameter set to 0 (<xref target="I-D.ietf-moq-transport"></xref>
Sections 3.1.1 and 9.20.18).  The subscription stays Established,
and the publisher sends no objects on it.  Failover then needs no
new SUBSCRIBE.</li>
<li>To end a subscription, the receiver sends STOP_SENDING
(<xref target="I-D.ietf-moq-transport"></xref> Section 3.1).</li>
</ul>
<t>The receiver does not pause its other subscriptions, such as those
to the catalog track, any manifest track (Section 7.2.5), and any
repair layer that no endpoint it holds carries and that it combines
with multicast source symbols (Section 6, item 2; <xref target="MOQ-FEC"></xref>
Section 6.3.6).</t>
<t>On failover (Section 6), the receiver resumes a paused subscription
with a REQUEST_UPDATE that sets FORWARD to 1.  FILL_PARAMETERS
carried while a subscription is paused opens no fill fetch stream,
and resuming without re-sending FILL_PARAMETERS opens none either
(<xref target="I-D.ietf-moq-transport"></xref> Section 3.4.1).  A receiver that needs
the current group from its start therefore sends FILL_PARAMETERS, as
in the join of Section 6, in a further REQUEST_UPDATE after it
receives the REQUEST_UPDATE_OK for the update that resumed the
subscription.  A receiver that ended its subscriptions subscribes
again as Section 6 describes.</t>
<t>Pausing or ending these subscriptions does not affect per-block
unicast repair (Section 6.2), which uses FETCH.</t>
</section>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>

<section anchor="multicast-security"><name>Multicast Security</name>
<t>SSM inherently limits traffic to authorized sources via (S,G)
filtering.  Receivers MAY detect replayed packets by tracking the
MMTP Packet Sequence Number per packet_id within one (source address,
groupAddress, port) tuple and discarding duplicates and packets
outside a bounded reordering window.  The source address is that of
the received packets, the same address Section 5 matches: it equals
<tt>sourceAddress</tt> on an SSM endpoint and is read from each packet on an
ASM endpoint, so two sending entities that share an ASM group and
each use the same packet_id are tracked separately.  For AMT, the
gateway reads the source address from the encapsulated header and
trusts the relay to have joined that source per <xref target="RFC7450"></xref>; the
relay authenticates nothing it forwards, so an AMT receiver of a
protected flow checks the source itself
(<xref target="receiver-enforcement-and-state"></xref>).</t>
</section>

<section anchor="content-authentication"><name>Content Authentication</name>
<t>Multicast UDP delivery lacks the integrity protection that QUIC
provides on unicast paths.  This section defines &quot;bc-provenance&quot;
version 2, a manifest-provenance authentication profile.  For each
protected flow the publisher computes a digest over every datagram
the flow emits, repair symbols included; collects digests into
windows of at most 37 datagrams or <tt>manifestTimerMs</tt>, whichever
closes first; places each window in a fixed-layout manifest record;
signs the record with a dedicated broadcast Ed25519 key <xref target="RFC8032"></xref>
under a network-identity prefix; and emits the record on the flow's
manifest port immediately before the datagrams it covers, followed
by a second copy.  Receivers verify one signature per window, hash
each datagram once, and admit a datagram to reassembly, FEC decode
or delivery only after its digest matches an entry of a verified
manifest.</t>
<t>The digest pseudoheader, the hold queue and the consume-on-admission
rule are those of AMBI (<xref target="I-D.ietf-mboned-ambi"></xref> Sections 3.4.2 and
3.2); the signed record carried on the flow's own (S, G) is the
multicast-carried manifest delivery that AMBI Section 2.1.1
anticipates and leaves to another specification.</t>
<t>The digest algorithm is carried in the signed record as a Named
Information Suite ID (<xref target="hash-algorithms"></xref>); SHA-256 <xref target="FIPS180-4"></xref> is
mandatory to implement.</t>
<t>The record, digest pseudoheader, signature prefix and hash registry
do not depend on MoQ; <xref target="signature-context-binding"></xref>,
<xref target="catalog-signaling"></xref> and <xref target="broadcast-carriage"></xref> bind them
to MoQ.</t>
<t>All multi-octet integers are unsigned and big-endian; <tt>u8</tt>, <tt>u16</tt>,
<tt>u32</tt> and <tt>u64</tt> are unsigned integers of that width; <tt>lp16(x)</tt> is a
<tt>u16</tt> octet count followed by the octets of <tt>x</tt>; a string is its
UTF-8 octets; <tt>H</tt> is the function selected by the record's
<tt>hash_alg</tt> (<xref target="hash-algorithms"></xref>), with a 32-octet output.  Test vectors
are in <xref target="test-vectors-bc-provenance-version-2"></xref>.</t>

<section anchor="protected-flows-and-sequence-numbers"><name>Protected Flows and Sequence Numbers</name>
<t>The protected unit is a flow: every datagram the sending entity
emits to one endpoint <tt>(S, G, port)</tt> of <xref target="multicast-endpoint-format"></xref>
whose <tt>auth</tt> member is present (<xref target="catalog-signaling"></xref>).  That is the
datagrams of every track the endpoint lists, repair tracks included,
and the MMTP signaling sub-flow on packet_id 0
(<xref target="multicast-packet-format"></xref>) where the endpoint carries it.  A
track carried on several endpoints is in a separate flow on each.
A packet of a listed track that another path delivers to a consumer
protected under the flow, one that has admitted a datagram of it, is
a datagram of the flow as well (step 2 of
<xref target="receiver-enforcement-and-state"></xref>).
A flow has one <tt>manifestPort</tt>, which no other flow of the same <tt>(S,
G)</tt> shares (<xref target="catalog-signaling"></xref>); its <tt>sequence</tt> counter (below) and
its <tt>mseq</tt> counter (<xref target="manifest-record"></xref>) are kept per <tt>(kid, epoch)</tt>.</t>
<t><tt>sequence</tt> is a 64-bit counter the sender assigns per <tt>(flow, kid,
epoch)</tt>: it starts at any value and increases by one for every
datagram the flow emits, in emission order, repair symbols and
signaling datagrams included.  It is carried only in manifest
records, never in data datagrams, and is unrelated to MoQ
<tt>group_id</tt> and <tt>object_id</tt>.  Digest <tt>i</tt> of a record covers
<tt>sequence = first_seq + i</tt>, so one window is a contiguous run of
the flow.  A window MAY span a MoQ group boundary.  A datagram the
sender emits twice (a carousel) has two sequences and two digests.</t>
</section>

<section anchor="digest"><name>Digest</name>
<t>Every datagram the sender emits in a protected flow has a digest:</t>

<artwork><![CDATA[D = H(pseudoheader || payload)
]]></artwork>
<t><tt>payload</tt> is the entire UDP payload as transmitted, including the
trailing Source FEC Payload ID when the packet's FEC Type is 1
(<xref target="MOQ-FEC"></xref> Section 8.5), so a verified digest also authenticates
the FEC block assignment of a received packet.  A packet the decoder
recovers carries no Source FEC Payload ID and none is re-appended for
delivery (<xref target="MOQ-FEC"></xref> Section 7.3); when its admission needs a digest,
the receiver appends the base SS_ID its position in the base block
implies, for the hash only (<xref target="receiver-enforcement-and-state"></xref>).</t>
<t><tt>pseudoheader</tt> is the AMBI pseudoheader of <xref target="I-D.ietf-mboned-ambi"></xref>
Section 3.4.2, restated here by value since that draft has expired:</t>

<artwork><![CDATA[pseudoheader =
    S                     # 4 octets (IPv4) or 16 octets (IPv6)
 || G                     # 4 or 16 octets, same family as S
 || 0x00                  # zeroes
 || 0x11                  # protocol, UDP
 || u16(payload_length)   # length of payload in octets
 || u16(src_port)
 || u16(dst_port)
 || u32(streamId)
]]></artwork>
<t>Addresses are in native width, never IPv4-mapped.  <tt>S</tt> is the
address from which the sending entity transmits the flow
(<xref target="multicast-packet-format"></xref>): the endpoint's <tt>sourceAddress</tt> on an
SSM endpoint and its <tt>auth.senderAddress</tt> on an ASM endpoint
(<xref target="catalog-signaling"></xref>).  <tt>src_port</tt> and <tt>dst_port</tt> are both the
endpoint's <tt>port</tt>: the sender MUST emit every datagram of the flow
from UDP source port <tt>port</tt>.  <tt>streamId</tt> is the endpoint's
<tt>auth.streamId</tt>.  Every input is catalog-declared, and a receiver without the catalog
holds the same values from its out-of-band provisioning
(<xref target="receiver-enforcement-and-state"></xref>), so for a datagram of the flow
delivered over a unicast MoQ path or an MMTP broadcast the receiver
forms the same pseudoheader and one digest verifies the same bytes
on every path (<xref target="broadcast-carriage"></xref>; step 2 of
<xref target="receiver-enforcement-and-state"></xref>).</t>
</section>

<section anchor="manifest-record"><name>Manifest Record</name>
<t>A manifest record is one UDP datagram on <tt>manifestPort</tt> of the same
<tt>(S, G)</tt> as the protected flow.  Every field except the signature is
at a fixed offset, and the signature is the last 64 octets.</t>
<table>
<thead>
<tr>
<th>Offset</th>
<th>Size</th>
<th>Field</th>
</tr>
</thead>

<tbody>
<tr>
<td>0</td>
<td>4</td>
<td><tt>magic</tt>, the ASCII octets &quot;MHM2&quot;</td>
</tr>

<tr>
<td>4</td>
<td>4</td>
<td><tt>kid</tt>, key identifier (<xref target="catalog-signaling"></xref>)</td>
</tr>

<tr>
<td>8</td>
<td>4</td>
<td><tt>epoch</tt>, increments on sender restart and key change</td>
</tr>

<tr>
<td>12</td>
<td>8</td>
<td><tt>mseq</tt>, manifest counter, monotonic per <tt>(flow, kid, epoch)</tt></td>
</tr>

<tr>
<td>20</td>
<td>8</td>
<td><tt>first_seq</tt>, the <tt>sequence</tt> of digest 0</td>
</tr>

<tr>
<td>28</td>
<td>2</td>
<td><tt>count</tt>, 1 to 37</td>
</tr>

<tr>
<td>30</td>
<td>1</td>
<td><tt>hash_alg</tt> (<xref target="hash-algorithms"></xref>)</td>
</tr>

<tr>
<td>31</td>
<td>1</td>
<td><tt>flags</tt> (below)</td>
</tr>

<tr>
<td>32</td>
<td>4</td>
<td><tt>sent</tt>, emission time in POSIX seconds (below)</td>
</tr>

<tr>
<td>36</td>
<td>32</td>
<td><tt>ctx</tt>, context hash (<xref target="signature-context-binding"></xref>)</td>
</tr>

<tr>
<td>68</td>
<td>32 x count</td>
<td>digests, in emission order</td>
</tr>

<tr>
<td>68 + 32 x count</td>
<td>64</td>
<td>Ed25519 signature</td>
</tr>
</tbody>
</table><t>Length is <tt>132 + 32 x count</tt> octets.  The cap of 37 digests is an MTU
choice: the 1316-octet maximum is 1344 octets on the wire with IPv4
and UDP headers and 1364 with IPv6, which leaves room for AMT
encapsulation <xref target="RFC7450"></xref> on a 1500-octet path.</t>
<t><tt>flags</tt>: bit 0, epoch end (<xref target="receiver-enforcement-and-state"></xref>); bits
1 to 7 reserved, MUST be zero on send and ignored on receipt.</t>
<t><tt>sent</tt> is the sender's clock when the record is signed, truncated to
whole seconds since 1970-01-01T00:00:00Z with leap seconds excluded,
from a clock synchronized to UTC within 1 s; the twin of a record
(<xref target="emission-and-cadence"></xref>) carries the original's value.  It is a count
of seconds, not the 16.16 NTP short format, so it wraps in 2106 rather
than every 18.2 hours.  A sender whose clock is further off than the
tolerance of <xref target="receiver-enforcement-and-state"></xref> makes its flow
unverifiable to receivers that check freshness.</t>
<t>All records of one flow carry the same <tt>hash_alg</tt> for the life of a
stream generation (<tt>streamId</tt>, <xref target="catalog-signaling"></xref>); a change of
algorithm is a new generation.  <tt>epoch</tt> never decreases under one
<tt>kid</tt>: a sending entity MUST NOT reuse an epoch under a <tt>kid</tt>, across
generations or restarts, and a generation that would start below the
<tt>kid</tt>'s last epoch starts under a new <tt>kid</tt> (<xref target="catalog-signaling"></xref>).</t>
</section>

<section anchor="signature-context-binding"><name>Signature Context Binding</name>
<t>The signature is computed over:</t>

<artwork><![CDATA[signed_message =
    "mcast-manifest-v2" || 0x00     # 18-octet prefix
 || u32(streamId)
 || S                               # 4 or 16 octets
 || G                               # 4 or 16 octets
 || u16(manifestPort)
 || record[0 .. len - 64)
]]></artwork>
<t>with <tt>S</tt>, <tt>G</tt>, <tt>streamId</tt> and <tt>manifestPort</tt> as declared for the
endpoint (<xref target="digest"></xref>, <xref target="catalog-signaling"></xref>).  The prefix binds the flow's network identity; a receiver needs no
catalog state to check it beyond those four values, which a MoQ
receiver takes from the catalog and a broadcast-only receiver from its
provisioning (<xref target="receiver-enforcement-and-state"></xref>).
The MoQ context is bound inside the record by <tt>ctx</tt>:</t>

<artwork><![CDATA[ctx = H("BCPV2-ctx"
        || u16(f) || f * lp16(field)        # namespace, in order
        || u16(n) || n * u16(packetId)      # ascending
        || params_hash)

params_hash = H("BCPV2-params"
    || lp16(codec) || u32(timescale) || u32(groupDurationTicks)
    || lp16(fecAlgorithm) || u32(sourceSymbols) || u32(repairSymbols)
    || u32(symbolSize) || u32(interleaveDepthMs))
]]></artwork>
<t>both with the record's <tt>hash_alg</tt>.  The <tt>field</tt> values are the <tt>f</tt>
Track Namespace Fields, in order, of the one MoQ namespace the sending
entity serves (<xref target="multicast-endpoint-format"></xref>): the Track Namespace of
the catalog track (<xref target="I-D.ietf-moq-transport"></xref>), which every track
listed on an endpoint shares (<xref target="multicast-endpoint-format"></xref>;
<xref target="I-D.ietf-moq-msf"></xref> Section 5.2.2); each field is the octets MOQT
carries for it.  The <tt>packetId</tt> values are those of the endpoint's
<tt>tracks</tt> (<xref target="multicast-endpoint-format"></xref>), <tt>n</tt> of them in ascending
order.  <tt>params_hash</tt> is taken from the catalog
entry of the endpoint's lowest-<tt>packetId</tt> media track or, for an
endpoint that lists only <tt>fec-repair</tt> tracks, from the catalog entry
of the source track that its lowest-<tt>packetId</tt> repair track repairs:
the track its <tt>depends</tt> names or, on layer 0, the track whose
<tt>fec.repairTrack</tt> names it (<xref target="MOQ-FEC"></xref> Section 5.1.1).  <tt>codec</tt> is
that entry's MSF field; <tt>timescale</tt> is its timescale and
<tt>groupDurationTicks</tt> its group duration in <tt>timescale</tt> ticks, both as
<xref target="MOQ-MMT"></xref> Section 4.4.2 resolves them; <tt>fecAlgorithm</tt> is the
<tt>algorithm</tt> member of its <tt>fec</tt> object and the other four are its
same-named members (<xref target="MOQ-FEC"></xref> Section 5.1).  An absent
<tt>interleaveDepthMs</tt> is encoded as <tt>u32(0)</tt>; an entry with no <tt>fec</tt>
object is encoded as <tt>lp16(&quot;none&quot;)</tt> followed by four <tt>u32(0)</tt>; an
entry for which <xref target="MOQ-MMT"></xref> Section 4.4.2 resolves no <tt>timescale</tt> or
no group duration, as it need not for a track that is not
mmtp-packaged (<xref target="multicast-packet-format"></xref>), is encoded as <tt>u32(0)</tt> for
that value.  A flow carried without a MoQ catalog sets <tt>ctx</tt>
to 32 zero octets.  A MoQ receiver recomputes <tt>ctx</tt> from the catalog
and MUST reject a record whose <tt>ctx</tt> differs
(<xref target="receiver-enforcement-and-state"></xref>); a receiver without the catalog
carries <tt>ctx</tt> blind.  A valid (record, datagrams) pair therefore cannot
be replayed onto another endpoint, stream generation, broadcast, key
or epoch; <tt>sent</tt> (<xref target="manifest-record"></xref>) bounds replay within an epoch to
the clock tolerance of <xref target="receiver-enforcement-and-state"></xref>.</t>
</section>

<section anchor="emission-and-cadence"><name>Emission and Cadence</name>
<t>A sender closes a window after 37 datagrams or after <tt>manifestTimerMs</tt>
since the window opened, whichever comes first; the timer governs only
while the flow emits at most 37 datagrams per <tt>manifestTimerMs</tt> (1850
per second at the default, about 21 Mbit/s of 1400-octet datagrams),
and the count cap governs above that.  A sender MAY raise
<tt>manifestTimerMs</tt> to at most <tt>maxAuthDelayMs / 4</tt> on low-rate flows.</t>
<t>The signed record MUST be transmitted immediately before the first
datagram it covers (only the twin of an earlier window MAY come
between them), so the sender holds at most one window of data.  A twin,
a byte-identical copy of the record, MUST be transmitted after the
last datagram the record covers, no later than <tt>maxAuthDelayMs / 2</tt>
after the record and before the signed record of window <tt>mseq + 64</tt>,
so that at most 63 later records precede it and it arrives above the
replay floor of <xref target="receiver-enforcement-and-state"></xref>, which only the
records arriving on <tt>manifestPort</tt> raise, however fast the flow
closes windows; RECOMMENDED is to transmit it immediately after
the signed record of window <tt>mseq + 2</tt> and before the first datagram
that record covers, or on its own at the <tt>maxAuthDelayMs / 2</tt>
deadline when window <tt>mseq + 2</tt> has not opened by then.  The two
copies then bracket the window's datagrams: a
contiguous loss burst that removes both copies has removed every
datagram between them, and a datagram that survives such a burst has a
usable copy on one side of it, whatever the flow's rate and timer;
only two separate losses can leave an arriving datagram without a
copy, and then at most 37 datagrams expire from the hold queues
(<xref target="threat-model"></xref>).  Every twin of an epoch MUST be transmitted before
the first record of a later epoch, since a receiver drops the records
of an earlier epoch once its grace period ends
(<xref target="receiver-enforcement-and-state"></xref>); every twin of the epoch not yet
transmitted when its last datagram has been sent, the twin of the
record whose <tt>flags</tt> bit 0 is set included, follows that datagram at
once.  A receiver handles either copy by step 1 of
<xref target="receiver-enforcement-and-state"></xref> and drops the second with one
lookup and one hash.</t>
<t>At 37 digests per window, a record and its twin cost about 71 octets
per 1400-octet datagram (5.1%); data datagrams are untouched.  Manifest
records MUST NOT be FEC-protected; the twin is their loss remedy.</t>
<t>A publisher MAY mirror the same record octets as objects on a MoQ
manifest track (one record per object; a record goes in
manifest-track group N, N being the Group ID of the latest packet,
emitted at or before the window's first datagram, of the track whose
catalog entry supplies <tt>params_hash</tt> (<xref target="signature-context-binding"></xref>),
or of that track's first group of the stream generation where none
precedes) for unicast subscribers.  A packet of a track the endpoint
lists that reaches a consumer protected under the flow is a datagram
of the flow on a MoQ unicast path too (step 2 of
<xref target="receiver-enforcement-and-state"></xref>), and the mirror is
its only source of records while the multicast path delivers none,
during the join of <xref target="multicast-endpoint-format"></xref> and after the
failover of <xref target="multi-path-delivery"></xref>.  Without a <tt>manifestTrack</tt> a
track's unicast fallback is a MoQ subscription outside this profile,
protected by the MoQ session alone, so a publisher that wants the
fallback verified against its records MUST name one.  Records read
from the manifest track charge a verification budget of their own,
not the flow's <tt>manifestPort</tt> budget
(<xref target="receiver-enforcement-and-state"></xref>); a receiver that reads the
manifest track while joined to the multicast path sees each record
first on the shorter path and drops the later copy as already
verified, matching its digests only where it raises the mirror's
lead (step 1 of <xref target="receiver-enforcement-and-state"></xref>), so where the
mirror leads <tt>manifestPort</tt> it widens, by the mirror's lead and
under the bound
below, the pending expiry of a mirrored record's entries and the
time for which it keeps a digest it admits recorded as admitted: a
relay copy admitted first would otherwise leave the multicast copy,
arriving up to the lead later, to be held and to expire as loss
(steps 1 and 2 of <xref target="receiver-enforcement-and-state"></xref>).  The lead of a
record is unknown when its entries are inserted, so the receiver
keeps the lead as a per-flow value: the largest interval it has
measured in the stream generation from the arrival of a record on
the mirror to the arrival of the first copy on <tt>manifestPort</tt>,
which the lookup drops (a later copy on that path, the twin or one
replayed within the retention, measures nothing; step 1 keeps the
time and path of arrival, and a digest of the record's bytes, with
each verified <tt>(kid, epoch, mseq)</tt> for this), each interval rounded
up to a multiple of <tt>manifestTimerMs</tt> before it is compared with
the lead (so the lead rises at most <tt>5000 / manifestTimerMs</tt> times
in a stream generation, 250 at the default, however a relay paces
the mirror; step 1), and the bound below until it has measured
one; a copy that raises the lead, byte-identical to the
verified record, re-admits or re-inserts, under the raised lead and
ahead of the datagrams they cover, the digests of that record and
of every record verified
on the manifest track within the raised lead before now whose first
copy has not yet arrived, for the pairs that have admitted nothing
and are not pending, widens the pending entries of those records by
the raised lead, and
records again as admitted, under the raised lead, the digests of
the pairs that have, so that a mirror whose lead grows past the one
measured, within the bound below, costs the datagrams of those
records nothing, a late copy of a datagram a pair has admitted is
dropped rather than held, and a copy replayed within the retention
re-admits nothing (step 1).  A
receiver that reads no manifest track applies no
lead, nor does one that will not join the multicast path; one that
may yet join applies the bound until it has measured a lead, since
the multicast copies of the datagrams its relay delivers before the
join arrive after it.  The timers of
<xref target="receiver-enforcement-and-state"></xref> are wall-clock bounds; a
relay-buffered QUIC path can deliver records later than
<tt>maxAuthDelayMs</tt>, or the datagrams of a window later than the record
that covers them, the manifest track and the media tracks being
separate streams, and a unicast receiver SHOULD widen its hold, and
the freshness tolerance of step 1 of
<xref target="receiver-enforcement-and-state"></xref> for the records it reads from the
manifest track, accordingly, by at most 5 s, the widening W, a
receiver that applies no lead widening the pending expiry of a
mirrored record's entries by W as well (step 1), since no lead
covers the datagrams its relay delivers after the records that
cover them: the widening is also its exposure at join to windows
that old served by its relay (<xref target="threat-model"></xref>), a record on
<tt>manifestPort</tt> keeps the unwidened tolerance and pending expiry as
the multicast path's hold queue keeps the unwidened expiry, and a
receiver that needs more is replaying
(<xref target="receiver-enforcement-and-state"></xref>).  The bound caps the lead as
well: a relay that leads the multicast path by more than the bound
plus <tt>maxAuthDelayMs</tt> outruns the admitted digests the lead widens,
and by more than that plus <tt>2 x manifestTimerMs</tt> the pending
entries as well, and the copy on <tt>manifestPort</tt> of a record it
mirrors that far ahead, which the receiver keeps verified for as
long as the freshness check of step 1 would pass it, or for as long
after that as its <tt>mseq</tt> stays above the manifest track's replay
floor (<xref target="receiver-enforcement-and-state"></xref>), is dropped by the lookup
of step 1 while so kept and by that check after, and, the lead
standing at the bound, admits nothing either way, so the multicast
copies of the datagrams of those records are held and expire from
the multicast path's hold queue, loss for the failover
(<xref target="threat-model"></xref>); the lead being the multicast path's lag behind
the mirror, such a path fails over on its own latency on a receiver
that checks freshness.  One that skips the check (the Clock
paragraph of <xref target="receiver-enforcement-and-state"></xref>) keeps the record
verified <tt>7 s + maxAuthDelayMs</tt> after its arrival on the manifest
track, or for as long after that as its <tt>mseq</tt> stays above the
manifest track's replay floor (the state list of that section), so
under a relay leading by more than the longer of the two the copy
on <tt>manifestPort</tt> misses the lookup, meets no freshness check and
is verified anew there, from that path's budget, admitting the
datagrams of its window as any record on <tt>manifestPort</tt> does: such
a receiver fails over on a lead between the bound plus
<tt>maxAuthDelayMs</tt> and that retention and, under a larger one,
verifies every record on both paths instead; on a flow whose 64
windows outlast <tt>7 s + maxAuthDelayMs</tt>, the floor keeps the record
verified until its copy arrives under any lead shorter than 64
windows, the lookup drops the copy, and such a receiver fails over
as one that checks freshness does.  Under a lead beyond that
retention such a receiver still loses windows at each epoch change,
and so may fail over there: those of the previous epoch's records
whose copies reach <tt>manifestPort</tt> past that epoch's deadline (step 1
of <xref target="receiver-enforcement-and-state"></xref>), and those of most of its 64
latest records, whose copies the lookup drops because that epoch's
replay floor, raised by no later record, keeps them verified.</t>
</section>

<section anchor="catalog-signaling"><name>Catalog Signaling</name>
<t>The <tt>auth</tt> member of the <tt>multicast</tt> object
(<xref target="multicast-endpoint-format"></xref>) declares the profile and its keys;
the <tt>auth</tt> member of an endpoint object marks that endpoint's flow
as protected and carries the flow's parameters:</t>

<sourcecode type="json"><![CDATA[{
  "multicast": {
    "auth": {
      "scheme": "bc-provenance",
      "version": 2,
      "sig": "ed25519",
      "keys": [
        { "kid": 1,
          "publicKey": "<32-byte Ed25519 key, base64url unpadded>",
          "firstEpoch": 1 }
      ],
      "minEpoch": 17
    },
    "endpoints": [{
      "sourceAddress": "198.51.100.7",
      "groupAddress": "232.10.20.30",
      "port": 5004,
      "bandwidth": 100000000,
      "tracks": [
        { "name": "video",        "packetId": 1 },
        { "name": "video/repair", "packetId": 2 }
      ],
      "auth": {
        "manifestPort": 5005,
        "streamId": 2864434397,
        "maxAuthDelayMs": 200,
        "manifestTimerMs": 20,
        "manifestTrack": "video/manifest"
      }
    }]
  }
}
]]></sourcecode>
<t>Members of <tt>multicast.auth</tt>:</t>
<t><strong>scheme</strong>, <strong>version</strong> (REQUIRED): &quot;bc-provenance&quot;, 2.</t>
<t><strong>sig</strong> (string, REQUIRED): &quot;ed25519&quot; <xref target="RFC8032"></xref>.</t>
<t><strong>keys</strong> (array, REQUIRED): one to four <tt>{kid, publicKey,
firstEpoch}</tt> objects.  <tt>kid</tt> is an unsigned 32-bit integer carried in
the clear in every record; <tt>publicKey</tt> is the 32-octet Ed25519 public
key, base64url-encoded without padding; <tt>firstEpoch</tt> is the first
<tt>epoch</tt> the key signs.  A key signs the epochs from its <tt>firstEpoch</tt>
up to, but not including, the next larger <tt>firstEpoch</tt> in the array,
and the key with the largest <tt>firstEpoch</tt> signs every later epoch.
<tt>kid</tt> values MUST be unique within a broadcast and <tt>firstEpoch</tt> values
MUST be distinct within the array.  A receiver that takes its keys
from a catalog whose <tt>keys</tt> repeats a <tt>kid</tt> or a <tt>firstEpoch</tt> holds no
key set for the broadcast: it admits nothing and treats every endpoint
of the <tt>multicast</tt> object as one that yields no data
(<xref target="multicast-endpoint-format"></xref>), as it does on a flow whose records
carry an algorithm it does not implement
(<xref target="receiver-enforcement-and-state"></xref>); a receiver that pins keys is
unaffected, since its key set comes from the pin.  A receiver drops, before
verification, a record whose <tt>epoch</tt> is outside its <tt>kid</tt>'s range
(<xref target="receiver-enforcement-and-state"></xref>), so a retired key cannot carry a
receiver past its successor's <tt>firstEpoch</tt>.</t>
<t><strong>minEpoch</strong> (integer, REQUIRED): the lowest <tt>epoch</tt> a receiver
accepts at join (step 1 of <xref target="receiver-enforcement-and-state"></xref>); after
its first verified record the current epoch is the floor, with the
previous epoch admitted on <tt>manifestPort</tt> until that path's grace
period ends, or while that path has started none, and never past the
bound
of <xref target="emission-and-cadence"></xref> plus <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>
after the current epoch became current, and on the manifest track
within the earlier-started of the two paths' periods (step 1).  The
publisher MUST write the current
<tt>epoch</tt> at every
epoch change, sender restart included, so a joiner with a current
catalog cannot be fed an earlier epoch.</t>
<t>Members of an endpoint's <tt>auth</tt>; a protected endpoint MUST also
declare <tt>bandwidth</tt>, which bounds the receiver's hold queues:</t>

<ul spacing="compact">
<li><strong>manifestPort</strong> (integer, REQUIRED): destination port of the record
datagrams, twins included, on the endpoint's <tt>(S, G)</tt>; MUST differ
from the <tt>port</tt> of every endpoint of the same <tt>(S, G)</tt> in the catalog,
this endpoint included, and from the <tt>manifestPort</tt> of every other
such endpoint, so that no port of an <tt>(S, G)</tt> carries both data and
records, or the records of two flows.  <tt>(S, G, manifestPort)</tt> is a
multicast tuple under the one-namespace rule of
<xref target="multicast-endpoint-format"></xref>.  A receiver MUST ignore
(<xref target="multicast-endpoint-format"></xref>) a protected endpoint whose ports
violate this.  Two ASM endpoints whose <tt>senderAddress</tt> differ MAY share
<tt>groupAddress</tt> and ports; the source check of
<xref target="receiver-enforcement-and-state"></xref> separates them.</li>
<li><strong>streamId</strong> (integer, REQUIRED): 32-bit value chosen per channel
generation, bound into every digest and signature.</li>
<li><strong>senderAddress</strong> (string, REQUIRED on an ASM endpoint, MUST NOT
appear on an SSM endpoint): the address from which the sending
entity transmits this endpoint's flow, <tt>S</tt> of <xref target="digest"></xref>; on an
SSM endpoint <tt>S</tt> is <tt>sourceAddress</tt>.</li>
<li><strong>maxAuthDelayMs</strong> (integer, OPTIONAL, default 200, range 50 to
1000): how long a receiver holds a datagram whose digest has not
arrived.</li>
<li><strong>manifestTimerMs</strong> (integer, OPTIONAL, default 20, at least 1):
the window timer of <xref target="emission-and-cadence"></xref>.</li>
<li><strong>manifestTrack</strong> (string, OPTIONAL): the MoQ track mirroring the
records (<xref target="emission-and-cadence"></xref>).</li>
</ul>
<t>When the <tt>multicast</tt> object carries <tt>auth</tt>, every endpoint of the
<tt>multicast</tt> object MUST carry <tt>auth</tt>; a receiver MUST ignore
(<xref target="multicast-endpoint-format"></xref>) an endpoint that lacks it, so every
multicast datagram it consumes from the sending entity is in a
protected flow and reaches a decoder only through
<xref target="receiver-enforcement-and-state"></xref>.  An endpoint that carries <tt>auth</tt>
when the <tt>multicast</tt> object does not is ignored likewise, since it
names no key.</t>
</section>

<section anchor="receiver-enforcement-and-state"><name>Receiver Enforcement and State</name>
<t>A receiver that implements this profile enforces it: a datagram of a
protected flow is admitted only after its digest matches an entry of a
verified record, and there is no verify-and-report mode.  A receiver
MUST be able to pin &quot;authentication required&quot; for a broadcast
independently of the catalog.  A receiver without the catalog (a
broadcast-only receiver, or native SSM reception without a MoQ
session) MUST be provisioned out of band with the keys, <tt>firstEpoch</tt> included,
and <tt>minEpoch</tt> and, per protected flow, the <tt>streamId</tt>, <tt>S</tt>, <tt>G</tt>, <tt>port</tt>,
<tt>manifestPort</tt> and <tt>bandwidth</tt> the catalog would declare
(<xref target="catalog-signaling"></xref>), and any non-default <tt>maxAuthDelayMs</tt> and
<tt>manifestTimerMs</tt>: the record carries none of them, and a broadcast
carries the flow on those addresses and ports or not at all
(<xref target="broadcast-carriage"></xref>); keys are never taken from a carrier's
signaling.  A
receiver that pins keys takes the key set, <tt>firstEpoch</tt> included, from
the pin rather than from the catalog, and MUST keep with the pin,
across sessions, the highest <tt>epoch</tt> it has verified under each pinned
<tt>kid</tt>; in step 1 below it drops, at join, a record whose <tt>epoch</tt> is
below that value for its <tt>kid</tt>, so a catalog that carries a lower
<tt>minEpoch</tt> cannot reopen an epoch a verified record has closed.  The
pin stores no value read from a catalog: every receiver takes
<tt>minEpoch</tt> from the catalog it fetched, or provisions it with the
keys, and it bounds that session only.  A receiver with no pin is
protected at its first join against an earlier epoch only as far as
that catalog is.  A receiver that implements no
algorithm a flow's records carry verifies nothing on that flow: it
admits nothing and treats the endpoint as one that yields no data
(<xref target="multicast-endpoint-format"></xref>).</t>
<t>On an ASM endpoint a receiver MUST discard, before any parsing or
hashing, a datagram on <tt>port</tt> or <tt>manifestPort</tt> whose IP source
address is not <tt>senderAddress</tt>; behind an AMT relay the address is
that of the encapsulated header (<xref target="multicast-packet-format"></xref>).  On an
SSM endpoint reached natively the network enforces the source and a
receiver SHOULD still discard a datagram whose source address is not
<tt>sourceAddress</tt>; a receiver reached through an AMT gateway MUST
discard it, since nothing upstream of its gateway filters the
encapsulated source address and <xref target="RFC7450"></xref> does not authenticate
Multicast Data messages.
A datagram that passes belongs to the flow <tt>(S, G, port)</tt> or <tt>(S, G,
manifestPort)</tt> with <tt>S</tt> as in <xref target="digest"></xref>.</t>
<t>State per flow: the current <tt>epoch</tt> with the <tt>kid</tt> that set it, the
time at which that epoch became current, per path (<tt>manifestPort</tt> and
the manifest track) the grace period that ends
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the first record of the
current epoch verified on that path or dropped there by the
already-verified lookup of step 1 as its byte-identical copy, and,
until the period on <tt>manifestPort</tt> ends or the previous epoch's
deadline passes, the previous <tt>epoch</tt>, the deadline being the bound
of <xref target="emission-and-cadence"></xref> plus <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>
after the current epoch became current (step 1; a record on
<tt>manifestPort</tt> meets its own path's period and the deadline, one read
from the manifest track the earlier-started of the two periods, which
ends before the deadline); the flow's
<tt>hash_alg</tt>, fixed by its first verified
record; per <tt>(kid, epoch)</tt> the verified <tt>mseq</tt> values above the
replay floor of the path they arrived on, and those verified on the
manifest track for as long as the freshness check of step 1 would
pass a copy of them on <tt>manifestPort</tt>, their <tt>sent</tt> within
<tt>7 s + maxAuthDelayMs</tt> behind the receiver's clock, or that long
after their arrival on the manifest track where the receiver skips
that check, whatever the floor (so that the lookup of step 1 still
meets, and measures the lead from, a <tt>manifestPort</tt> copy arriving
after 64 or more further records were verified on the mirror, and
drops as already verified every copy that check would pass: one
arriving more than the bound of <xref target="emission-and-cadence"></xref> plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the mirror's copy, the
lead standing at the bound, then admits nothing, and the datagrams
it covers expire, as that section says, rather than being verified
anew on <tt>manifestPort</tt>; where the lead stands below the bound, the
first such copy raises it to the bound and handles the record's
pairs again as step 1 says; a receiver that skips the check
verifies anew a copy arriving after the retention and below the
manifest track's replay floor, save a copy of the previous epoch
arriving past that epoch's deadline, which step 1 drops, dropping by
the lookup one that arrives above the floor, as that section also
says), each with the
time and path of its arrival, its <tt>sent</tt>, which that retention
reads, the <tt>hash_alg</tt> digest of the record's bytes,
per pair of the record, whether a datagram has been admitted against
it and, for a record verified on the manifest track whose first copy
on <tt>manifestPort</tt> has not yet arrived, the record's pairs, which a
raise of the lead re-handles (steps 1 and 2), per path the
<tt>highest_mseq</tt> that the records
arriving on it have
raised (step 1) and, once a verified record has <tt>flags</tt> bit 0 set,
the <tt>mseq</tt> that closed it; the verification budgets
of step 1, one for <tt>manifestPort</tt> and one for the manifest track,
each with a count of the records it dropped; the mirror's lead
(<xref target="emission-and-cadence"></xref>), a multiple of <tt>manifestTimerMs</tt> or the
bound, and whether the stream generation has measured one, the bound of that section applying until it has and
zero on a receiver that reads no manifest track or will not join the
multicast path; a pending table of
<tt>(digest, sequence, expiry)</tt> entries, each naming the record and
the pair that inserted it, an entry whose expiry the mirror's lead
or W widened keeping its insertion time as well (step 1);
the digests recorded as admitted, each until the expiry set when
it was recorded, the time of recording plus <tt>maxAuthDelayMs</tt> plus
the mirror's lead then in force, a copy on <tt>manifestPort</tt> that
raises the lead recording again the digests of the marked pairs
it handles (steps 1 and 2); for each open block of a protected
consumer, the number of decodes, at most two, taken with the help
of symbols of unlisted tracks (step 2) that have left it
unrecovered, block state under the bound of <xref target="MOQ-FEC"></xref> Section 9
released with the block (step 2); on a receiver that can request
unicast repair, for each open block that has an FEC deadline, the
admission time of the first packet, source or repair, admitted into
it, or of the packet that stands for it under step 2 (the source
packet of highest SS_ID admitted at or below its lower bounding
packet, or the packet admitted above it where the instance has
admitted none below), and for each base instance the admission time
of the source packet of highest SS_ID it has admitted in the stream
generation, kept under the serial number arithmetic of <xref target="MOQ-FEC"></xref>
Section 7.5 (step 2); two hold queues of
<tt>(digest, datagram, expiry)</tt> entries in arrival order,
<tt>digest</tt> being absent from an entry until the first record of the
flow is verified (step 2): one for the datagrams a MoQ unicast path
delivers and the packets the base instance recovers with the help of
a symbol that path delivered (step 2), and one for the rest.  State
per receiver, shared by its flows: the time of the last catalog
re-fetch that an unknown <tt>kid</tt> triggered (key rotation, below), and
the per-<tt>kid</tt> epoch floor kept with a key pin.  The per-flow state
belongs to the stream generation (<xref target="manifest-record"></xref>): when the
<tt>streamId</tt> the catalog, or a receiver's provisioning, declares for the
endpoint changes, the receiver discards every per-flow item above but
the two verification budgets and their counts and the hold queues
(the current and previous <tt>epoch</tt>, <tt>hash_alg</tt>, the per-<tt>(kid, epoch)</tt>
<tt>mseq</tt> state, the mirror's lead, the pending table with the entries
kept for unicast repair, the admitted digests, the per-block decode
counts and the per-block and per-instance admission times), clears
the <tt>digest</tt> of every held
datagram, and
processes the next record as the first of the flow under the
join-time floors of step 1.</t>
<t>A receiver applies the following, in this order, with no
variable-length interpretation before a cryptographic check:</t>

<ol>
<li><t><strong>Record datagram</strong> (on <tt>manifestPort</tt>, <tt>magic</tt> &quot;MHM2&quot;).  Require
<tt>164 &lt;= len &lt;= 1316</tt> and <tt>(len - 132) mod 32 == 0</tt>.  Read the
fixed-offset integers <tt>magic</tt>, <tt>kid</tt>, <tt>epoch</tt>, <tt>mseq</tt>, <tt>count</tt>,
<tt>hash_alg</tt> and <tt>sent</tt> and the 32 octets of <tt>ctx</tt>.  Drop before any
cryptography if: <tt>magic</tt> differs; <tt>kid</tt> is not in the key set, or
<tt>epoch</tt> is outside its range (<xref target="catalog-signaling"></xref>);
<tt>(kid, epoch, mseq)</tt> is already verified (this drops the second
copy of a record with one lookup and one hash, a copy of the
previous epoch arriving after its grace period included; a
dropped copy whose bytes hash, under the flow's <tt>hash_alg</tt>, to
the digest kept with the verified entry is the record itself and
counts as its arrival on this path: it raises the <tt>highest_mseq</tt>
of the path and starts the path's grace period as a verified
record would, below, and, where the verified copy arrived on the
manifest track and this one is the record's first copy on
<tt>manifestPort</tt> (its pairs, kept until then, are released at this
arrival; a later copy there, the twin or one an injector replays
within the retention, counts as a further arrival on the path and
measures nothing), the interval between the two arrivals, rounded
up to a multiple of <tt>manifestTimerMs</tt> and capped at the bound of
<xref target="emission-and-cadence"></xref>, becomes the mirror's lead if it is the
first measured in the stream generation and raises it if it
exceeds it, a copy that raises it also handling this record and
every record verified on the manifest track within the raised
lead before now whose first copy on <tt>manifestPort</tt> has not yet
arrived (the verified entry keeps their pairs for this, this
record's own bytes carrying its pairs), each pair of
such a record as below for a record on <tt>manifestPort</tt>, under the
raised lead: a pair marked as having admitted a datagram has its
digest recorded as admitted until <tt>now + maxAuthDelayMs</tt> plus
the raised lead; one whose digest is pending has its entry's
expiry set to the insertion time plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> plus the raised lead; one
whose digest is neither admitted nor pending admits a held
datagram or inserts a pending entry with expiry
<tt>now + maxAuthDelayMs + 2 x manifestTimerMs</tt> plus the raised
lead, <tt>now</tt> being its insertion time; and, once every such record
is processed, each datagram held in either hold queue whose
digest is now recorded as admitted is dropped (step 2), so that
the datagrams of every record the mirror delivered under a lead
the receiver had not yet measured, within the bound of
<xref target="emission-and-cadence"></xref>, those of the records mirrored while the
raising copy was in flight included, are still admitted when
they arrive, a late copy of one a pair has admitted is
dropped rather than held, and a copy replayed within the
retention re-admits nothing a pair has admitted
(<xref target="threat-model"></xref>); a dropped
copy whose bytes do not match counts as nothing); <tt>epoch</tt> is
below the current epoch and is not the previous epoch within the
grace period this record meets and before the previous epoch's
deadline, the bound of <xref target="emission-and-cadence"></xref> plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the current epoch
became current (below; a path on which no record of the current
epoch has yet been verified, or dropped by the lookup above as its
byte-identical copy, has started no grace period, so the previous
epoch stays admitted on <tt>manifestPort</tt> until the deadline passes
or, once such a record starts that path's period, until that
period ends, if sooner, and on the manifest track until the
earlier-started of the two paths' periods ends), or, when no
record has yet been
verified, is below
<tt>minEpoch</tt> or below the pinned floor for its <tt>kid</tt>; <tt>mseq &lt;=
highest_mseq(kid, epoch) - 64</tt>, the <tt>highest_mseq</tt> this record
meets (below; replay floor, none before a record of that
<tt>(kid, epoch)</tt> has raised a <tt>highest_mseq</tt> this record meets, no
upper bound);
<tt>mseq</tt> is above the <tt>mseq</tt> that closed the epoch, once a verified
record has closed it; <tt>sent</tt> is more than <tt>7 s + maxAuthDelayMs</tt>
behind, or more than 6 s ahead of, the receiver's UTC clock
(freshness: the sender's 1 s bound and the receiver's 5 s bound,
plus, behind, the truncation of <tt>sent</tt> to whole seconds and the
delivery of the twin; a receiver that widens its hold per
<xref target="emission-and-cadence"></xref> widens the behind tolerance by the same
amount for a record read from the manifest track, and not for one
on <tt>manifestPort</tt>); <tt>count != (len - 132) / 32</tt>; <tt>hash_alg</tt> is not in the
receiver's accepted set, or differs from the flow's <tt>hash_alg</tt> once
one is fixed; or, on a receiver that holds the catalog, <tt>ctx</tt>
differs from the value recomputed from it (a receiver without the
catalog carries <tt>ctx</tt> blind, <xref target="signature-context-binding"></xref>).  The
replay floor and the grace period a record meets are its path's:
a record on <tt>manifestPort</tt> meets the <tt>highest_mseq</tt> and the grace
period that the records arriving on <tt>manifestPort</tt>, verified or
dropped by the lookup above as the record itself, have raised or
started; a record read from the manifest track meets the higher
of the two paths' <tt>highest_mseq</tt> values and the earlier-started
of their grace periods.  The mirror's lead so moves no floor and
no grace period
a multicast copy meets: a record a relay withholds from the
mirror, or one the mirror loses, is tested against the multicast
path's own cadence however far the mirror leads, and, while the
mirror leads by no more than the bound of <xref target="emission-and-cadence"></xref>,
the previous epoch's deadline drops on <tt>manifestPort</tt> only a record
of that epoch reordered more than
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> behind the new epoch's
first record
(<xref target="threat-model"></xref>), and a mirror that lags the multicast path by
the floor brings nothing a receiver still holds.  Charge the
per-flow verification budget, a token
bucket of depth 64 refilled at 1000 tokens per second (a record
read from the manifest track of <xref target="emission-and-cadence"></xref> charges a
second bucket of the same shape, so a flood on <tt>manifestPort</tt>
cannot starve the mirror); drop and
count a record that finds it empty (nothing before the signature
tells a forged record from a genuine one, so a forged record spends
the same token, <xref target="threat-model"></xref>).  Verify the Ed25519 signature over
<tt>signed_message</tt>; drop on failure, and only on success read
<tt>first_seq</tt>, <tt>flags</tt> and the digests.  If <tt>epoch</tt> is above the
current epoch, or this is the first verified record, make <tt>epoch</tt>
current, record when it became current, and make the former current
epoch the previous epoch, with a
grace period on each path that ends
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the first record of
the new epoch that arrives on it and is verified, or dropped by
the lookup above as the record itself (this record starts it on
its own path now; <tt>manifestPort</tt>, while no such record has yet
arrived there, admits the previous epoch until one starts that
path's period and then until that period ends, and the manifest
track until the earlier-started of the two periods ends,
the sender emitting every twin of it before the new epoch's first
record, <xref target="emission-and-cadence"></xref>; neither path admits it past the
previous epoch's deadline, above, which this record sets, so a
path on which the new epoch's records are dropped holds the
previous epoch open no longer than that),
fix the flow's <tt>hash_alg</tt> from this record if none is fixed yet, and
hash every held datagram that has no digest yet; pending entries and held datagrams
of the previous epoch are not flushed and live out their expiry.  A
record of the previous epoch that passes the checks above is
processed under that epoch's own floor and verified set and never
changes the current epoch.
Record <tt>(kid, epoch, mseq)</tt> as verified, with the time and path
of its arrival, its <tt>sent</tt> and the <tt>hash_alg</tt> digest of its
bytes, and raise the <tt>highest_mseq</tt> of the path it arrived on.
For each
<tt>(digest, sequence)</tt> of the
record, in order: if a hold queue holds a datagram with that
digest, admit the oldest such datagram, mark the pair as having
admitted it, remove it from its queue and record its digest as
admitted until <tt>now + maxAuthDelayMs</tt> plus the mirror's lead
(step 2); otherwise insert the pair into the
pending table with expiry
<tt>now + maxAuthDelayMs + 2 x manifestTimerMs</tt> (240 ms at the
defaults), plus the mirror's lead, or W on a receiver that
applies no lead, for a record read from the manifest track
(<xref target="emission-and-cadence"></xref>), an entry so widened keeping its
insertion time.  Once every pair of the record is
processed, drop from both hold queues each datagram still held
whose digest is now recorded as admitted: it is a copy of a
datagram this record admitted, delivered on the other path or
repeated on the same one, which step 2 would not have held after
the admission; its drop is not an expiry and counts toward nothing
(<xref target="threat-model"></xref>).  If <tt>flags</tt> bit 0 is set the epoch is closed at this
<tt>mseq</tt> (<xref target="manifest-record"></xref>).</t>
</li>
<li><t><strong>Data datagram</strong> (on <tt>port</tt>).  Compute <tt>D</tt> with the flow's
<tt>hash_alg</tt> (a datagram that arrives before the first record of the
flow is verified is held without a digest) and look it up in the
pending table, which maps a digest to an ordered list of sequences
(carousels repeat byte-identical datagrams).  Hit: remove the lowest
sequence, admit the datagram, mark the pair of the record that
inserted the entry as having admitted it (step 1) and record <tt>D</tt>
as admitted until <tt>now + maxAuthDelayMs</tt> plus the mirror's lead
(<xref target="emission-and-cadence"></xref>).  Miss: drop the datagram if <tt>D</tt> is so recorded
(a copy of an admitted datagram delivered again, on any path, is
never held); otherwise append <tt>(D, datagram)</tt> to the hold queue of
its path, with expiry <tt>now + maxAuthDelayMs + W</tt>, each queue
bounded to <tt>(maxAuthDelayMs + W) x bandwidth / 8000</tt> octets, W
being the widening of <xref target="emission-and-cadence"></xref> in milliseconds,
zero on the multicast path; drop the newest on overflow and drop
at expiry, so a MoQ unicast path that fills its queue evicts no
multicast datagram.  Repair symbols follow the same rule,
so an injected symbol is dropped before FEC decode.  A receiver MUST
NOT feed a FEC instance, a keyframe overlay or the packet_id 0
signaling consumer with a datagram of a protected flow that it has
not admitted under step 1 or 2, on whatever carrier it arrived
(<xref target="broadcast-carriage"></xref>).  A FEC instance, a keyframe
overlay or the packet_id 0 consumer that has admitted a datagram of
a protected flow is a protected consumer, protected under each flow
a datagram of which it has admitted (one that a source endpoint and
a repair-only endpoint both feed, <xref target="MOQ-FEC"></xref> Section 6.3.6, under
both), and a symbol admitted under any flow of the multicast object
is an admitted symbol in every consumer it reaches: a packet of a
track that the endpoint of a flow the consumer is protected under
lists, reaching it over any path, a MoQ unicast path included, is a
datagram of that flow and is admitted by this step (<xref target="digest"></xref>).
A consumer that has admitted none, because the receiver takes the
track over MoQ unicast alone while its multicast join completes
(<xref target="multicast-endpoint-format"></xref>) or after the failover of
<xref target="multi-path-delivery"></xref>, is a MoQ subscriber outside this profile:
the MoQ session protects its objects, and it MAY verify them
against the records of a <tt>manifestTrack</tt> (<xref target="emission-and-cadence"></xref>);
a consumer protected under one flow of the object is such a
subscriber for the tracks of a flow it is not yet protected under,
as one that a repair-only endpoint feeds is for a source track the
receiver takes over MoQ unicast (<xref target="MOQ-FEC"></xref> Section 6.3.6).
On failover a receiver that reads no manifest track discards its
protected consumers and continues as such a subscriber; one that
does MAY keep them.  A receiver holds an endpoint while it is
joined to its group, reads its <tt>manifestTrack</tt>
(<xref target="emission-and-cadence"></xref>) or receives the broadcast that carries
the flow (<xref target="broadcast-carriage"></xref>), and a consumer is protected
under a
flow only while the receiver holds the flow's endpoint: when the
receiver ceases to hold one, on the failover above or on dropping
a repair layer (<xref target="MOQ-FEC"></xref> Section 6.3.6), it discards that
flow's state as on a <tt>streamId</tt> change (above) and, since no
further record of the flow will arrive, the flow's hold queues
with the datagrams they hold, a drop that is no expiry and counts
toward nothing; the consumer ceases to be protected under the
flow, the symbols it admitted under it staying admitted, and the
endpoint's tracks are, for the consumer, unlisted (below) unless
another flow it is protected under lists them.  At the admission
that makes a consumer
protected under a flow, the receiver discards every packet of a
track that flow's endpoint lists that the consumer still holds and
that it did not admit under step
1 or 2, so that none stands, under the deduplication of item 1 of
<xref target="multi-path-delivery"></xref>, for the admitted copy of the same symbol;
within a protected consumer that deduplication keeps an admitted
symbol over an unadmitted one of the same key.  For a consumer, a
track that no endpoint of a flow it is protected under lists is
an unlisted track, one whose symbols reach the consumer only over
a MoQ unicast path (<xref target="MOQ-FEC"></xref> Section 6.3.6): a track that no
endpoint the receiver holds lists, or one that an endpoint the
receiver holds lists while the consumer is not yet protected
under that endpoint's flow, its join still completing or its
records unread, the multicast copy of such a packet being a
datagram of that flow held by this step.  A symbol of an unlisted
track is not a datagram of any flow the consumer is protected
under: it MAY enter a FEC instance under that path's own
integrity protection, which vouches for the relay and not for the
publisher, neither anchoring nor tripping the fail-closed check of
<xref target="MOQ-FEC"></xref> Section 6.3.9 (that section), and the admission that
makes the consumer protected
under a flow that lists the track ends this for that track, the
discard above dropping each such symbol the consumer still holds
and a provisional block those symbols delimited returning its
source symbols to the undelimited state of <xref target="MOQ-FEC"></xref>
Section 7.4.  The rules of this step from here to
its end, for blocks, decodes and recovered packets, govern a FEC
instance or keyframe overlay only while it is protected under a
flow whose endpoint lists its source track.  One protected under
no such flow, as one that a repair-only endpoint feeds while the
receiver takes the source track over MoQ unicast alone
(<xref target="MOQ-FEC"></xref> Section 6.3.6), the track unlisted or the join of
the endpoint that lists it still completing, is a MoQ subscriber
outside this profile for its blocks: the symbols it admitted
enter its decodes as admitted symbols, its other packets are what
the MoQ session delivers, and <xref target="MOQ-FEC"></xref> Sections 7.4, 9 and
11.4 govern its blocks as those of a consumer outside this
profile, until the admission that makes it protected under a flow
whose endpoint lists the track (a record of that flow, from
<tt>manifestPort</tt> or its <tt>manifestTrack</tt>, admitting a source packet
under step 1 or this step), at which the discard above applies.
Within a protected consumer the block
extent such a symbol signals is provisional: a block that only
such symbols have delimited yields to an admitted repair packet
whose (SS_Start, SSB_length) conflicts with it under <xref target="MOQ-FEC"></xref>
Section 7.4, the receiver discarding the provisional block's repair
symbols, returning its source symbols to the undelimited state of
that section and accepting the admitted packet, so that the
first-accepted rule of that section discards an admitted repair
packet only for conflict with another admitted one; such a symbol
that conflicts with a block an admitted repair packet has
delimited is discarded as that section requires.  Nor is such a
symbol the later signal of <xref target="MOQ-FEC"></xref> Section 9 or the packet
whose receipt anchors a block's ts there, whether or not an
admitted repair packet later delimits the block the symbol
delimited (that block's ts is the receipt time of the first
packet of it, in the order received, that this profile admits),
nor does a
block only such symbols have delimited take any SS_ID out of a
run or stand as the S_prev of the Group range of <xref target="MOQ-FEC"></xref>
Section 7.5: within a protected consumer such a block is held for
decoding only, has no ts and no FEC deadline, whatever packets
are admitted into or above its range before or after the symbol,
and requests no repair, and the SS_IDs in its range that the
receiver lacks are, for <xref target="MOQ-FEC"></xref> Sections 9 and 11.4, runs
between the packets the receiver has admitted and the blocks
admitted packets have delimited, as if the provisional block were
not delimited, each revealed and given its ts only by a packet
admitted above it, whether the symbol delimited the block before
or after that packet, and classified, requested and cancelled as
those sections specify; what the block decodes before such a
run's deadline is admitted by its digest, as below, and closes
the run or part of it.  A relay could otherwise, by one repair
packet whose SS_Start lies above what the publisher has sent,
give the SS_IDs below it a deadline they reach unrecovered, by
one whose range begins at an SS_ID the receiver admitted long
ago, delimit a block that takes its ts from that old admission
and is past its deadline on arrival, by one whose <tt>SSB_length</tt>
reaches past what the publisher is sending, delimit a block that
reaches a deadline before the rest of its range is sent, and
force the repair request of <xref target="MOQ-FEC"></xref> Section 11.4.2 any of
those ways, or, by one whose range covers a run that has a ts,
take the run's deadline and repair request away, or, by one whose
range ends just below a lost SS_ID, narrow the run's Group range
to exclude the block that contains it.  Under the
open-block bound
of <xref target="MOQ-FEC"></xref> Section 9 the receiver discards a block that has
no ts before any block that has a ts, whether or not the track
derives an FEC deadline, so that the
blocks such symbols delimit, however many a relay sends, evict
under a memory cap no block of the multicast path; such a discard
gives the block no deadline and no class, forces no repair
request, drops the block's repair symbols and returns its source
symbols to the undelimited state of <xref target="MOQ-FEC"></xref> Section 7.4.  The
receiver
decodes a block from admitted symbols where they suffice and uses
such symbols only for the shortfall.  A packet the base instance
recovers with such a symbol's help is admitted by its digest like
a received datagram, against the pending entries of its window,
hashed with the base SS_ID its position in the base block implies
(SS_Start + ESI, <xref target="MOQ-FEC"></xref> Section 8.3) appended for the hash
only (<xref target="MOQ-FEC"></xref> Sections 7.3 and 8.5), and on a miss is held in
the MoQ unicast path's hold queue, under that queue's expiry and
bound, since only such a symbol, which that path alone delivers,
made the decode possible, so that its octets and its expiry count
toward nothing of the multicast path.  A recovered packet that
leaves the queue unadmitted, at expiry or on overflow, is not a
symbol the receiver holds for <xref target="MOQ-FEC"></xref> Section 11.4: a block
of which the receiver has not admitted every one of its
SSB_length source symbols, received or recovered, is unrecovered
for that section and for the release below, at its FEC deadline
as at any other time, the receiver keeping the packets it admitted
(a decode none of whose recovered packets is admitted recovers
nothing, a corrupt symbol among those decoded from corrupting
what the decode recovers), and it SHOULD discard the symbols of
unlisted tracks that fed a decode that left the block unrecovered
and decode again when the symbols it then holds suffice, at most
once more per block with such symbols' help: a block that two
such decodes have left unrecovered takes no further symbol of an
unlisted track into a decode, since a relay could otherwise drive
one decode of the block per symbol it sends, while a decode from
admitted symbols alone, and the admission of a held packet, remain
open to it; a held
packet admitted after the deadline recovers the block as a
received datagram admitted then would, and the receiver SHOULD
cancel a repair FETCH then outstanding for it, as <xref target="MOQ-FEC"></xref>
Section 11.4.2 has it for a repair the next RAP makes moot.  A
packet recovered from admitted symbols
alone, by the base instance or a keyframe overlay, is admitted with
them; a packet only the overlay recovers with the help of such a
symbol has no base SS_ID to hash with (<xref target="MOQ-FEC"></xref> Section 7.3) and
is not delivered.  A receiver that can request unicast repair
(<xref target="per-block-unicast-repair"></xref>) keeps, instead of letting them
expire, the pending entries of each open block that has an FEC
deadline: those pending at the admission of the first packet,
source or repair, that the receiver admits into the block, and
those inserted after it (a mirrored record's entries are
inserted up to the lead before the datagrams they cover arrive
on the multicast path, and under the bound of
<xref target="emission-and-cadence"></xref> until the stream generation has measured
a lead, so no span reckoned from the lead in force at the
admission names them all; the admitted packet is not necessarily
the packet
whose receipt set the block's ts in <xref target="MOQ-FEC"></xref> Section 9: a
datagram held on a miss is admitted, by step 1, after one that a
verified record admitted on arrival, and a symbol of an unlisted
track no step admits, nor does one set a ts, so a block only such
symbols have delimited has no deadline and keeps no entries as a
block, the lost SS_IDs in its range keeping theirs as the runs
they are, below), until that
deadline (the receiver keeps the admission time with the block).
A
run of lost SS_IDs that the receiver has not delimited
(<xref target="per-block-unicast-repair"></xref>) is one such block (<xref target="MOQ-FEC"></xref>
Section 11.4) of which it holds no packet, and the receiver
learns of the run, and of the block's deadline, only from a packet
admitted above it (<xref target="MOQ-FEC"></xref> Sections 7.4 and 9; step 2), by
when the run's entries would have expired; so for each base
instance it also keeps the entries pending at the admission of
the source packet of highest SS_ID it has admitted, and those
inserted after it
(keeping that admission time with the instance; a symbol of an
unlisted track, which no step admits, is never
this anchor however high its SS_ID) until it admits a source
packet of the instance above that SS_ID; a source packet admitted
below it moves neither that anchor nor the entries so kept
(highest, above and below in SS_ID, and the gap below, under the
serial number arithmetic of <xref target="MOQ-FEC"></xref> Section 7.5, so that the
anchor follows SS_ID across its wrap to 0).  A gap
in SS_ID between the two reveals lost SS_IDs, of such a run or of
a block that a repair packet alone has delimited (<xref target="MOQ-FEC"></xref>
Section 7.4); each takes the entries so kept as its own from the
admission of the source packet of highest SS_ID admitted at or
below its lower bounding packet (lower and upper in SS_ID order,
<xref target="MOQ-FEC"></xref> Section 11.4.1; a bounding packet can itself be such
a symbol), or of the packet admitted above it where the instance
has admitted none below it;
otherwise the entries whose expiry has passed by the admission of
the packet above are released.  The blocks that the admitted
repair packets a run's Group range FETCH returns delimit take
over the entries so kept; a block that only symbols of an
unlisted track the response returns have delimited is provisional
as above and takes none, the run keeping its entries, against
which the decode of that block admits its recovered packets,
until the run is recovered or its requested repair, the FETCH on
each layer it needs (<xref target="MOQ-FEC"></xref> Section 11.4.2), completes
without recovering it, fails or is cancelled.
They are block state under the bound of <xref target="MOQ-FEC"></xref> Section 9 and
are released when the block is recovered, when its deadline passes
with no repair requested, when a requested repair, the FETCH on
each layer it needs, completes without recovering the block, fails
or is cancelled (<xref target="MOQ-FEC"></xref> Section 11.4.2), or when the block is
discarded under that bound.</t>
</li>
</ol>
<t><strong>Replay.</strong> Receivers in an explicitly configured DVR or replay mode
MAY relax the epoch, floor and freshness checks.  <strong>Clock.</strong> The
freshness check needs a receiver clock synchronized to UTC within
5 s.  A receiver without one MAY skip that check alone; it
then accepts any window of the current epoch at join and has no
protection against replay of the epoch's earlier windows until its
first verified record sets the floor (<xref target="threat-model"></xref>).</t>
<t><strong>Key rotation.</strong> A key change is an epoch change
(<xref target="manifest-record"></xref>).  The publisher adds the next <tt>kid</tt> to the
catalog with the <tt>firstEpoch</tt> at which it will sign, at least one
catalog lifetime ahead; signs with it from that epoch; and raises
<tt>minEpoch</tt>.  A receiver that holds that catalog accepts the retired
<tt>kid</tt> below the successor's <tt>firstEpoch</tt> only, so records the retired
key signs are admitted on <tt>manifestPort</tt> until that path's grace
period for the new epoch ends, or while that path has started none,
but never past the bound of <xref target="emission-and-cadence"></xref> plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the new epoch became
current, and on the manifest track only within the earlier-started
of the two paths' periods (step 1 of
<xref target="receiver-enforcement-and-state"></xref>); a receiver that verifies no
record of the new epoch keeps the epoch before it current, which the
retired key still signs, and a
receiver whose catalog or pin predates the rotation is exposed to the
retired key until it re-fetches or is re-provisioned
(<xref target="threat-model"></xref>).  A record with an unknown <tt>kid</tt> triggers at most
one catalog re-fetch per 30 s per receiver; until then such records
are dropped.  Whenever a re-fetch or a re-provisioning changes the key
set or a <tt>kid</tt>'s range, the receiver re-applies the range check of
<xref target="catalog-signaling"></xref> to the <tt>(kid, epoch)</tt> that set each flow's
current epoch; where it now fails, the receiver discards that flow's
state as it does on a <tt>streamId</tt> change (above), the <tt>digest</tt> of
every held datagram included, and returns to the join state of step
1, so a record a retired key signed before the re-fetch cannot hold
the flow afterwards.  Revocation is key removal,
effective within one catalog lifetime.</t>
<t><strong>Cost.</strong> A datagram from another source costs one address comparison;
one that spoofs <tt>S</tt>, or that a relay delivers on a MoQ unicast path,
costs one hash, two lookups and, on a miss of both, space in its
path's hold queue until expiry, so a copy of an admitted datagram costs one hash and two
lookups; an injected record costs at most one
verification inside the budget, a replayed one whose
<tt>(kid, epoch, mseq)</tt> is still verified one lookup and one hash
whatever its age (the already-verified lookup of step 1 precedes the
freshness check), one outside the verified set and outside the
freshness tolerance that lookup alone, a miss, before the epoch
floor, the replay floor or the freshness check drops it, the second
copy of a verified record one lookup and one hash plus, where the
first arrived on the manifest track and this is the first copy on
<tt>manifestPort</tt> and raises the mirror's lead, the re-handling of its
own pairs and of the pairs kept for the records mirrored while it
was in flight, which no later copy of the record repeats and which
the lead's rise by multiples of <tt>manifestTimerMs</tt>
(<xref target="emission-and-cadence"></xref>) bounds to <tt>5000 / manifestTimerMs</tt> raises
in a stream generation, 250 at the default, each over at most the
records the manifest track's bucket admitted within the raised lead
(step 1),
and a corrupt symbol
of an unlisted track
at most two decodes of its block.  With one verification per 37
datagrams, a Cortex-A53 without
SHA2 instructions spends about 0.26 of a core at 100 Mbit/s and at
most 0.43 of a core under a record flood at a budget of 1000/s, or
0.6 of a core on a receiver that also reads the manifest track, whose
second bucket admits another 1000/s; a core with SHA2 instructions
spends a fraction of that.</t>
</section>

<section anchor="threat-model"><name>Threat Model</name>
<t>This profile protects against off-path and on-segment UDP injection,
against corrupted or attacker-injected FEC symbols, and against
replay: a <tt>(record, datagrams)</tt> pair cannot be moved to another
endpoint, stream generation, broadcast, key or epoch
(<xref target="signature-context-binding"></xref>); an earlier epoch is rejected at join
by <tt>minEpoch</tt> (<xref target="catalog-signaling"></xref>) and, on a receiver that pins
keys, by the epoch floor kept with the pin, and after the first
verified record by the current epoch, the previous epoch admitted
on <tt>manifestPort</tt> until that path's grace period ends, or while that
path has started none, and never past the previous epoch's deadline,
the bound of <xref target="emission-and-cadence"></xref> plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the current epoch became
current, and on the manifest track
within the earlier-started of the two paths' periods
(<xref target="receiver-enforcement-and-state"></xref>); and
within an epoch a receiver with a UTC-synchronized clock admits no window whose record was
emitted more than <tt>7 s + maxAuthDelayMs</tt> before receipt, so the verification
budget cannot be spent with stale genuine records and the replay floor
of <xref target="receiver-enforcement-and-state"></xref> takes over after the first
verified record.  A receiver that skips the freshness check can be fed
the current epoch's earlier windows at join, and an injector that
keeps its budget spent can hold it there; a unicast receiver that
widens its tolerance by W (<xref target="emission-and-cadence"></xref>) can likewise be
started at a window up to W old, 5 s at most, by the relay that
buffers for it.  What a relay delivers to a consumer, of the tracks
a flow's endpoint lists, before the admission that makes the consumer
protected under that flow is discarded at that admission unless
admitted by then (<xref target="receiver-enforcement-and-state"></xref>), so a relay
that leads the multicast path during the join cannot leave its own
copy of a symbol in place of the admitted one.  A relay that leads
the multicast path and withholds records from the mirror moves no
replay floor and no grace period the multicast copies of those
records meet, each path keeping its own, and, leading it by no more
than the bound of <xref target="emission-and-cadence"></xref>, makes the previous
epoch's deadline drop only a copy reordered more than
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> behind the new epoch's first
record (step 1 of
<xref target="receiver-enforcement-and-state"></xref>), so a healthy multicast path
loses no window to what the mirror omits, absent the injector of the
availability paragraph below, and a relay that lets the
receiver measure a small lead and then leads by more, within the
bound of <xref target="emission-and-cadence"></xref>, gains nothing either, the copy on
<tt>manifestPort</tt> that raises the lead re-admitting
or re-inserting, under the raised lead, ahead of their datagrams,
the digests of that
record and of the records mirrored while it was in flight, for the
pairs that have admitted nothing and have no pending entry, widening
the pending entries of those records by the raised lead, and
recording again as admitted, under the raised lead, the digests of
the pairs that have, so that a copy an injector replays within the
retention, which measures nothing, re-admits no datagram and a late
multicast copy of a datagram the relay delivered is dropped rather
than held
(step 1 of <xref target="receiver-enforcement-and-state"></xref>).  Because every endpoint of a
sending entity that carries <tt>auth</tt> is protected
(<xref target="catalog-signaling"></xref>), an endpoint without <tt>auth</tt> cannot share a FEC
instance, a keyframe overlay or the signaling sub-flow with a
protected one.</t>
<t>Catalog rewrite and key substitution are defended only for receivers
that hold an out-of-band key pin.  The digest algorithm is inside the
signed record, so a rewrite can remove <tt>auth</tt>, substitute keys or
lower <tt>minEpoch</tt>; the pin catches the first two and the per-<tt>kid</tt> epoch
floor kept with it (<xref target="receiver-enforcement-and-state"></xref>) the third,
except at a receiver's first join under a <tt>kid</tt>, where a lowered
<tt>minEpoch</tt> admits records of an earlier epoch signed with a
still-listed <tt>kid</tt> until a record of the current epoch is verified,
and after that as the previous epoch (step 1 of
<xref target="receiver-enforcement-and-state"></xref>), so rotated-out keys SHOULD be
removed promptly.  A rewrite that
raises <tt>minEpoch</tt> costs a receiver, pinned or not, that session's
data and nothing more, since neither carries a catalog value past the
session (<xref target="receiver-enforcement-and-state"></xref>).
Rewriting any other member of the endpoint or its <tt>auth</tt>
only makes genuine records fail the signature or <tt>ctx</tt> check, or
resizes the hold queues and pending expiry (<tt>bandwidth</tt>,
<tt>maxAuthDelayMs</tt>, <tt>manifestTimerMs</tt>), which a receiver bounds by local
policy (<xref target="catalog-as-attack-surface"></xref>).</t>
<t>The profile protects integrity, not availability.  A forged record
passes every check of step 1 of <xref target="receiver-enforcement-and-state"></xref> up
to the signature, so each one spends a token of the verification
budget: an attacker who sustains more than 1000 records per second on
the manifest port (about 1.3 Mbit/s of 164-octet records) takes a
share of the budget proportional to its rate, and at several times
that rate both copies of a window lose their tokens and the flow
yields no data.  The budget bounds the receiver's CPU, and the
lead's rise by multiples of <tt>manifestTimerMs</tt> bounds the
re-handling a raise costs outside it (Cost,
<xref target="receiver-enforcement-and-state"></xref>); it does not
raise the cost of this attack, and the record flood is not the
cheapest availability attack on every protected flow: a datagram on
the data port that matches no pending entry is held on the
multicast path until it expires (step 2 of
<xref target="receiver-enforcement-and-state"></xref>), and those expiries are loss for
the failover below, so an injector that sustains, with the same
access as the record flood, the loss fraction at which the receiver
fails over moves it to the unicast fallback for less than the 1000
records per second the record flood needs wherever that fraction of
the flow's packet rate is below it, as on every audio flow and most
low-rate video; only evicting genuine datagrams, by overflowing the
multicast path's hold queue, needs the endpoint's bandwidth (a relay
on a MoQ unicast path can fill only its own path's queue, without
spoofing, and that path's availability is already its to withhold; a
symbol of an unlisted track it delivers gives no block or run a ts or
a deadline, whatever admitted packets lie in its range, takes no
SS_ID out of a run, and delimits a block that is decoded only, never
requested, the SS_IDs it covers that the receiver lacks staying runs
that admitted packets alone reveal, and the
blocks only such symbols delimit are discarded under the open-block
bound of <xref target="MOQ-FEC"></xref> Section 9 before any block that has a ts,
so it forces no repair request, evicts no block of the multicast
path and fails no check of <xref target="MOQ-FEC"></xref> Section 6.3.9 closed, step 2
of <xref target="receiver-enforcement-and-state"></xref>; a
corrupt symbol of an unlisted track it delivers leaves unrecovered
only a block the admitted symbols could not recover alone, costs at
most two decodes of that block, and what a decode it corrupts
recovers is held on, and expires from, that relay's own queue, step
2 of <xref target="receiver-enforcement-and-state"></xref>).
A receiver that reads the manifest track
has a second bucket of the same shape, so its ceiling is 2000
verifications per second, half of them spendable by its relay without
spoofing anything (Cost, <xref target="receiver-enforcement-and-state"></xref>).  On an SSM endpoint
reached natively either flood must spoof <tt>S</tt> or be on path; on an ASM
endpoint any host that can send to the group suffices, and so it does
behind an AMT gateway, where a flood need only spoof the relay's
address toward the gateway (<xref target="receiver-enforcement-and-state"></xref>).  With
that access an injector forges nothing: a genuine record it reads
from the manifest track, as any subscriber can, and sends on
<tt>manifestPort</tt> more than <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>
(240 ms at the defaults) before the multicast path delivers the
datagrams it covers is verified there with unwidened pending
entries, which expire before those datagrams arrive, so they are
held, and the sender's own copy, dropped by the already-verified
lookup, admits nothing (step 1 of
<xref target="receiver-enforcement-and-state"></xref>); one record per window is then
loss of every window for the failover below, at most 50 a second
at the default <tt>manifestTimerMs</tt> on a flow the timer governs and
one per 37 datagrams on a flow above 1850 datagrams a second
(<xref target="emission-and-cadence"></xref>), less than the data-port injection above
needs wherever the failover fraction of the flow's packet rate
exceeds that window rate, above 50 a second on the former and above
a thirty-seventh of the packet rate on the latter, at a lead of at
most 64 windows; at a lead of <tt>L</tt> windows above that, the omission
below costs one record per <tt>L - 63</tt> windows.  On a receiver that
reads the manifest track the outcome turns on arrival order.
For a record the receiver's mirror delivers before the injector's
copy, that copy, replayed from the mirror ahead of the sender's, is
the record's first copy on <tt>manifestPort</tt> and measures the lead, so
the sender's copy and the twin measure nothing and the re-handling
that a raising copy affords (step 1 of
<xref target="receiver-enforcement-and-state"></xref>) is not afforded against it.
Where no larger interval has been measured in the stream generation
this pins the lead at the injector's replay interval, rounded up to
a multiple of <tt>manifestTimerMs</tt> as step 1 rounds it, so the entries
of every later record the mirror delivers are widened by that lead
alone and are short of their datagrams wherever the mirror delivers
the record more than that lead plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> before the multicast path
delivers the datagrams it covers, which the geometry of the attack
(the injector's copy preceding those datagrams by more than
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>) meets up to the rounding,
under <tt>manifestTimerMs</tt>: they are held and expire as a record the
injector's copy brings to <tt>manifestPort</tt> first does below, the
attack succeeding for every such record whose first copy on
<tt>manifestPort</tt> the injector keeps supplying, with no race against
the mirror to win.  It ends at the first such record whose first
copy on <tt>manifestPort</tt> is the sender's (the injector idle, or its
copy lost or arriving after the sender's): that copy measures the
interval from the mirror's delivery to its own arrival, which the
geometry puts above the pinned lead, raises the lead to it (capped
at the bound of <xref target="emission-and-cadence"></xref>) and affords the re-handling
of step 1 of <xref target="receiver-enforcement-and-state"></xref>, so that from then
the next case applies.  A lead the sender's copies measured larger,
before the injector began or at such a record, is never lowered
(<xref target="emission-and-cadence"></xref>) and keeps widening the entries of mirrored
records, so their datagrams are admitted on arrival and the attack
fails for such a record while the interval from the mirror's
delivery of a record to the multicast path's delivery of the
datagrams it covers stays within that lead plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>; an interval that later grows
past it is not absorbed, since the sender's copy that would have
raised the lead and afforded that re-handling is dropped by the
lookup and measures nothing, so those datagrams are held and expire
as below.  A record the injector's copy brings to <tt>manifestPort</tt>
before the receiver's mirror delivers it is verified there with
unwidened entries whatever lead the receiver has measured, and the
mirror's later copy, dropped by the already-verified lookup,
measures nothing and widens nothing (step 1), so its datagrams are
held and expire as on a receiver that reads no manifest track; an
injector wins that race on every record where its copy reaches the
receiver sooner than the receiver's mirror does, its read of the
manifest track and its injection path to the receiver counted
together against the mirror's delivery delay.  The
injector's copy also raises the <tt>highest_mseq</tt> of <tt>manifestPort</tt> and
starts its grace period early, which drops the sender's copies of a
record only where the injector's copies lead the multicast path at
the receiver by 64 windows, less the windows by which the record's
twin lags it where the record's own copy was lost on the multicast
path (two at the RECOMMENDED placement of
<xref target="emission-and-cadence"></xref>, up to 63 under its bound, which assumes
that only the sender's records raise the floor of step 1), and
those of a record of the previous epoch once a grace period so
started has run.  The first needs the injector's copies to lead the
multicast path at the receiver by that many windows; the second, to
lead it by more than <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> (240 ms
at the defaults), a lead within the bound of
<xref target="emission-and-cadence"></xref> on any flow: the injector's copy of the new
epoch's first record starts the period that long before the
multicast path delivers that record, which the last twins of the
previous epoch immediately precede (<xref target="emission-and-cadence"></xref>), so a
record of the previous epoch, its own copy or its twin, that
arrives on <tt>manifestPort</tt> within the excess of the lead over the
period before that record is dropped.  A copy the receiver's own
mirror delivers meets the higher floor and the earlier-started
period (step 1), so it is dropped as well where the injector's
copies lead that mirror by as much, by 64 windows for the first
drop and by more than the period for the second; a window expires
where every copy of its record is so dropped, the injector having
sent none ahead, whether its mirror lost the record, its relay
withheld it or it omitted the record itself, and the receiver's
mirror delivering none or a late one.  The omission is the cheaper
attack where the lead allows it: a record of mseq <tt>k</tt> the injector
sends raises <tt>highest_mseq</tt> to <tt>k</tt>, so the sender's copies of every
record of mseq at most <tt>k - 64</tt> whose copies have yet to arrive are
dropped, and an injector whose copies lead the multicast path at
the receiver by <tt>L</tt> windows, <tt>L</tt> above 64, loses every such window,
on a receiver that reads no manifest track or whose mirror those
copies also lead by at least as much, with one record per <tt>L - 63</tt>
windows, against the one record per window the replay above costs;
where they lead the receiver's mirror by fewer windows, <tt>M</tt>, the
records the mirror delivered before the injector's are verified
there, their sender's copies dropped by the already-verified
lookup, which precedes the floor in step 1, and their windows
admitted as a record whose first copy on <tt>manifestPort</tt> is the
sender's has it above: that copy, dropped as already verified but
the record's first on that port, measures the interval from the
mirror's delivery to its own arrival and, where that exceeds the
lead in force, raises it and affords the re-handling of step 1,
the entries that lead widened admitting their datagrams on arrival
where it does not; so each record it sends loses only the <tt>M - 63</tt>
windows the mirror delivers after it, none of them where <tt>M</tt> is
below 64 and one, the cost of the replay, where <tt>M</tt> is 64, and
whichever of the <tt>L - 63</tt> the receiver's mirror delivers none of,
whose sender's copies alone arrive and are dropped by the higher
floor whatever <tt>M</tt> is, as the expiry above has it.
The windows of its own records follow the arrival-order cases
above: on a receiver that reads no manifest track or whose mirror
the injector's copy precedes, they are lost by that replay where
the lead also exceeds the period and admitted, by the unwidened
entries of its copies, where it does not, as on a flow the count
cap governs (<xref target="emission-and-cadence"></xref>) whose rate, above about 9,900
datagrams a second at the defaults, closes 64 windows within
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>, so that an injector leading
by more than 64 windows but by less than the period harms nothing
by replaying and costs a window only by omitting its record; where
the receiver's mirror delivered the record first, they are admitted
by the entries the measured lead widened, within that lead plus the
period.  The
injector chooses which mirror it reads but makes no mirror
deliver earlier, and the statements of this section about a relay
assume no such sender on the group.  Datagrams
that
expire from the multicast path's hold queue are loss for the failover
of <xref target="multicast-endpoint-format"></xref> and <xref target="multi-path-delivery"></xref>, those
that expire from the unicast path's queue are not, a held copy that
step 1 drops because a record admitted another copy of the same
datagram expires from neither, and, absent such an injector, a relay
that leads the multicast path by no more than the bound of
<xref target="emission-and-cadence"></xref> produces none, its admitted digests and
pending entries outliving the lead the receiver applies, which that
bound caps (step 1),
the copy on <tt>manifestPort</tt> that raises that lead, when the relay
leads by more than the receiver has measured, re-admitting or
re-inserting, under the raised lead, before their datagrams arrive,
the digests of that
record and of the records mirrored while it was in flight, for the
pairs that have admitted nothing and have no pending entry, widening
the pending entries of those records by the raised lead, and
recording again as admitted, under the raised lead, the digests of
the pairs that have, so that the late multicast copies of datagrams
the relay delivered are dropped as copies rather than
held, and what the relay delivers or withholds on the mirror
moving no replay floor and no grace period a multicast copy meets
(step 1 of <xref target="receiver-enforcement-and-state"></xref>); a receiver
SHOULD also count records that
the <tt>manifestPort</tt> bucket dropped for want of a token toward that
failover, so a sustained record flood ends in the unicast fallback of
<xref target="emission-and-cadence"></xref>, a MoQ subscription outside this profile
unless the receiver verifies it against the records of a
<tt>manifestTrack</tt> (step 2 of <xref target="receiver-enforcement-and-state"></xref>), rather
than in silence.
Records the manifest-track bucket dropped count toward nothing of the
multicast path: a relay that floods the mirror could otherwise move a
receiver with a healthy multicast path onto itself.  A receiver that holds the catalog naming the successor key accepts
the retired <tt>kid</tt> only below the successor's <tt>firstEpoch</tt>
(<xref target="catalog-signaling"></xref>), and records of the previous epoch on
<tt>manifestPort</tt> until that path's grace period ends, or while that
path has started none, and never past the bound of
<xref target="emission-and-cadence"></xref>
plus <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the new epoch
became current, and on the manifest track within the earlier-started
of the two paths' periods (step 1), so for that receiver a key
retired at an epoch boundary signs records admitted on a path for at
most <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the new epoch's
first record is verified there, or dropped there as its
byte-identical copy, and on any path for at most the bound of
<xref target="emission-and-cadence"></xref> plus <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>
(5.24 s at the defaults) after the new epoch became current, however
many of the new epoch's records an attacker drops on <tt>manifestPort</tt>.
An attacker that drops
every record of the new epoch on each path a receiver reads, the
multicast path alone on a receiver that reads no manifest track,
keeps the epoch before the change current at that receiver, and,
holding the retired key, signs records that receiver admits until a
record of the new epoch reaches it, a catalog removes the retired
<tt>kid</tt> or the
receiver is re-provisioned, <tt>minEpoch</tt> binding only a join.  A
receiver with an older catalog is exposed to the
retired key until it re-fetches, one catalog lifetime plus the 30 s
re-fetch interval of <xref target="receiver-enforcement-and-state"></xref> at most, and a
pinned receiver until it is re-provisioned.</t>
<t>Losing both copies of a window leaves at most 37 datagrams
unauthenticated, which expire from the hold queues.  Latency of
authentication is bounded by <tt>maxAuthDelayMs</tt> on the multicast path
and by <tt>maxAuthDelayMs + W</tt> on a MoQ unicast path, W being the
widening of <xref target="emission-and-cadence"></xref>.  Digest <tt>i</tt> of a record
covers <tt>sequence = first_seq + i</tt>, so loss and reordering within a
flow are detectable (<xref target="I-D.ietf-mboned-multicast-security"></xref> Section
2.3), and no mode of this profile delivers an unauthenticated datagram
(Section 2.4.4 of that document).</t>
</section>

<section anchor="broadcast-carriage"><name>Broadcast Carriage</name>
<t>Hybrid receivers that also hold the MoQ catalog verify datagrams of
the flow from any path, a MoQ unicast path included, against the same
records (<xref target="digest"></xref>); a symbol of an unlisted track, one that no
endpoint of a flow the consumer is protected under lists, is not a
datagram of any such flow, and what it helps the base instance
recover is admitted by digest (<xref target="receiver-enforcement-and-state"></xref>).  A
broadcast carries the flow as an MMTP service (<xref target="ATSC-A331"></xref>
Section 8.1.2.1; <xref target="ARIB-B60"></xref>): the flow's datagrams are the MMTP
packets of one MMTP session, carried as the UDP datagrams the sender
emits, each the whole MoQ UDP payload, trailer included, on the
endpoint's <tt>S</tt>, <tt>G</tt> and <tt>port</tt>, and the records are the sender's
record datagrams on <tt>manifestPort</tt> of the same <tt>S</tt> and <tt>G</tt>, both
copies (<xref target="emission-and-cadence"></xref>), carried in the same IP multicast
of the broadcast.  A broadcast that re-addresses either flow, or
carries the datagrams in any other form, is not covered: the
pseudoheader binds <tt>S</tt>, <tt>G</tt> and the ports (<xref target="digest"></xref>), and a record
verifies as it was signed or not at all.  A broadcast-only receiver
reads both UDP flows of the broadcast, forms the pseudoheader and
<tt>signed_message</tt> from the endpoint's <tt>S</tt>, <tt>G</tt>, <tt>port</tt>, <tt>streamId</tt>
and <tt>manifestPort</tt>, and holds those values and the keys by
provisioning (<xref target="receiver-enforcement-and-state"></xref>): the SLS of the
service describes the MMTP session (<xref target="ATSC-A331"></xref> Section 7.2) and
nothing of this profile, so <tt>manifestPort</tt> and the rest come from
provisioning alone, and this document specifies no in-band carriage
of them.  A record so read is a record on <tt>manifestPort</tt> for step 1
of <xref target="receiver-enforcement-and-state"></xref>, as on any multicast path.</t>
</section>

<section anchor="hash-algorithms"><name>Hash Algorithms</name>
<t><tt>hash_alg</tt> is a Suite ID of the IANA Named Information Hash Algorithm
Registry (<xref target="RFC6920"></xref> Section 9.4), a value from 0 to 63; values 64
to 255 are not Suite IDs and MUST NOT be sent.  Only a suite whose
output is 256 bits is usable, since every digest field is 32
octets; the truncated suites MUST NOT be used.  Suite 1, sha-256
<xref target="FIPS180-4"></xref>, is mandatory to implement.  A receiver MAY implement
other 256-bit suites; it drops a record whose <tt>hash_alg</tt> it does not
implement (<xref target="receiver-enforcement-and-state"></xref>).</t>
</section>
</section>

<section anchor="catalog-as-attack-surface"><name>Catalog as Attack Surface</name>
<t>The multicast catalog extension directs receivers to join multicast
groups and to dial network sources.  A hostile or compromised
catalog is therefore an attack vector:</t>

<ul>
<li><t><strong>Join amplification</strong>: Catalog-directed joins create IGMP/MLD
and multicast routing state on intermediate routers
(Section 4.1).  A catalog listing many endpoints, or rapidly
changing endpoints, can exhaust router state or subscribe
receivers to unwanted high-bandwidth traffic.  Receivers MUST
validate that each <tt>groupAddress</tt> falls within the multicast
address ranges given in Section 4.1 before joining, and SHOULD
bound the number of concurrent joins performed on behalf of a
single catalog.</t>
</li>
<li><t><strong>Attacker-chosen dial-out</strong>: The <tt>relay</tt> field of an AMT network
source is an address the receiver dials over UDP.  A hostile
catalog can direct traffic at arbitrary targets (traffic
reflection) or at internal infrastructure (address-space
probing).  Receivers and relays MUST treat catalog-supplied
endpoint and relay addresses as untrusted input and validate them
against local policy (such as an allowlist of permitted relays or
networks) before dialing, and SHOULD reject loopback, link-local,
and otherwise non-routable relay addresses unless explicitly
configured.</t>
</li>
<li><t><strong>Resource claims</strong>: <tt>bandwidth</tt>, <tt>maxAuthDelayMs</tt> and
<tt>manifestTimerMs</tt> size the hold queues and pending table of
<xref target="receiver-enforcement-and-state"></xref> and, with the manifest track's
verification budget, the records verified on that track which a
receiver retains for as long as the freshness check of step 1
would pass a copy of them on <tt>manifestPort</tt>, each with the pairs
kept until its first <tt>manifestPort</tt> copy arrives; the widening W
of <xref target="emission-and-cadence"></xref> enlarges the unicast path's queue and,
on a receiver that applies no lead, the pending expiry of a
mirrored record's entries, and the mirror's lead that expiry, the
admitted-digest set and the entries kept for unicast repair (step
2, those pending at an admission), both receiver-chosen and capped
at 5 s.  A
receiver bounds all of these
by local policy, and the
pending entries it keeps for unicast repair by the open-block bound
of <xref target="MOQ-FEC"></xref> Section 9.</t>
</li>
</ul>
</section>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document has no IANA actions.  <tt>hash_alg</tt> values are Suite IDs
of the Named Information Hash Algorithm Registry (<xref target="RFC6920"></xref>
Section 9.4), which this document does not change, and the multicast
catalog extension (Section 4) requires no registration.</t>
</section>

</middle>

<back>
<references><name>References</name>
<references><name>Normative References</name>
<reference anchor="ATSC-A322" target="https://www.atsc.org/wp-content/uploads/2026/06/A322-2026-06-Physical-Layer-Protocol.pdf">
  <front>
    <title>Physical Layer Protocol</title>
    <author>
      <organization>ATSC</organization>
    </author>
    <date year="2026" month="June"></date>
  </front>
  <seriesInfo name="ATSC" value="A/322:2026-06"></seriesInfo>
</reference>
<reference anchor="ATSC-A331" target="https://www.atsc.org/atsc-documents/3312017-signaling-delivery-synchronization-error-protection/">
  <front>
    <title>Signaling, Delivery, Synchronization, and Error Protection</title>
    <author>
      <organization>ATSC</organization>
    </author>
    <date year="2026" month="April"></date>
  </front>
  <seriesInfo name="ATSC" value="A/331:2026-04"></seriesInfo>
</reference>
<reference anchor="FIPS180-4" target="https://doi.org/10.6028/NIST.FIPS.180-4">
  <front>
    <title>Secure Hash Standard (SHS)</title>
    <author>
      <organization>National Institute of Standards and Technology</organization>
    </author>
    <date year="2015" month="August"></date>
  </front>
  <seriesInfo name="FIPS PUB" value="180-4"></seriesInfo>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-moq-msf.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-moq-transport.xml"/>
<reference anchor="ISO.23008-1" target="">
  <front>
    <title>Information technology - High efficiency coding and media delivery in heterogeneous environments - Part 1: MPEG media transport (MMT)</title>
    <author>
      <organization>ISO/IEC</organization>
    </author>
    <date year="2023"></date>
  </front>
  <seriesInfo name="ISO/IEC" value="23008-1:2023"></seriesInfo>
</reference>
<reference anchor="MOQ-FEC" target="">
  <front>
    <title>Forward Error Correction for Media over QUIC</title>
    <author fullname="Omar Ramadan" initials="O." surname="Ramadan">
      <organization>Blockcast</organization>
    </author>
    <date year="2026" month="October" day="26"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-ramadan-moq-fec-01"></seriesInfo>
</reference>
<reference anchor="MOQ-MMT" target="">
  <front>
    <title>MPEG Media Transport (MMT) Packaging for Media over QUIC</title>
    <author fullname="Omar Ramadan" initials="O." surname="Ramadan">
      <organization>Blockcast</organization>
    </author>
    <date year="2026" month="October" day="26"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-ramadan-moq-mmt-01"></seriesInfo>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.4607.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6763.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6920.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7450.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8032.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8777.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9776.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9777.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="ARIB-B60" target="">
  <front>
    <title>MMT-Based Media Transport Scheme in Digital Broadcasting Systems</title>
    <author>
      <organization>ARIB</organization>
    </author>
    <date></date>
  </front>
  <seriesInfo name="ARIB STD-B60" value="Version 1.14"></seriesInfo>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-mboned-ambi.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-mboned-multicast-security.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-moq-cmsf.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-moq-loc.xml"/>
<reference anchor="QUIC-MULTICAST" target="https://datatracker.ietf.org/doc/draft-jholland-quic-multicast/">
  <front>
    <title>Multicast Extension for QUIC</title>
    <author fullname="Jake Holland" initials="J." surname="Holland">
      <organization>Akamai Technologies, Inc.</organization>
    </author>
    <author fullname="Lucas Pardue" initials="L." surname="Pardue"></author>
    <author fullname="Max Franke" initials="M." surname="Franke">
      <organization>TU Berlin</organization>
    </author>
    <author fullname="Kyle Rose" initials="K." surname="Rose">
      <organization>Akamai Technologies, Inc.</organization>
    </author>
    <date year="2026" month="July" day="6"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-jholland-quic-multicast-09"></seriesInfo>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9706.xml"/>
<reference anchor="WICG-DirectSockets" target="https://wicg.github.io/direct-sockets/">
  <front>
    <title>Direct Sockets API</title>
    <author fullname="Andrew Rayskiy" initials="A." surname="Rayskiy">
      <organization>W3C Community Group Draft Report</organization>
    </author>
    <date year="2026"></date>
  </front>
</reference>
</references>
</references>

<section anchor="test-vectors-bc-provenance-version-2"><name>Test Vectors: bc-provenance Version 2</name>
<t>This appendix is informative.  It gives one worked <tt>bc-provenance</tt>
version 2 example with <tt>hash_alg</tt> 1 (sha-256, <xref target="hash-algorithms"></xref>):
one endpoint, key and two datagrams and one signed record with
<tt>count</tt> 2; its twin is byte-identical.
Octet strings are hexadecimal, the IPv4 addresses dotted decimal and
other integers decimal unless prefixed 0x; the Ed25519 secret seed
is included only so the vectors are reproducible.</t>
<t>Common inputs:</t>

<artwork><![CDATA[ed25519 secret seed : 000102030405060708090a0b0c0d0e0f
                      101112131415161718191a1b1c1d1e1f
ed25519 publicKey   : 03a107bff3ce10be1d70dd18e74bc099
                      67e4d6309ba50d5f1ddc8664125531b8
S, G                : 198.51.100.7, 232.10.20.30 (IPv4)
port                : 5004 (src_port = dst_port)
manifestPort        : 5005
streamId            : 2864434397 (0xaabbccdd)
kid, epoch, mseq    : 1, 17, 1
first_seq           : 1000
flags               : 0x00
sent                : 1792972800 (0x6ade9800, 2026-10-26T00:00:00Z)
namespace           : ["example.arena"] (f = 1)
endpoint packetIds  : [1, 2]
params (for params_hash):
  codec="avc1.640028" timescale=90000 groupDurationTicks=3000
  fecAlgorithm="raptorq" sourceSymbols=32 repairSymbols=16
  symbolSize=1312 interleaveDepthMs=133
datagram 0 payload  : 0x11 repeated 1400 times
datagram 1 payload  : 0x22 repeated 1400 times
pseudoheader (20)   : c6336407e80a141e00110578138c138c
                      aabbccdd
signed_message prefix (32 octets, "mcast-manifest-v2" NUL
streamId S G manifestPort):
                      6d636173742d6d616e69666573742d76
                      3200aabbccddc6336407e80a141e138d
]]></artwork>
<t>The signed message is the 32-octet prefix followed by the first 132
octets of the record.</t>

<artwork><![CDATA[D_0                 : 7ead4d8be7d24e08d7075bbac8fbe777
                      9ab3b8f1c47d3f8be4d2a514f2ec2069
D_1                 : 6f7d9007e81b73c9f54aff8c2f66bd6e
                      8956bfa7e001789b986c328f9ed78751
params_hash         : 1f652ba84e4a44425da55ddffb24c079
                      1a3851a1d1022b4b47b30881146edf7c
ctx                 : fe68177dd329ee6fdd668cb43ebdeb6d
                      2e4c2ae351fd25050ab68dd98af19214
signature           : 9d5154a8e6beb7c472a09fa1e1d3d426
                      b6042519343728835ed5c9416e615aad
                      7ad335c0f0403aa8985e6b6979e5918d
                      3d28e787ca29d55447930777c380d808
]]></artwork>
<t>Record (196 octets):</t>

<artwork><![CDATA[4d484d32000000010000001100000000
0000000100000000000003e800020100
6ade9800fe68177dd329ee6fdd668cb4
3ebdeb6d2e4c2ae351fd25050ab68dd9
8af192147ead4d8be7d24e08d7075bba
c8fbe7779ab3b8f1c47d3f8be4d2a514
f2ec20696f7d9007e81b73c9f54aff8c
2f66bd6e8956bfa7e001789b986c328f
9ed787519d5154a8e6beb7c472a09fa1
e1d3d426b6042519343728835ed5c941
6e615aad7ad335c0f0403aa8985e6b69
79e5918d3d28e787ca29d55447930777
c380d808
]]></artwork>
</section>

<section anchor="changes-since-draft-ramadan-moq-multicast-00"><name>Changes Since draft-ramadan-moq-multicast-00</name>
<t>This appendix is informative.  It is to be removed before publication
as an RFC.</t>

<ul spacing="compact">
<li>Section 1: added a paragraph on TreeDN <xref target="RFC9706"></xref> as an incremental
path to native multicast, with AMT relays serving unicast-only
networks; receivers without UDP socket access stay on
MoQ unicast.  The statement that MoQ relays MAY operate as TreeDN
nodes is removed, because <xref target="RFC9706"></xref> defines no such role.  The
catalog extension is no longer called container-agnostic.</li>
<li>Section 1.1: a catalog-provided service name or relay address is
a discovery input, not proof that an upstream is reachable or
healthy.</li>
<li>Section 2: defines service hostname and protocol health.</li>
<li>Sections 3 and 4.1: the availability of several transports, the
preference for SFN diversity reception, and the configurability of
hysteresis thresholds are no longer stated with BCP 14 keywords.</li>
<li>New Section 3.1: a receiver given a service hostname for a MoQ
upstream or an AMT relay resolves it at connection time and treats
the A and AAAA answers as candidates.  It selects a candidate only
after protocol health: MoQ session setup and the capability check
for a MoQ upstream, or the AMT exchange and the first data or a
bounded wait for an AMT relay.  It races a bounded batch, cancels
the losers, honors DNS TTLs, and does not pin an address across
reconnects.</li>
<li>Section 4.1: an endpoint lists only mmtp tracks and the
<tt>fec-repair</tt> tracks that repair them, and a receiver ignores an
endpoint that lists any other track.  A loc or cmaf track reaches
multicast through an mmtp rendition track in its <tt>altGroup</tt>.</li>
<li>Section 4.1: an endpoint names tracks in the namespace of the
catalog track.  The IPv6 ASM range is now ff00::/8 outside the SSM
range ff3x::/32, which admits transient, unicast-prefix-based and
embedded-RP groups.  IGMPv3 and MLDv2 are cited to <xref target="RFC9776"></xref> and
<xref target="RFC9777"></xref>, which obsolete RFC 3376 and RFC 3810.</li>
<li>Section 4.1: points to Section 6.3, under which a receiver SHOULD
pause or end the unicast subscriptions that the multicast path
replaces once multicast delivery is verified.</li>
<li>Section 4.2.1: the new <tt>discovery</tt> value &quot;dns&quot; resolves the
<tt>relay</tt> service hostname to candidates that the subscriber
validates (Section 3.1).  It is now the recommended method when a
relay service hostname is available, in place of &quot;driad&quot;.
<tt>relay</tt> is a hostname with &quot;dns&quot;, a literal
address with &quot;manual&quot;, and absent with &quot;driad&quot;.  A losing AMT
candidate sends a Membership Update with the leave before its
socket closes.  The search starts with a relay in the subscriber's
own network, found with DNS-SD or the AMT anycast addresses
(<xref target="RFC8777"></xref> Section 3.1.2).  The claim that DRIAD relay addresses
are typically anycast is removed.  The examples name a relay
service hostname.  The <tt>port</tt> field now says that a value other
than 2268 departs from <xref target="RFC7450"></xref> and never applies to a relay
found by DRIAD.</li>
<li>Section 4.2.2: the atsc3 fields cite <xref target="ATSC-A331"></xref> and
<xref target="ATSC-A322"></xref>, and <tt>slsUri</tt> acquires the SLS of an MMTP service,
its MP table included (<xref target="ATSC-A331"></xref> Section 7.2), in place of
the S-TSID with FEC parameters of -00.</li>
<li>Section 5: the paragraph on LOC and CMAF objects now refers to the
mmtp rendition rule of Section 4.1.  It no longer says that
multicast carriage uses MMTP &quot;regardless of a track's unicast
packaging&quot;.  The catalog-derived FEC configuration now also covers
the Reed-Solomon parameters (<xref target="MOQ-FEC"></xref> Section 4.5).  The
transport table drops the QUIC datagram row, because <xref target="MOQ-MMT"></xref>
Section 4.3 sends the objects of an mmtp track only in subgroups.
The mapping of MMTP packets onto Multicast QUIC DATAGRAM frames,
and the use of <xref target="MOQ-FEC"></xref> on that channel, are now described,
informatively, in <xref target="MOQ-FEC"></xref> Section 11.3, which that document's
-01 makes informative, rather than specified there.</li>
<li>Section 6: failover uses the joins of <xref target="I-D.ietf-moq-transport"></xref>
Section 3.5 (LOCATION_FILTER Next Object or Relative Start, and
FILL_PARAMETERS for the current group).  These replace the &quot;Largest
Object filter&quot; and &quot;Next Group Start filter&quot;, which that document no
longer defines.  A paused unicast subscription is resumed instead
of subscribing again.</li>
<li>Section 6.2: &quot;standalone FETCH&quot; is now FETCH, and &quot;Largest Location&quot;
is now Largest Object, with the LARGEST_OBJECT parameter cited.  The
text now says that repair Group IDs use only [0, 2^62) of the MoQ
Transport Group ID range, which goes up to 2^64 - 1.  A relay that
does not hold a repair-track group fetches it upstream, so a FETCH
fails only when no publisher upstream still holds the group.</li>
<li>New Section 6.3: after a switch to multicast, a receiver SHOULD
pause its unicast subscriptions with REQUEST_UPDATE (FORWARD 0) or
end them with STOP_SENDING, and resumes paused ones on failover.  It
keeps any repair layer that no endpoint carries and that it combines
with multicast source symbols.</li>
<li>Editorial: the Abstract and Section 1 expand MMTP, AMT and
DRIAD.  Section 1 no longer calls failover seamless,
Section 1.1 calls multicast an additional delivery path instead of
an optimized one, and Section 3 calls MoQ/WebTransport the fallback
that every client supports instead of the universal fallback.  The
examples of Section 4.1 use a documentation address.</li>
<li>References: <xref target="MOQ-MMT"></xref> and <xref target="MOQ-FEC"></xref> now name their -01
revisions, and <xref target="MOQ-FEC"></xref> lists both of its authors.  ATSC A/331,
ATSC A/322, ARIB STD-B60 and FIPS 180-4 are now cited; BLAKE3 is no
longer cited.</li>
<li><xref target="content-authentication"></xref> replaces bc-provenance version 1 with
version 2; the BCPV1 <tt>manifest_object</tt>, its &quot;BCPV1-sig&quot; prefix,
the <tt>compaction</tt> member, the Merkle Tree Hash and the 198-octet
test vector are withdrawn.</li>
<li>The digest is hash-agile: <tt>hash_alg</tt> is a Named Information Suite
ID (<xref target="hash-algorithms"></xref>) and sha-256 is the only mandatory
algorithm.  The keyed <tt>authScope</tt> is removed (<tt>S</tt>, <tt>G</tt>, the ports
and <tt>streamId</tt> separate domains).</li>
<li>Receivers that implement the profile enforce it; the &quot;shadow&quot; and
&quot;off&quot; modes and the <tt>enforcement</tt> member are removed.</li>
<li>The catalog <tt>auth</tt> object gains a REQUIRED <tt>version</tt> member, 2
(<xref target="catalog-signaling"></xref>); <tt>keys</tt>, each entry with a <tt>firstEpoch</tt>,
replaces <tt>publicKey</tt>, <tt>keyId</tt> and <tt>previousKey</tt>; <tt>minEpoch</tt>
is added; <tt>hash</tt>, <tt>compaction</tt> and the <tt>tracks</tt> array (<tt>mediaTrack</tt>,
<tt>authScope</tt>, <tt>sourceSymbolFormat</tt>) are removed.  A protected
endpoint carries an <tt>auth</tt> object of its own: <tt>manifestPort</tt>,
<tt>streamId</tt>, <tt>senderAddress</tt>, <tt>maxAuthDelayMs</tt>, <tt>manifestTimerMs</tt>,
and <tt>manifestTrack</tt>, which moves there from the <tt>tracks</tt> entry and
becomes OPTIONAL.</li>
<li>A <tt>multicast</tt> object that carries <tt>auth</tt> requires <tt>auth</tt> on every
endpoint (<xref target="catalog-signaling"></xref>).</li>
<li>A packet of a track the endpoint lists is a datagram of the flow
on every path that delivers it to a consumer protected under the
flow, one that has admitted a datagram of it, a MoQ unicast path
included, and is verified against the same records, a consumer
that several endpoints feed being protected under each and a
symbol admitted under any flow of the object being admitted in it,
a consumer being protected under a flow only while the receiver
holds its endpoint, joined to its group, reading its
<tt>manifestTrack</tt> or receiving the broadcast that carries the
flow, and leaving one discarding that flow's state and
hold queues, ending the protection under it and keeping what was
admitted; only a symbol of an unlisted track, one that no endpoint
of a flow the consumer is protected under lists, a held endpoint's
track the consumer is not yet protected under included until the
admission that makes it so, and that a MoQ unicast path alone
therefore delivers to it, is outside every such flow and MAY enter
a FEC
instance under that path's own protection, a block that only such
symbols have delimited yielding to an admitted repair packet that
conflicts with it; the block rules of step 2 govern a FEC instance
or keyframe overlay only while it is protected under a flow whose
endpoint lists its source track, one protected under no such flow,
as one a repair-only endpoint feeds while the source track comes
over MoQ unicast alone, being a MoQ subscriber outside the profile
for its blocks until a record of a flow that lists the track admits
a source packet.  A packet recovered from admitted symbols
alone is
admitted with them, the receiver decoding from admitted symbols
where they suffice; a packet the base instance recovers with such a
symbol's help is admitted by its digest, hashed with the base SS_ID
its block position implies, a miss held in the MoQ unicast path's
hold queue, and a block is recovered only once every one of its
source symbols is admitted, a recovered packet dropped unadmitted
at expiry or on overflow leaving it unrecovered, the receiver then
SHOULD discard the unlisted-track symbols that fed the decode and
decode again when what it holds suffices, at most once more per
block with such symbols' help, and a held packet admitted after
the FEC deadline recovers the block, the receiver then SHOULD
cancel a repair FETCH outstanding for it; a
packet only the overlay recovers with such help is not delivered,
and a symbol of an unlisted track gives no block or run its ts or
FEC deadline and neither anchors nor trips the fail-closed check
of <xref target="MOQ-FEC"></xref> Section 6.3.9, a block only such symbols delimit
being decoded
only, with no ts and no deadline, the lost SS_IDs in its range
staying runs that a packet admitted above them reveals, and a
block with no ts being
discarded under the open-block bound before any block that has a
ts.
A copy
of an admitted datagram delivered again is never held, whether
step 1 or step 2 admitted it.  At the admission that makes a
consumer protected under a flow it discards what it holds,
unadmitted, of the tracks that flow's endpoint lists, and its
deduplication keeps an admitted symbol over an unadmitted one.  A
track taken over MoQ unicast
alone, before the multicast join completes or after failover, is
a MoQ subscription outside the profile unless the receiver
verifies it against the records of a <tt>manifestTrack</tt>
(<xref target="receiver-enforcement-and-state"></xref>, <xref target="emission-and-cadence"></xref>).</li>
<li>A receiver that can request unicast repair keeps the pending
entries of an open block with an FEC deadline and, since a run of
lost SS_IDs is revealed only by a packet admitted above it, those
pending at the admission of the source packet of highest SS_ID
each base instance has admitted and those inserted after it,
as block state under the bound of <xref target="MOQ-FEC"></xref> Section 9; step 1
drops
the other held copy of a datagram it admits, which counts toward
nothing; a record meets a 64-deep replay floor per <tt>(kid, epoch)</tt>,
and a record of the previous epoch is admitted within a grace
period of <tt>maxAuthDelayMs + 2 x manifestTimerMs</tt>, in place of the
<tt>previousKey</tt> overlap of -00, floor and grace period kept per
arrival path (<tt>manifestPort</tt>, the manifest track), the floor raised
by the records arriving on the path, verified or dropped as the
byte-identical copy of a verified one, the period ending that long
after the first such record of the new epoch on the path,
<tt>manifestPort</tt> admitting the previous epoch while that path has
started none, a record
read from the mirror meeting the higher of the two floors and the
earlier-started period, and no record of the previous epoch
admitted past the bound of <xref target="emission-and-cadence"></xref> plus
<tt>maxAuthDelayMs + 2 x manifestTimerMs</tt> after the new epoch became
current; the sender transmits each record
twice, the byte-identical twin after the last datagram the record
covers, no later than <tt>maxAuthDelayMs / 2</tt> after the record and
before the signed record of window <tt>mseq + 64</tt>, so that it arrives
above the floor; a generation reset discards
every per-flow item but the budgets and the hold queues; the hold
queues are per path, so a MoQ unicast path that fills its own
evicts nothing from the multicast path's, and only the multicast
path's expiries and the <tt>manifestPort</tt> bucket's drops count toward
failover; a unicast receiver widens its hold, its hold queue with
it, and the freshness tolerance of the records it reads from the
manifest track, by at most 5 s, a record on <tt>manifestPort</tt> keeping
the unwidened tolerance, one that reads the mirror and will not
join the multicast path widening the pending expiry of a mirrored
record's entries by the same W, and a receiver reading the mirror
that has joined or may yet join widens the pending expiry of a
mirrored record's entries and the life of its admitted digests by
the mirror's lead, a per-flow value it measures, the bound until
the generation's first measurement
(<xref target="receiver-enforcement-and-state"></xref>, <xref target="emission-and-cadence"></xref>,
<xref target="threat-model"></xref>).</li>
<li>A <tt>keys</tt> array that repeats a <tt>kid</tt> or a <tt>firstEpoch</tt> yields no
key set: the receiver admits nothing from the broadcast
(<xref target="catalog-signaling"></xref>).</li>
<li>Per-flow receiver state belongs to the stream generation: a change
of the endpoint's <tt>streamId</tt> discards it, the budgets and the hold
queues excepted, and the next record is processed as the first of
the flow; the current epoch is kept with the <tt>kid</tt> that set it,
and a re-fetch or re-provisioning after which that <tt>(kid, epoch)</tt>
fails the range check of <xref target="catalog-signaling"></xref> resets the flow the
same way (<xref target="receiver-enforcement-and-state"></xref>).</li>
<li>A broadcast carries the flow as an MMTP service: the flow's
datagrams as the MMTP packets of one MMTP session, carried as the
sender's UDP datagrams on <tt>S</tt>, <tt>G</tt> and <tt>port</tt>, and the records as
the sender's record datagrams on <tt>manifestPort</tt>, a broadcast that
re-addresses either flow or carries the datagrams in any other
form being uncovered.  This document no longer covers ROUTE: the
carriage of -00, the record in the ROUTE signaling carousel and
the signed group digest in an ALC/LCT EXT_AUTH header, is removed,
the profile having no inclusion proofs, and so is the
<tt>sourceSymbolFormat</tt> member of -00, whose only value named the
authenticated bytes of that revision, trailer excluded
(<xref target="broadcast-carriage"></xref>).</li>
<li><xref target="multicast-endpoint-format"></xref> defines <tt>S</tt> for the tuple rule
(<tt>sourceAddress</tt> on an SSM endpoint, <tt>auth.senderAddress</tt> on a
protected ASM endpoint, absent on an unprotected ASM endpoint),
binds each <tt>(S, G, port)</tt> tuple to at most one endpoint object as
well as one namespace, adds the same rule for a protected
endpoint's <tt>(S, G, manifestPort)</tt> tuple, and requires a track
listed on an endpoint to carry the catalog track's namespace
(<xref target="I-D.ietf-moq-msf"></xref> Section 5.2.2); a receiver ignores an
endpoint that lists another.</li>
<li><xref target="multicast-security"></xref> no longer delegates source trust to the AMT
relay: an ASM receiver, and a receiver reached through an AMT
gateway on SSM, discards a datagram whose IP source address is not
the endpoint's sender before parsing or hashing it
(<xref target="receiver-enforcement-and-state"></xref>).</li>
</ul>
</section>

</back>

</rfc>
