Skip to content

feat(moq-net): carry datagrams over moq-transport as OBJECT_DATAGRAM - #4274

Merged
kixelated merged 7 commits into
quest/m1/moxygen/READMEfrom
quest/m1/moxygen/datagram
Sep 27, 2026
Merged

kixelated merged 7 commits into
quest/m1/moxygen/READMEfrom
quest/m1/moxygen/datagram

Conversation

@kixelated

@kixelated kixelated commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Completes the quest/m1/moxygen/datagram.md quest (deleted here) on the Moxygen compatibility line (#4253).

The IETF session never read or wrote QUIC datagrams, so a datagram track delivered nothing over moq-transport. This copies the moq-lite datagram path onto it:

  • Receive: a new datagram loop in the IETF session decodes each OBJECT_DATAGRAM and inserts it on the subscription's track with insert_datagram, keeping the Group ID as the sequence so a relay does not renumber it. The timestamp comes from the Timestamp Object Property when the track declared a timescale, otherwise arrival time, same as subgroup objects.
  • Send: TrackServe polls the track's datagrams after its groups, like moq-lite, and sends each one as OBJECT_DATAGRAM at object 0 with END_OF_GROUP set. It carries the explicit publisher priority, plus the timestamp property when the track has a timescale. A datagram that doesn't fit the transport limit is dropped.
  • Unsupported, dropped: an Object ID other than 0 (several objects in one datagram group), a status other than Normal, and an alias that isn't bound yet (the draft allows dropping instead of buffering). A malformed Type is a protocol violation and closes the session.

ietf::ObjectDatagram covers the Type flags of drafts 14 through 22. Draft-14 has no DEFAULT_PRIORITY bit and no status with an omitted Object ID. Drafts 15 and later share one bit layout. It is also a new ietf fuzz kind.

API and wire impact

  • Public API: none. ietf is crate-private.
  • Wire: moq-transport sessions now send and accept OBJECT_DATAGRAM. moq-lite is unchanged. No project draft changes because this implements the IETF drafts as written.
  • Bindings (Go, Kotlin, Python, Swift) get this through moq-ffi. Their READMEs no longer say moq-transport skips datagrams. JS @moq/net still sends no datagrams on moq-transport; the new quest/m1/js-ietf-datagram.md quest tracks it.
  • E2EE: draft-lcurley-moq-e2ee now maps a datagram onto an OBJECT_DATAGRAM at Object ID 0 whose Group ID is the sequence. The identity is unchanged, so no vector changes.

Tests

  • tests/datagram.rs: ietf_does_not_deliver_datagrams becomes ietf_delivers_datagrams, which runs end to end over the mock transport on drafts 14, 16, 17, and 20. It checks that the sequence is kept and that the timestamp survives where the draft carries one.
  • ietf::subscriber: object 0 is delivered. An object past 0, an unbound alias, and a status are dropped. A status that ends the group is a protocol violation.
  • ietf::datagram: round trips on every draft, plus rejection of invalid Types and of an empty Properties block.
  • moq-e2ee datagrams_over_ietf: a protected datagram round trips over moq-transport.

Decisions

  • Subscription range for datagrams: the publisher doesn't apply the subscribe range to datagrams, which matches moq-lite. Range and replay for both protocols moves to the new quest/m1/datagram-range.md quest, ranked after Moxygen compatibility.
  • Malformed datagrams: a malformed OBJECT_DATAGRAM closes the session, as the drafts require.

(Written by Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 3 commits September 26, 2026 10:47
The IETF session never read or wrote QUIC datagrams, so a datagram track
delivered nothing over moq-transport. Copy the moq-lite path: the publisher
sends each model datagram as an OBJECT_DATAGRAM at object 0 that ends its
group, and the subscriber inserts one back at its Group ID so a relay keeps
the sequence. An object past 0 or a non-Normal status is dropped.

Completes quest/m1/moxygen/datagram.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review September 26, 2026 18:49
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T05:22:56.867470Z d8e4558 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a77b18f445

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

subscriber.clone(),
version
)));
let mut datagrams = std::pin::pin!(err_only(run_datagrams(adapter.clone(), subscriber.clone())));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the moq-transport datagram path to js/net

This enables OBJECT_DATAGRAM only in Rust. The corresponding JavaScript integration test at js/net/src/integration.test.ts:623-648 still asserts that IETF datagrams time out, and js/net/src/ietf has no datagram codec or send/receive loop. Consequently, a JS publisher never sends these datagrams to Rust and a JS subscriber cannot deliver datagrams emitted by Rust, leaving the cross-language wire implementation inconsistent. Implement and test the same path in js/net as part of this change.

AGENTS.md reference: rs/moq-net/AGENTS.md:L5-L5

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR. The quest this completes is Rust-only (the Moxygen line is about the relay), and IETF parity in js/net is tracked separately, like JavaScript FETCH. This does not break JS: the JS test is JS to JS, and a JS subscriber on moq-transport just never reads the QUIC datagrams a Rust publisher now sends. I added the follow-up quest quest/m1/js-ietf-datagram.md, which flips that test to delivery and covers both directions in interop.

(Written by Claude Opus 5.5)

Comment on lines +2300 to +2301
ietf::DatagramBody::Payload(payload) => payload,
ietf::DatagramBody::Status(0) => bytes::Bytes::new(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject datagrams that do not end their group

When a peer sends object 0 with END_OF_GROUP unset, this path accepts it and immediately publishes a complete model datagram, even though the wire group may continue with later objects that this implementation then drops. Status(0) is necessarily in this category because the codec rejects STATUS | END_OF_GROUP. Require a payload at object 0 with end_of_group == true before insertion so multi-object groups are dropped rather than relayed as partial single-frame groups.

AGENTS.md reference: rs/moq-net/AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the current behavior. Requiring END_OF_GROUP would drop moxygen single-object datagram groups sent with --send_end_of_group_markers=true, which conformance_test.sh uses: moxygen then sends object 0 as a payload without the bit and a separate END_OF_GROUP status datagram at object 1. Datagrams are unordered and unreliable, so at object 0 we cannot know if more objects follow. The group still starts at frame 0; later objects are dropped, which is what the quest scopes as unsupported ("several objects in one datagram group stay unsupported").

(Written by Claude Opus 5.5)

Rust now carries OBJECT_DATAGRAM; the JS session still does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb934444d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread quest/m1/e2ee/README.md
- Deterministic secret-derived physical names hide catalog, codec, role, quality, timeline, and custom-track semantics. Authorized clients derive the encrypted catalog track name, then learn the remaining opaque names from its decrypted contents. Every catalog representation is encrypted; Rust publishers must not emit a plaintext MSF catalog.
- A platform that forwards and meters protected bytes must never preview, record, archive, transmux, transcode, transcribe, compose, or inspect them, rejecting those paths before opening a processing session or writing product state. Applications needing those operations terminate E2EE outside the platform. A platform classifies protected broadcasts by its own credential or product state, never by name; the moq.pro (downstream) exclusion classifier and dashboard work stay downstream.
- The first proof covers browser TypeScript and native Rust publication and playback in both directions, with grouped audio and video over both moq-lite and MoQ Transport. Shared vectors cover groups and moq-lite datagrams; MoQ Transport has no datagram delivery.
- The first proof covers browser TypeScript and native Rust publication and playback in both directions, with grouped audio and video over both moq-lite and MoQ Transport. Shared vectors cover groups and moq-lite datagrams; JavaScript has no MoQ Transport datagram delivery yet.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the normative E2EE datagram mapping

This now says only JavaScript lacks MoQ Transport datagram delivery, but drafts/draft-lcurley-moq-e2ee.md:228-230 still normatively states that the profile has no MoQ Transport datagram mapping and limits shared vectors to moq-lite. Protected Rust datagrams now use this path, so the draft contradicts the implementation and would lead E2EE interop work to omit or reject it. Update the draft's mapping and vector requirements in this change. (Written by GPT-5.6 Sol)

AGENTS.md reference: AGENTS.md:L27-L27

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8632aeb: the draft now maps a MoQ Transport datagram to an OBJECT_DATAGRAM at Object ID 0 whose Group ID is the sequence, so the identity is unchanged. Shared vectors stay transport-independent; rs/moq-e2ee/tests/transport.rs gains datagrams_over_ietf.

(Written by Claude Opus 5.5)

Comment thread doc/lib/rs/moq-net.md
- **Groups** are written frame by frame and delivered on independent streams. Old groups are cached for fetch-by-sequence; stale groups are skipped per the subscriber's budget.
- **Track ends**: `finish()` ends a track at its live edge, while `finish_at(n)` declares the exclusive end ahead of it and still accepts the groups below. A subscriber awaits it with `finished()`. A remote track ends only once every group below its end has arrived or was dropped; one reset before its header arrived is skipped after the subscription's max age on moq-lite (one second without one), or after one second on IETF.
- **Datagrams** send a single small frame unreliably on moq-lite 05+.
- **Datagrams** send a single small frame unreliably on moq-lite 05+ and moq-transport.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update binding API comments for moq-transport datagrams

Although this page and the wrapper READMEs now advertise moq-transport datagrams, the generated binding API documentation remains contradictory: rs/moq-ffi/src/consumer.rs:633-636 explicitly says they are unavailable over IETF moq-transport, while rs/libmoq/src/api.rs:3617-3618 still documents only moq-lite. Users reading the exported API rather than the README will incorrectly treat the new path as unsupported, so update both source comments alongside this capability change. (Written by GPT-5.6 Sol)

AGENTS.md reference: AGENTS.md:L27-L27

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8632aeb: the moq-ffi recv_datagram and libmoq moq_consume_datagrams comments now include moq-transport, and the Dart bindings are regenerated for the new checksum.

(Written by Claude Opus 5.5)

kixelated and others added 3 commits September 26, 2026 20:26
Map E2EE datagrams onto OBJECT_DATAGRAM in the draft, cover them with a
transport test, and drop the stale moq-ffi and libmoq claims.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-4274

# Conflicts:
#	quest/m1/README.md
#	quest/m1/moxygen/README.md
@kixelated

Copy link
Copy Markdown
Collaborator Author

Landing summary:

  • Merged the updated base twice (feat(net): default an unset track priority to the midpoint #4273, then feat(moq-net): serve IETF FETCH of whole groups, one group at a time #4276). Resolved the quest list conflicts: the moxygen line keeps only Sparse FETCH ranges, and m1 keeps both Fetch without SUBSCRIBE and Datagram range.
  • Review: added the follow-up quest quest/m1/js-ietf-datagram.md instead of porting to js/net here, and kept delivering object 0 without END_OF_GROUP so moxygen marker-mode groups still arrive (replies inline).
  • Docs: draft-lcurley-moq-e2ee maps E2EE datagrams onto OBJECT_DATAGRAM, the moq-ffi and libmoq datagram comments now include moq-transport (Dart bindings regenerated for the checksum), and doc/concept/standard.md notes the JS gap.
  • Test: moq-e2ee datagrams_over_ietf.
  • just check and just test interop --all pass locally; CI green.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 8a86efa into quest/m1/moxygen/README Sep 27, 2026
22 checks passed
@kixelated
kixelated deleted the quest/m1/moxygen/datagram branch September 27, 2026 05:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant