Skip to content

fix(net): renew draft-16 announcements in place - #6051

Merged
kixelated merged 5 commits into
mainfrom
quest/m1/auth/request-update
Oct 20, 2026
Merged

kixelated merged 5 commits into
mainfrom
quest/m1/auth/request-update

Conversation

@kixelated

@kixelated kixelated commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

moq-dev/moq#5695 let a route swap its auth and renew the live PUBLISH_NAMESPACE with REQUEST_UPDATE, but only from draft 17. On draft 16 the publisher still withdrew the namespace and announced it again, so subscribers saw it go away, even though draft 16 has REQUEST_UPDATE and our receiver already accepts a renewal there.

The reason was routing, not the message. Draft 16 rides the control-stream adapter, and the peer answers an update with REQUEST_OK or REQUEST_ERROR naming the update's own Request ID. That ID names no virtual stream, so the adapter dropped the answer. For the same reason our draft-16 subscription updates were never counted, so nothing kept one update in flight at a time there.

The quest's lite half needs no code: moq-dev/moq#5972 decided lite puts nothing on the wire for Route::auth, and #5695 already advertises lite routes unnamed. This completes Live requests follow their auth, so the quest file is deleted.

Change

  • Adapter (Rust and JS): on draft 16, each REQUEST_UPDATE we send is recorded against the request it rides before it leaves. Its REQUEST_OK or REQUEST_ERROR goes to that request's stream, and a refusal no longer closes the request. An entry goes with its answer or its request, so our own sends bound the map. Draft 15's answers are still dropped, as before.
  • Publisher (Rust and JS): a swapped route auth on draft 16 is one REQUEST_UPDATE naming its PUBLISH_NAMESPACE, like draft 17+. Drafts 14 and 15, and naming or dropping a token, still withdraw and announce again.
  • Unanswered draft-16 update: dropping a control-stream request tells the peer nothing, so the publisher now sends PUBLISH_NAMESPACE_DONE before offering the namespace again. Without it the peer would hold two copies.
  • Cancel ahead of the answer (Rust and JS): a draft-16 PUBLISH_NAMESPACE_CANCEL that beats the answer ends only that request, which is offered again with no PUBLISH_NAMESPACE_DONE (draft 16 §6.2 sends none after a cancel). Rust used to end the announce loop with UnexpectedMessage; JS, whose adapter closes the stream on a cancel, used to answer it with a DONE.
  • Subscriber (Rust and JS): draft-16 subscription updates now count their answers, so one is in flight at a time, as from draft 17.
  • Docs: Route::auth, JS Route.auth, doc/concept/standard.md and doc/concept/moq-lite.md now say renewal is in place from draft 16. moq-lite.md now describes moq-transport renewal at all.
  • Quest: deleted quest/m1/auth/request-update.md and pointed its references at feat(net)!: let live announcements renew their auth #5695.

Tests

Each fails without its fix:

  • Rust adapter::our_update_answers_reach_their_request: answers reach the updated request, a refusal leaves it open, an update split across writes is still recorded, and nothing is left once the request goes. draft_15_update_answers_are_not_routed covers draft 15.
  • Rust swapping_a_route_auth_is_a_request_update now runs on drafts 16 and 19. an_unanswered_renewal_advertises_again runs on both and checks the draft-16 PUBLISH_NAMESPACE_DONE. New a_draft_16_cancel_ahead_of_the_answer_ends_only_its_request also checks no DONE follows the cancel.
  • The existing integration test upstream_priority_follows_the_subscribers covers the draft-16 subscriber. With routing but no counting, it fails with moq-transport-16 ... priority stuck.
  • JS: an answer to our update reaches the request it updated on draft 16 only, draft 16 swaps a route's token with one REQUEST_UPDATE (a second renewal waits for the first answer, so it needs routing), an unanswered draft 16 update withdraws its request before offering it again, a draft 16 cancel ahead of the answer offers the namespace again without DONE, and a draft 16 subscription update waits for the last one's answer.

Impact

  • Public API: none.
  • Wire: no new messages, fields or codes. On draft 16 we now send REQUEST_UPDATE (Existing Request ID set) on a PUBLISH_NAMESPACE when its route auth changes, where we used to send PUBLISH_NAMESPACE_DONE and a new PUBLISH_NAMESPACE. We now wait for each draft-16 update's answer before sending the next update on that request; draft 16 section 9.11 requires the peer to send exactly one. A draft-16 peer that never answers stalls further subscription updates (priority, max age) on that subscription, as on draft 17+. An unanswered namespace update times out and is withdrawn.

Follow-ups (not in this PR)

  • JS renewals are serial (accepted). As feat(net)!: let live announcements renew their auth #5695 noted, the JS announce loop sends one update at a time, so N routes renewing together take N round trips. Rust sends them concurrently. This matters only if a JS publisher carries many routes.
  • Idle cancels are noticed late. Nothing reads an idle PUBLISH_NAMESPACE stream, so a draft-16 PUBLISH_NAMESPACE_CANCEL or a draft-17+ cancel is only seen at the next update. This predates the PR.

Checks

  • just check: every Rust (nextest, 7663 tests and the scoped suites) and JS suite passed. The one failure was bench/relay.test.py (test_a_foreign_listener_never_proves_relay_readiness), which times out inside the agent sandbox and passes outside it. This branch does not touch bench/. Recipes after it in the dispatch may not have run locally; CI covers them.
  • just test interop --all: all checks passed.
  • After merging origin/main: cargo test -p moq-net --lib (2169 passed) and js/net bun test (2346 passed). quest check is clean.
  • just fix is clean.
  • After the review fixes: cargo test -p moq-net --lib (2169 passed), clippy on moq-net lib and tests, js/net bun test (2347 passed), tsc and biome, and just test interop --all (all passed).

Decision

  • The quest completes here. The maintainer took the recommendation: this PR deletes request-update.md. JS sending renewals one at a time stays accepted, as in feat(net)!: let live announcements renew their auth #5695, and is recorded under Follow-ups; it becomes its own quest only if a JS publisher with many routes appears.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 3 commits October 19, 2026 21:44
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Draft 16 answers a REQUEST_UPDATE by the update's own Request ID, which
the control-stream adapter dropped. Route that answer to the request it
updated, in Rust and JS, so a swapped route auth renews a draft-16
PUBLISH_NAMESPACE with REQUEST_UPDATE instead of withdrawing and
re-announcing it, and draft-16 subscription updates keep one in flight.
An unanswered draft-16 update now withdraws with PUBLISH_NAMESPACE_DONE,
and a cancel that beats the answer ends only its request.

Completes quest/m1/auth/request-update.md.

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

# Conflicts:
#	quest/m1/auth/request-race.md
#	quest/m1/auth/request-update.md
@kixelated

Copy link
Copy Markdown
Collaborator Author

Outcome: draft-16 announcements now renew in place with REQUEST_UPDATE, with answers routed by the update's own Request ID through the control-stream adapter (Rust and JS). Lite needed no code (#5972). Interop (just test interop --all) passed; just check passed except bench/relay.test.py, which times out only inside the agent sandbox and is unrelated.

Left as a draft for one decision: delete the quest now (recommended) or keep it open for JS concurrent renewals.

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review October 20, 2026 06:06
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 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-10-20T07:53:51.647559Z 853f714 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: 2a5322e386

ℹ️ 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 rs/moq-net/src/ietf/publisher.rs Outdated
Comment thread js/net/src/ietf/subscriber.test.ts Outdated
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 6 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3d907d31-486b-438f-8af6-a9d824d68672
📥 Commits

Reviewing files that changed from the base of the PR and between 2a5322e and 853f714.

📒 Files selected for processing: 5
  • js/net/src/ietf/adapter.test.ts
  • js/net/src/ietf/publisher.test.ts
  • js/net/src/ietf/publisher.ts
  • js/net/src/ietf/subscriber.test.ts
  • rs/moq-net/src/ietf/publisher.rs

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7b47869f-bf95-4f17-9ff0-48329ae469b0
📥 Commits

Reviewing files that changed from the base of the PR and between 7a14a48 and 2a5322e.

📒 Files selected for processing: 15
  • doc/concept/moq-lite.md
  • doc/concept/standard.md
  • js/net/src/hop.ts
  • js/net/src/ietf/adapter.test.ts
  • js/net/src/ietf/adapter.ts
  • js/net/src/ietf/publisher.test.ts
  • js/net/src/ietf/publisher.ts
  • js/net/src/ietf/subscriber.test.ts
  • js/net/src/ietf/subscriber.ts
  • quest/m1/auth/README.md
  • quest/m1/auth/request-update.md
  • rs/moq-net/src/ietf/adapter.rs
  • rs/moq-net/src/ietf/publisher.rs
  • rs/moq-net/src/ietf/subscriber.rs
  • rs/moq-net/src/model/origin.rs
💤 Files with no reviewable changes: 1
  • quest/m1/auth/request-update.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.

Walkthrough

Draft 16 update handling now includes response routing, in-place namespace authorization renewal, and subscription update tracking. Tests cover success, refusal, cancellation, timeout, retries, and serialized subscription updates. Documentation now identifies Draft 16 as the threshold for REQUEST_UPDATE, and the live-request authentication renewal quest document was removed.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 2a532

Draft 16 namespace renewal and subscription update routing look consistent across the Rust and JavaScript adapters. No merge-blocking issue was identified.

Pre-merge checks: Passed 4 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Docstring Coverage Inconclusive Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 9 files. (5 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main change: in-place renewal of draft-16 announcements.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description directly explains the draft-16 adapter, publisher, subscriber, documentation, quest, and test changes in the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 9 files. (5 skipped: 3 unsupported, 2 too large.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

A draft-16 PUBLISH_NAMESPACE_CANCEL that beats the answer to our update
already ended the request, and draft 16 sends no DONE after a cancel. Drop
the request locally and offer it again, in Rust and JS.

The new draft-16 JS tests settle the mock transport instead of waiting on
wall-clock time.

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

Copy link
Copy Markdown
Collaborator Author

@codex review

The review of 1c2409c has shown running since 06:28Z; retriggering.

(Written by Claude Opus 5.5)

@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: 1c2409cd80

ℹ️ 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 js/net/src/ietf/adapter.ts
A draft-16 receiver refuses an update with REQUEST_ERROR and then
PUBLISH_NAMESPACE_CANCEL, and draft 16 sends no DONE after a cancel. Drop the
request locally on a refusal, the same as on a cancel that beat the answer, and
re-offer it once the refusal's wait is out, in Rust and JS.

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

Copy link
Copy Markdown
Collaborator Author

Merge summary: draft-16 announcements renew in place with REQUEST_UPDATE, with answers routed back to the request they updated, in Rust and JS.

Review rounds since the last design change:

  • Codex P2s on 2a5322e were fixed in 1c2409c (no PUBLISH_NAMESPACE_DONE after a draft-16 cancel that beats the answer).
  • Codex P2 on 1c2409c was fixed in 853f714: a refused draft-16 update now drops the request without PUBLISH_NAMESPACE_DONE, since our receivers follow REQUEST_ERROR with PUBLISH_NAMESPACE_CANCEL. Regression tests on both sides fail without it.
  • Codex reviewed 853f714 with no findings; CodeRabbit had nothing actionable.

Decision: on draft 16, a refused namespace update is treated as ending the advertisement with no DONE. A peer that refuses but never cancels would keep its old advertisement while we re-offer; draft 16 does not define this for namespaces, so we follow the subscription rule and our own receivers.

Local checks on 853f714: just check, just test interop --all, moq-net lib tests, js/net tests. Unrelated: moq-cli export::redundant_importers_switch_through_bufferbloat_on_lite_07 failed twice locally under load ("stitching export exited") and then passed; it runs on lite-07 only, which this PR does not touch.

Enabling auto-merge on 853f714.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 697441d Oct 20, 2026
3 checks passed
@kixelated
kixelated deleted the quest/m1/auth/request-update branch October 20, 2026 08:15
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