Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # js/net/src/lite/publisher.ts # quest/m1/auth/README.md # quest/m1/auth/relay-refresh.md
…4124) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai> Co-authored-by: bgreenway <brad.greenway@me.com> Co-authored-by: Brad Greenway <brad.greenway@surveillx.com>
# Conflicts: # js/net/src/ietf/publisher.ts # js/net/src/ietf/subscriber.ts # js/net/src/lite/publisher.ts # js/net/src/lite/subscriber.ts # quest/m1/README.md # quest/m2/README.md # rs/moq-net/src/ietf/subscriber.rs
…rants (#4200) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…side it (#4181) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…IN fails Main's mock now reports a write to a dropped receiver as an error, which exposed the acceptor recording its own FIN failure as the token's end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the lite announce loop against #4302's prefix-rooted cursor (the grant still matches the full path), keeps both Request::auth and Request::token, and applies the quest audit: path-patterns and setup-token are done, the auth README keeps main's preflight, error-codes, and expired-error children. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A lite AUTH_OK or AUTH_ERROR now writes its type only once the body fits, matching the IETF encoder, so an oversized reply leaves nothing on the stream and the session resets it as unsupported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lite AUTH_ERROR code past u32 closes the session with PROTOCOL_VIOLATION instead of saturating. JS AuthSession.close() recomputes the union, and a JS presenter closes its side when the acceptor FINs. A dropped serve task settles Issued::closed with the session's error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(net): refuse an AUTH_OK that cannot be encoded, on any sizing error
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s JS test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/m1/auth/narrowing
… result Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…owned gate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A joining FETCH answers from its subscription's cache, which outlives the subscription, so it now holds its own gate on the subscription's namespace through loading and the whole response. The FETCH gate is no longer optional. A TRACK_STATUS answer is raced against the gate while it is written and reset if the grant narrows first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A positive, #[non_exhaustive] setup::Extensions { auth, solicit }, all on by
default, set through Client::with_extensions and Server::with_extensions. It
replaces the unconditional SOLICIT and AUTH options and folds in what
run_setup used to decide alone.
- A side that declines MoQ Solicit leaves SOLICIT out of its SETUP (every
draft) and takes an unsolicited PUBLISH_NAMESPACE without calling it a
violation.
- A side that declines MoQ Auth leaves AUTH out of its SETUP, presents no
credential, and refuses an inbound AUTH as a protocol violation.
- A draft-17+ server now omits AUTH when the client did not offer it.
Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
connect::Config and listen::Config carry the setup::Extensions their sessions offer, TOML-only and omitted when every extension is offered. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ConnectProps and AcceptProps take Extensions { auth?, solicit? }, each on
unless set to false, mirroring the Rust setup::Extensions. MoQ Auth is
negotiated only when both sides offer it, and a side that declined Solicit
takes an unsolicited PUBLISH_NAMESPACE.
Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every AUTHORIZATION TOKEN (0x03) on a moq-transport request now decodes with the SETUP option's Token structure, on the strict decode_params! path and draft-14's parameter block alike. A repeat stays accepted. - REGISTER on a request closes with AUTH_TOKEN_CACHE_OVERFLOW (0x13): we advertise no token cache, so the limit is 0. Only SETUP falls back to a value. - DELETE or USE_ALIAS closes with UNKNOWN_AUTH_TOKEN_ALIAS (0x17), on a request and in SETUP, which used PROTOCOL_VIOLATION. Recorded as a deviation in doc/concept/standard.md. - A structure that does not decode closes with KEY_VALUE_FORMATTING_ERROR. - USE_VALUE is read and grants nothing yet. SessionError gains the two codes, which join the lite draft's Session Error Codes table. DecodeError::Session carries a code a decode closes with. A draft-14 response still ignores a token, as section 9.2 has. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirrors the Rust decode: every request token decodes, REGISTER closes with AUTH_TOKEN_CACHE_OVERFLOW, DELETE or USE_ALIAS with UNKNOWN_AUTH_TOKEN_ALIAS (SETUP too), and a malformed structure with KEY_VALUE_FORMATTING_ERROR. USE_VALUE grants nothing yet. ProtocolViolation gains a code, PROTOCOL_VIOLATION by default, so a decoder names the close and the dispatch sends it. SessionCode gains 0x13 and 0x17. A draft-14 SUBSCRIBE_OK decodes with its own parameter list, so a token there is ignored rather than decoded. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lite publisher holds its grant watch from the first check until the answer is acknowledged, so a TRACK_INFO blocked on flow control is reset instead of finishing after revocation, matching Rust's TRACK_STATUS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…4039-d816ca # Conflicts: # test/interop/clients/js-native/subscribe.ts # test/interop/interop.sh
…uest/m1/auth/request-token Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de' into quest/m1/auth/request-token Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of #4675 onto the auth line, on top of the setup-extensions (#5106) and request-token-decode (#5107) carve-outs, which already supply setup::Extensions, the 0x03 decode and its session closes, and 0x13/0x17. A SUBSCRIBE or PUBLISH_NAMESPACE is authorized by the session grant first, then by the AUTHORIZATION TOKEN it carries, verified by the app's auth::Handle::requests() acceptor and scoped to that one request, else UNAUTHORIZED. With no acceptor a token is refused NOT_SUPPORTED. The token's grant never joins the session union, ends with the request, and is renewed in band by an update (SUBSCRIBE_UPDATE on drafts 14 and 15, REQUEST_UPDATE from 16) on every draft for a subscription and from draft 17 for a namespace. A refused renewal ends only that request. - Draft-19+ advertises and enforces MAX_REQUEST_UPDATES, closing with TOO_MANY_REQUEST_UPDATES (0x1B); earlier drafts keep a local guard that ends only the request. 0x1B joins SessionError, js/net SessionCode, and the lite draft's Session Error Codes. - The sender presents auth::Handle::set_request_token on its requests, keeps one renewal in flight per subscription, and refreshes live announces. - moq-tokio: Auth, Connection::auth, connect::Config::with_request_token, and server::Request::auth. The connection owns the token across reconnects and seeds each session under the lock renewals take. Adapted to the base: messages carry the decoded token (ietf::token::RequestToken, the first if it repeats), so set_request_token takes a setup::Token; draft-14-16 updates arrive through the existing FollowUp routing; tests run on moq_net_sim. Kyle's malformed- and alias-token tests are dropped, since request-token-decode closes the session for those and tests it. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While a renewal's verdict was pending, the publisher answered a token-less REQUEST_UPDATE at once, so its REQUEST_OK went out ahead of the renewal's answer. From draft 17 answers carry no Request ID, so the peer pinned each answer to the wrong update. Every update behind a pending verdict is now stashed, token or not, and handled in order once it resolves. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Admitting a request on its token (verify, check the grant covers the request, hold it for the request's life) was written out at both receivers; it is now auth::Handle::admit_request. The renewal state both receive loops kept by hand (the pending verdict, the FIFO behind it, and its MAX_REQUEST_UPDATES bound) is now request_update::Renewal, and RequestGrant::renew applies a verdict, returning whether it renewed. Each loop keeps only how it answers and how it ends. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
auth::Request's path(), kind(), and token_kind() were three Options that
were always all Some or all None. They become one scope() returning an
auth::Scope { path, kind, token_kind }, so an acceptor tells a request
token from a connection credential with one check.
set_request_token already takes a setup::Token rather than encoded
bytes (carried over in the port), so a caller cannot send an alias form.
Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
moq-lite requests have no AUTHORIZATION TOKEN, so set_request_token on a moq-lite session stored a token nothing would ever send. It now fails with Error::Unsupported instead. moq-tokio follows: Auth::set_request_token returns the live session's refusal (later sessions are still seeded), and a dial whose session cannot carry the configured token fails rather than connect without it. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drafts 15 and 16 answer an update naming the update's own Request ID, which has no stream on the control stream. The adapter found the subscription by re-parsing every SUBSCRIBE_UPDATE the subscriber wrote. The subscriber now registers the update with Control when it sends it, and the guard it keeps while the renewal is in flight unregisters it, so the adapter's sniffer, its map, and its reply hook go. Also drops Peer::max_request_updates and request_update::from_setup, which nothing read. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A refused renewal returned Error::Unsupported only so PUBLISH_DONE would say UPDATE_FAILED, which also turned any track that ended with Unsupported (a relay refusing a copy whose metadata changed) into a failed update. The serve loop now reports Served::UpdateFailed for an update it could not apply, unsupported parameters included, and a track's own error maps to INTERNAL_ERROR whatever it is. A refused renewal ends the request with Unauthorized. Co-authored-by: Kyle Sletmoe <sletmoe@amazon.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 9, 2026
kixelated
added a commit
that referenced
this pull request
Oct 10, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
added a commit
that referenced
this pull request
Oct 10, 2026
…renewal Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
added a commit
that referenced
this pull request
Oct 10, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
quest/m1/auth/request-token(scope restored in #5104). This carries @ksletmoe-aws's #4675 forward: his fork does not allow maintainer edits, so the work moved to this branch. Every commit credits Kyle as co-author.Stacked on #5106 (Extensions) and #5107 (request-token decode). Both are merged into this branch, so the diff includes them until they land on the line. The commits to review here start at
7277e5df3.Summary
A request is authorized by the session's grant first. If that does not cover it, the
AUTHORIZATION TOKENon the request authorizes it, and with neither the request is refused UNAUTHORIZED. The token is honored on SUBSCRIBE and PUBLISH_NAMESPACE.auth::Handle::set_request_tokenpresents the token, and moq-tokio'sConnection::auth()keeps it across reconnects.The decisions from #4675's review (10/1 Q1-Q4, 10/5) are recorded in the quest and unchanged.
Commits
7277e5df3: port of feat(net): a token on a request authorizes that request #4675's remaining behavior onto the current line. It drops what feat(net): declare the offered Setup extensions with setup::Extensions #5106, feat(net): decode a request's AUTHORIZATION TOKEN by the SETUP rules #5107, and fix(net): route draft 14-16 updates to their target #4961 already carry.1d34a0e4b: updates behind a pending renewal are answered in arrival order. Before this, a token-less update could be answered ahead of the renewal, so on draft-17+ the unkeyed answers went out of order.2737117b9: one admission helper (auth::Handle::admit_request) and one renewal queue (request_update::Renewal), replacing four and two copies.536f27bb3:auth::Request::{path, kind, token_kind}becomescope() -> Option<&auth::Scope>.210742ebf: a moq-lite session refusesset_request_tokenwithUnsupportedinstead of ignoring it.e26a4e5f0: the outbound update sniffer is gone. A legacy update's answer is routed by the ID the sender already holds.311a73c8e: a failed update ends through its ownServed::UpdateFailedpath instead of overloadingError::Unsupported.Public API and wire impact
auth::RequestKind,auth::Scope,auth::Request::scope(),auth::Handle::set_request_token(setup::Token) -> Result<()>, andSessionError::TooManyRequestUpdates.Auth,Connection::auth(),connect::Config::with_request_token(setup::Token), andserver::Request::auth().SessionCode.TooManyRequestUpdates.Testing
just checkpasses.just test interop --allpasses everything except the C++ cells, which could not build here becauseuniffi-bindgen-cppis missing.Each fix's regression test fails with the fix reverted, except where noted:
an_update_behind_a_pending_renewal_is_answered_in_ordera_moq_lite_session_refuses_a_request_tokena_session_that_cannot_carry_the_token_refuses_ita_legacy_renewal_answer_reaches_its_subscription(drafts 15 and 16)an_unsupported_track_error_is_not_a_failed_update, which pins the new path; the old return type could not express the difference.For review
auth::Scope.Auth::set_request_tokenreturns the live session's refusal but keeps the token for later sessions.requests()consumer receives both session and request tokens.requests()also stays open on sessions without the Auth extension.Follow-ups: a JS request-token setter and accept-side
requests(), a moq-cli--request-token, the setter through moq-ffi, and the relay lease (quest/m1/auth/request-lease).Supersedes #4675.
(Written by Claude Opus 5.5)
🤖 Generated with Claude Code