Skip to content

Design: an object-level IETF API (subgroup, object ID, properties, status) and a relay mode that forwards them unchanged #4926

Closed as not planned

Description

@allyblockcast

Measured at 375453856f18a84776a9d05bca4c6d6a2adfafdf, draft-20.

What I'm asking for

A way for an application to publish and receive moq-transport objects with their wire identity intact — subgroup ID, object ID, object properties, object status — and a relay mode that forwards those unchanged.

This is a design request, not a bug report. The current behaviour looks deliberate and is pinned by your own tests; I'd like to understand whether an object-level path is in scope, and if so what shape you'd want it to take.

Where the limit is

Not in the IETF codec — in the shared model both the codec and the relay are built on:

// rs/moq-net/src/model/frame.rs
pub struct Frame {
    pub timestamp: Timestamp,
    pub payload: Bytes,
}

// rs/moq-net/src/model/group.rs
pub struct Info {
    pub sequence: u64,
}

js/net/src/group.ts:23 is the same shape. Object identity is position in the group; group identity is a u64. So the IETF path necessarily narrows to that, and does, consistently:

  • rs/moq-net/src/ietf/subscriber.rs:2605 — subgroup != 0: warn! + Err(Unsupported), stream dropped
  • rs/moq-net/src/ietf/subscriber.rs:3299, :3752 — same for fills and group fetches
  • rs/moq-net/src/ietf/subscriber.rs:6814 next_object_id — any gap is Err(Unsupported)
  • rs/moq-net/src/ietf/group.rs:84 decode_object_time — _ => {} discards every property id but Timestamp (0x10) and Timescale (0x08)
  • js/net/src/ietf/subscriber.ts:1038 — throw new Error("subgroups are not supported")
  • js/net/src/ietf/object.ts Frame.decode — if (delta !== 0) throw

The comment on GroupFlags::has_subgroup_object (rs/moq-net/src/ietf/group.rs:158) states the position directly: "Since we don't support subgroups or object ID > 0, this is trivial to support." And ietf::subscriber::stitch_tests::a_non_zero_subgroup_leaves_the_track_flowing pins the drop as intended. So I'm not reporting these as oversights.

Two observations that may be worth separating out

1. The publisher has no way to emit what the subscriber refuses. Every emit site writes the literal sub_group_id: 0 (rs/moq-net/src/ietf/publisher.rs:2510, 2551, 3003, 3179, 3222, 3270, 4908) and both fetch sites write FetchSubgroup::Zero (:1238, :1246). Worth noting in case a future object-level API is approached from the subscriber side first — the egress leg would still have nothing to write.

2. The non-contiguous-object-ID drop is quieter than the subgroup one, and this one may be a genuine bug. A gapped group makes recv_group return Ok(()): the error from next_object_id reaches

Err(err) => {
    tracing::debug!(%err, group = %producer.sequence, "group error");
    let _ = producer.abort(err);
}

and then falls through to Ok(()). The subgroup case is warn! + an Err the caller sees; this one is debug! and the caller is told the stream was handled. Measured: 3 objects in, 0 out, Ok(()), session open. A relay in this state forwards a truncated track with no error anywhere above debug. If an object-level API is out of scope, raising this one to warn! — or returning the refusal — would still be valuable on its own.

Measurements, each against its own control (same objects, subgroup 0 / contiguous IDs):

[subgroup]    sent=3  received(subgroup=0)=3  received(subgroup=1)=0  verdict=Err(Unsupported)
[object-ids]  sent=3  received(ids=[0,1,2])=3 received(ids=[0,2,5])=0  verdict=Ok(())
[properties]  sent=2  received=2  properties_sent/object=2  properties_received/object={Timestamp}

Why we'd use it

We carry MPEG MMTP over MoQ, including AL-FEC repair. The mapping needs all four: a repair flow is a separate subgroup from its source flow, repair objects are deliberately non-contiguous in object ID against the source numbering, and MMTP per-object metadata rides in object properties. Today a moq-relay between our publisher and our receiver silently reduces that to a timestamped byte stream.

I'd be glad to hear that this is out of scope and that an MMTP mapping belongs outside moq-net's model — that's a useful answer and we'll plan around it. What would help most is knowing which of these you'd consider:

  1. an opaque per-object properties blob carried through the model unmodified, leaving subgroup/object ID alone;
  2. object identity (subgroup + object ID) in the model, with groups allowed to be multi-stream;
  3. a relay-only forward-unchanged mode that never materialises the model at all;
  4. none of the above — the model is the product, and this belongs in a separate stack.

Happy to do the work on whichever shape you'd accept.

Activity

  1. kixelated commented on Oct 7, 2026

    @kixelated
    Collaborator

    Heh sorry, lots of bagagge to unpack here. Years of trauma from IETF debates.

    I don't really agree with everything in the moq-transport draft although I'm semi-open to some things if there's a good use-case.

    Why we'd use it

    We carry MPEG MMTP over MoQ, including AL-FEC repair. The mapping needs all four: a repair flow is a separate subgroup from its source flow, repair objects are deliberately non-contiguous in object ID against the source numbering, and MMTP per-object metadata rides in object properties. Today a moq-relay between our publisher and our receiver silently reduces that to a timestamped byte stream.

    IMO each sub-group could be a separate track, aligned by the application. It's more work for this use case, but it does mean you can have the subscriber choose/prioritize the repair tracks. Same ideal with SVC; each layer should be a track (not a sub-group) so you can choose/prioritize the enhancement layers.

    Another reason I don't support sub-groups, or filters, or gaps in object IDs because they create a fragmented cache. If the relay only gets object 0,5,10... it might not know if 1-4 and 6-9 exist. In the worst case scenario we get a FETCH request and now have to query upstream for every hole in the cache. It's a general problem with moq-transport (sparse group IDs have a similar issue).

    If you do want non-contiguous object IDs with moq-lite... your application can encode the object ID delta at the start of each payload. It's the exact same result as moq-transport sans the previous object ambiguity. It doesn't waste any bytes and the relay doesn't actually need to know the Object ID.

    I'd be glad to hear that this is out of scope and that an MMTP mapping belongs outside moq-net's model — that's a useful answer and we'll plan around it. What would help most is knowing which of these you'd consider:

    1. an opaque per-object properties blob carried through the model unmodified, leaving subgroup/object ID alone;
    2. object identity (subgroup + object ID) in the model, with groups allowed to be multi-stream;
    3. a relay-only forward-unchanged mode that never materialises the model at all;
    4. none of the above — the model is the product, and this belongs in a separate stack.

    Happy to do the work on whichever shape you'd accept.

    1. I don't think object properties should be used as an opaque K/V storage; it's just the wrong layering. The relay can't use any of this information and yet these object properties are plain-text with a crude encoding. If you want structured data, use protobuf or json or something with a schema instead.

    2. Maaaaaaaybe sub-groups could make sense to avoid a race conditions where base and enhancement layer subscriptions start at different group IDs, but IDK it feels niche. I just don't think it's worth introducing another layer into the moq-net API, because now every subscriber has to be prepared to potentially receive sub-groups.

    3. That might be worth doing in general. A full moq-transport relay is a non-goal though, because there's so many extremely niche/difficult features stuck in the draft (ex. multi-publisher). I think a dedicated relay mode could make sense for performance reasons, but maybe what you're actually asking for is full moq-transport parity, which would be a nightmare (nobody is close to it).

    4. I'm mostly trying to focus on the good bits of moq-transport. Unfortunately every niche/questionable feature like that adds up, both in terms of the API surface and maintenance. I have to draw the line somewhere and I'm on the more minimal side of the spectrum. I don't think anybody will support the entire moq-transport specification because there be dragons.

  2. added a commit that references this issue on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions