Skip to content

feat(net): moq-lite-07 requests name their auth by AUTH ID - #5927

Closed
kixelated wants to merge 11 commits into
mainfrom
quest/m1/auth/request-auth-id
Closed

kixelated wants to merge 11 commits into
mainfrom
quest/m1/auth/request-auth-id

Conversation

@kixelated

@kixelated kixelated commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Completes the lite half of quest/m1/auth/request-auth-id.md. The moq-transport mirror is split into quest/m1/auth/request-auth-id-ietf.md.

What changes

  • AUTH ID. Each moq-lite-07-wip AUTH now carries an Auth ID (i). The opener numbers its own IDs densely from 0. An ID of 2^64-1 is a PROTOCOL_VIOLATION, because no request could name it as ID plus 1. A reused ID is also a violation.
  • Auth field. These requests now carry Auth (i), encoded last in the body: SUBSCRIBE, SUBSCRIBE_UPDATE, FETCH, ANNOUNCE_REQUEST, ANNOUNCE_START, ANNOUNCE_UPDATE and ANNOUNCE_RESTART. A value of 0 means the session scope; otherwise the value is the ID plus 1.
  • Named requests (Rust). A named request is held to that one token's grant, through the new auth::Authority.
    • It waits while its ID is still in flight.
    • It is refused UNAUTHORIZED if the token already ended.
    • It ends, or its announcement is withheld, when the token ends or narrows away from it.
    • A SUBSCRIBE_UPDATE or ANNOUNCE_UPDATE renames the token. The old token keeps deciding until the new one decides.
  • Per-token wakeups. A change to one token wakes and re-decides only what names it.
    • The peer's tokens live apart from the session's auth state, and each request watches its token through a Changes set.
    • An announce stream indexes its routes by token, so it re-decides only the routes naming the changed one.
    • A presented token's stream waits on its own withdrawal signal, not on every state change.
    • Grant unions (ours and the issued scope) are kept as per-pattern counts, so one token's AUTH_OK no longer re-unions every token.
  • Gapless renewal. Withdrawing a token on lite is now a FIN, not a reset.
    • Its grant leaves the session union immediately.
    • Requests that still name it keep it until they move off, and then the acceptor FINs.
    • A reset is still an abrupt end.
    • So renewal is: present the new token, update the route to it, drop the old one. No new message is needed.
  • Presenter naming. A route's Route::auth is named on its announcements automatically, in both Rust and js/net. Swapping the token sends an ANNOUNCE_UPDATE. A route whose own token ends is retracted, and stays off the wire until it names another token. It never falls back to the scope (Auth 0).
  • JS acceptor. js/net encodes and decodes every new field, numbers its AUTH IDs, refuses reused or maximal IDs, and withdraws with a FIN on lite. It does not yet enforce named grants (decision 6).

Public API and wire impact

  • Public API: none. The lite message types and the js/net wire helpers are internal, and naming is automatic. No MIGRATE entry.
  • Wire: moq-lite-07-wip only. AUTH and seven request messages gain a field, and Auth Stream withdrawal becomes FIN-linger. Published versions are unchanged. The draft changelog is updated.

Decisions

Settled by the maintainer.

  1. Dense AUTH IDs from 0, never reused. ✅ Keep. The acceptor tracks a watermark plus a small ahead set.
  2. ANNOUNCE_RESTART also carries Auth. ✅ Keep. A restart replaces the announcement, so it must be able to name its token.
  3. FIN-linger withdrawal is the renewal mechanism. ✅ Keep. No new message, and no race.
  4. Only the presenter names routes. ✅ Keep. Outgoing SUBSCRIBE, FETCH and ANNOUNCE_REQUEST still send 0. No Subscription::auth until a consumer asks for it.
  5. A named ANNOUNCE_REQUEST filters per route. ✅ Keep, to match how scope narrowing already behaves.
  6. The JS acceptor does not enforce named grants yet. ✅ Deferred to quest/m1/auth/request-auth-id-ietf.md, so the logic is shared.
  7. The bench slope. ✅ Flatten now, with per-token wakeups (above).

Tests

  • Rust unit tests:
    • token status: in flight, live or over;
    • linger after withdrawal;
    • rename;
    • a token change tells only what names it;
    • a counted union matches what it still holds, expiry included;
    • a named subscribe waits for its token, is held to it, and is renamed by an update.
  • Rust integration:
    • lite_07_a_named_route_is_held_to_its_token
    • lite_07_renewing_a_route_token_keeps_it_announced
    • lite_07_a_route_ends_with_its_revoked_token
  • Rust/JS interop: lite_auth_id_interop carries the largest nameable ID across on AUTH and SUBSCRIBE. Both sides refuse u64::MAX.
  • JS: the ID round-trip, the Rust golden, u64::MAX refusal, reused-ID refusal, request-auth round-trips, and a route ending with the token it names.
  • Bench: a new named bench in rs/moq-net/benches/auth.rs. It sweeps live tokens [1, 16, 128] against named routes [1, 16, 128] and times one token narrowing and restoring. Before and after the per-token wakeups, median of 5 interleaved runs of each binary on the same machine:
tokens / routes before after
1 / 1 13.6 µs 11.4 µs
1 / 16 14.3 µs 11.6 µs
1 / 128 17.5 µs 11.6 µs
16 / 1 17.0 µs 11.5 µs
16 / 16 19.0 µs 11.7 µs
16 / 128 35.0 µs 11.8 µs
128 / 1 38.7 µs 11.9 µs
128 / 16 40.7 µs 11.7 µs
128 / 128 56.6 µs 11.8 µs

The cost no longer grows along either axis.

Checks run on the pushed head (merged with origin/main, including the Codex fix):

  • just check passes, including the wasm build that used to fail.
  • just drafts check passes.
  • just test interop --all passes.
  • cargo test -p moq-net passes (2113 lib tests plus integration).

Interop note: the earlier CI failure printed remote error: 54 (Unroutable) on an audio subscribe, right after the chromium cold-start audio FAIL. It shows up while the harness tears down the browsers. The relay's upstream session to the publisher dies, and #5729 maps a dead upstream session to Unroutable. The message is a teardown artifact of that known flake, not this PR. The rerun passed.

Follow-ups

  • quest/m1/auth/request-auth-id-ietf.md (new): the draft-lcurley-moq-auth mirror, JS named enforcement, and the doc/concept/standard.md update.
  • Subscription::auth in Rust and JS, once a consumer needs to name a token on a subscription.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 7 commits October 18, 2026 22:12
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AUTH carries an ID, numbered densely per side, and SUBSCRIBE,
SUBSCRIBE_UPDATE, FETCH, ANNOUNCE_REQUEST, and ANNOUNCE_START/UPDATE/
RESTART carry an Auth field naming one. A named request is admitted
by that token's grant alone, waits while it is in flight, and ends
with it. A presenter FIN withdraws a token gracefully: it keeps
deciding the requests already naming it until they move off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The auth bench sweeps the peer's tokens against the routes naming
them, timing one token's change. js/net numbers its Auth Streams,
names a route's token by its ID, withdraws with a FIN, and refuses a
reused or maximal ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec the lite-07 AUTH ID and each request's Auth field in the draft,
with FIN-linger withdrawal for gapless renewal. Cross the largest
nameable ID between Rust and JS and refuse u64::MAX on both. Document
named announcements, and split the moq-transport mirror into its own
quest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A change to one of the peer's lite-07 tokens bumped the session's auth
epoch, so every gate and every announce stream re-decided every route,
and every presented token's stream woke on every AUTH_OK. The peer's
tokens now live apart from the session state, each request names its
token through a Changes set that only that token's changes touch, and
an announce stream re-decides only the routes naming a changed token.
A presented token's stream waits on its own withdrawal signal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The presenter re-unioned every token's grant on each AUTH_OK, and the
acceptor every issued grant on each issue, so one token's change still
cost O(tokens). A Tally keeps per-pattern and per-deadline counts, so a
change costs that token's own grant. With it the named bench is flat
across both axes.

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

Copy link
Copy Markdown
Collaborator Author

Recording the maintainer's decisions on the seven open questions:

  1. Dense AUTH IDs from 0: keep.
  2. ANNOUNCE_RESTART carries Auth: keep.
  3. FIN-linger withdrawal as the renewal mechanism: keep.
  4. Only the presenter names routes. No Subscription::auth until a consumer asks for it.
  5. A named ANNOUNCE_REQUEST filters per route: keep.
  6. JS acceptor named-grant enforcement: deferred to quest/m1/auth/request-auth-id-ietf.md.
  7. Bench slope: flatten now. Done in 1931786 and 14483f5:
    • The peer's tokens live apart from the session auth state, and each request watches its own token through a Changes set.
    • An announce stream indexes its routes by token, so it re-decides only the routes that name the changed token.
    • A presenter's token stream waits on its own withdrawal signal, not on every state change.
    • Grant unions are kept as per-pattern counts, so one AUTH_OK no longer re-unions every token.

named bench, median of 5 interleaved before/after runs: 13.6 to 56.6 µs before, flat at 11.4 to 11.9 µs after across tokens [1, 16, 128] x routes [1, 16, 128]. The full table is in the description.

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review October 19, 2026 16:43
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 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-19T18:08:58.167285Z 5b4476c 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: 14483f5773

ℹ️ 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/auth.rs Outdated
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The change adds AUTH IDs and optional auth fields to lite announcements, subscriptions, updates, and fetches. It tracks named-token grants and lifecycle, then applies those grants to announcement and request admission. Token withdrawal removes grants from session scope while existing named requests remain tied to that token. Tests, interoperability checks, benchmarks, drafts, and documentation are updated.

Priority: ➖ Normal

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.98.1)

Clippy execution timed out

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

kixelated and others added 2 commits October 19, 2026 09:56
# Conflicts:
#	js/net/src/auth_session.ts
#	js/net/src/lite/connection.ts
When a route's own token ended, the presenter fell back to naming no
token, so its next re-price or restart went out under the session's
scope and the receiver reattached a route that should have ended with
its credential. The presenter now retracts every advertisement naming a
token of ours that ended, and keeps it off the wire until the route
names another. Mirrored in js/net, where a withdrawn token also wakes
the announce loop. The draft states the sender rule.

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: e44c2e93a8

ℹ️ 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/lite/subscriber.rs Outdated
The per-token index of received announcements was filed under each token a
route named but cleaned only under the ones it still named at retract. A rename
dropped before it applied, or one a verdict applied, left the path under the old
token, so a peer could grow the index without bound with unique paths. Each
route now records the tokens it is filed under and refiles on every rename and
verdict, so the index holds exactly what live routes name.

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: 6b27abad45

ℹ️ 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/lite/publisher.rs
A SUBSCRIBE_UPDATE that renamed a running request only marked its gate stale,
and the same pass went on to serve the next group before the gate judged the
new token. A rename to an ended token, or one whose grant excludes the path,
let one more group out under it. The gate is now checked after reading updates
each pass, so nothing is served under a rename until it is judged.

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: 5b4476c7de

ℹ️ 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/auth.rs
match next.as_mut().map_or(scope, |naming| naming.verdict(direction, path)) {
Verdict::Waiting => {}
Verdict::Admitted => self.current = self.next.take().expect("checked above"),
Verdict::Denied => return Verdict::Denied,

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 Adopt a denied replacement before returning

When an announcement is renamed from token A to token B, or to the session scope, and the replacement has already decided Denied, this return leaves current pointing at A and next at the replacement. The Naming for A therefore remains counted even though the update replaced it; if the presenter then gracefully withdraws A, AuthServe's poll_peer_unnamed never completes, leaving the old AUTH stream and acceptor request open until the advertisement changes or ends. Move the decided replacement into current even when it denies the path so the previous authority is released.

Useful? React with 👍 / 👎.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Closing per maintainer decision: lite drops the Auth ID and the per-request Auth field and keeps union-only scope. There's no per-request auth on lite, and no byte spent on every request. The initial token rides the CONNECT URL (or an auth header once browsers allow one), and more tokens join the union through AUTH streams. A follow-up quest PR rewrites quest/m1/auth/request-auth-id.md to match. It includes how a request racing a just-presented AUTH avoids an extra RTT without naming it. moq-transport's per-request tokens stay acceptor-side, as #5148 modeled them.

(Written by Claude Opus 5.5)

@kixelated kixelated closed this Oct 19, 2026
@kixelated
kixelated deleted the quest/m1/auth/request-auth-id branch October 19, 2026 18:33
kixelated added a commit that referenced this pull request Oct 19, 2026
… race

Rewrites quest/m1/auth/request-auth-id.md as quest/m1/auth/request-race.md
after the maintainer closed #5927: lite AUTH gets no ID and lite requests no
Auth field, so lite requests are judged by the session union alone. The
remaining race, a request sent right behind the token it relies on, gets
options (acceptor waits, AUTH_WAIT, presenter waits) with a recommendation.
The auth README, request-update and token-in-band follow: moq-transport
per-request tokens stay acceptor-side, and the first token stays in the
CONNECT request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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