Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndle Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix the reconnect test, which shadowed its token and never compiled under `--all-targets` with tcp; its OOM was the build on a loaded machine, not the test (it now runs in 0.15s). Hold issued grants instead of leaking them. Extend `a_handle_backs_one_active_session` to cover a draining session keeping an unanswered token beside its successor, and a token added after the drain riding only the successor. Point the relay auth bench at `moq_net::auth`, update the relay refresh tests for a transient refusal that now retries instead of failing `add()`, add MIGRATE entries for moq-net and moq-tokio, and apply rustfmt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirror the moq-net change in js/net. `Auth.Handle` is now a class: a
session-independent handle passed to `connect({ auth })` presents every
token it holds on each session built on it, so a route naming one of its
`Auth`s keeps it across a reconnect. A handle backs one active session at a
time, plus the ones draining after a GOAWAY; attaching another rejects with
`Auth.Duplicate`. Only the peer's verdict, or the handle's end, ends a token.
The reconnect loop owns one handle (`Reload.auth`), surfaced as
`Connection.auth`, and aborts it when the loop closes. A transiently refused
token on moq-lite is presented again after a 1s backoff doubling to 30s, as
in Rust. `Auth.None` is removed.
Update the docs that said to add the token again and re-announce after a
reconnect, and add a MIGRATE entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mark the wire view's auth field internal so the declaration build no longer imports an internal type, and satisfy clippy's field_reassign_with_default in the moq-tokio reconnect test. 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>
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>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # rs/moq-tokio/MIGRATE.md
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>
…sends - `MoqSession::auth()` returns a new handle per call, each watching the grant on its own and starting from the grant it was made with, so concurrent watchers no longer steal each other's changes and `grants()` never repeats the first grant. - `MoqAuth::add` refuses a token with `Unsupported` when the session cannot send it: moq-transport without the Auth extension accepts one only for the requests that name it, which no binding can. - moq-tokio publishes a token's grant before `add` returns. - OBS keeps the relay's refusal for the dock and presents a retyped token again. - Docs: AUTH also rides moq-transport draft-17+ with the Auth extension. - interop.sh guards empty arrays for bash 3.2; the Swift grant wait has a watchdog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
moq-net reports a clean close as Cancel, which the connection's token task surfaced as an error, so client sessions in the bindings saw Cancelled where server sessions saw success. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…notices - moq-tokio's connection refuses a token as unsupported when its session accepts it without AUTH, so a reconnect between the answer and moq-ffi's check can no longer turn an accepted token into Unsupported. - The OBS dock keeps why a token edit was not presented until the next one, instead of losing it on the next status refresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # dart/moq_ffi/lib/src/moq.dart # doc/lib/dart/index.md # doc/lib/kt/index.md # doc/lib/py/index.md # doc/lib/swift/index.md
lite-07-wip gains a client-only Token parameter (0x6) that fills server::Handshake::token, and moq-transport clients now write the AUTHORIZATION TOKEN setup option. Client::with_token sets it. setup::Token's Debug no longer prints the credential. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
connect::Config gains tokens (--connect-token, MOQ_CONNECT_TOKEN). The first rides SETUP, and the URL query while an offered version has no SETUP token; the rest each ride an AUTH stream. A URL that already carries ?jwt= keeps it, and every token rides AUTH. Client::tokens() returns a connect::Tokens handle: each dial reads it, a refusal redials only when it changed, and a live session swaps its AUTH tokens, withdrawing the replaced ones once their replacements are answered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MoqClientConfig gains tokens and MoqClient gains set_tokens, mirroring moq_tokio's connect::Config::tokens and Client::tokens(). The lite draft specs the client-only Token setup parameter (0x6). A relay test checks a configured token reaches the auth server as the SETUP token on lite-07 and as ?jwt= on lite-06, and the interop harness's Rust client now presents its token with --connect-token. Finishes the token-in-band quest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Python, Go, Kotlin, Dart, and Swift take tokens on the client config and expose set_tokens, and each binding page documents them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
js/net mirrors the Rust client: lite-07's Token setup parameter, the moq-transport AUTHORIZATION TOKEN option on the client's SETUP, a tokens connect option with the same credential rules, and a reloading Connection whose tokens signal redials after a refusal only once they changed and swaps AUTH tokens on a live session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rted On moq-transport without the Auth extension the session accepts a token at once for the requests that name it, but a configured token rides no request. Treat it as unsupported instead of granted, and log that once per session. 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>
moq-dev#6109 replaces moq-tokio's connection auth with one moq-net auth handle that spans a connection's sessions, so: - moq-ffi's temporary dispatch enum collapses onto moq_net::auth::{Handle, Watch, Auth}. - Configured tokens are held once per connection on that handle (connection/tokens.rs) instead of re-added per session, which would have presented each one twice while a GOAWAY'd session drained. Each dial tells the holder its connection credential. - A configured token a moq-transport session cannot send is kept for a later session with AUTH rather than dropped, and logged once per dial. - js/net holds configured tokens once per auth handle, so a reloading connection's shared handle presents each once across reconnects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the conflicts with moq-dev#6137: moq-tokio's connection::auth stays deleted, so moq-ffi's auth wrapper collapses onto the one moq_net::auth::Handle, as moq-dev#6137 decided for whichever landed second. Tests from main that build a session's handle use auth::Link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A token added after a GOAWAY was offered to the draining session too: a draining moq-lite session without AUTH refused it as Unsupported, which ended it on the successor, and in JS a draining session opened a fresh AUTH stream for it. Offer new tokens only to active sessions, and in JS stop a transient retry once the session drains. JS also points the handle's grants at the successor as soon as it attaches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lite driver checked only a received GOAWAY before opening an AUTH stream, so a transient retry that woke after this side's own GOAWAY opened a fresh one on the draining session. Gate opens and retries on the link being active, which covers both directions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
During a GOAWAY overlap a token can sit unanswered on the draining session while the successor grants it. A late refusal there ended the token on every session, withdrawing the live grant and every route naming it. A draining session's verdict now ends a token only on that session once another session granted it, in Rust and JS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # js/net/MIGRATE.md # rs/moq-net/MIGRATE.md
…ry stays unanswered - A draining session's refusal is stale while an active session still presents the token, even mid-retry with no grant, so it no longer ends the successor's token. - A token waiting out a transient refusal counts as unanswered, so a publication it might cover waits instead of closing the session as unauthorized. - An ended auth handle stops the reconnect loop (moq-tokio and js/net) instead of redialing sessions the handle refuses. - A session's own handle has no successor: a token added after its GOAWAY fails with GoingAway, and any end of its stream is final. - moq-transport's presenter drops its retry loop: AUTH_ERROR has no transient code. - JS requests() throws Auth.Duplicate, like Rust; docs follow the handle's lifetime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssor moq-transport's AUTH_ERROR GOING_AWAY judged nothing, but the presenter counted it as a refusal, so it ended a shared handle's token before the next session could present it. Matches the Rust presenter, which never marks GoingAway as refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
moq-dev#6137 landed on main first and moq-dev#6109 has since merged main and collapsed moq-ffi's auth dispatch, so this takes moq-dev#6109's ffi auth.rs. The configured token holder sits beside moq-dev#6109's new redirect tracking in the reconnect loop. token-in-band.md is deleted, and bindings-polish.md no longer requires it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@kixelated, could you confirm the API direction for decision 2 in the description? A token accepted over AUTH can lose its live grant during a reconnect before moq-ffi checks it. The binding currently mistakes that missing grant for
Do you prefer A or B, and if A, what public shape should represent that result? Decision 1 is settled on a separate tokens handle with (Written by GPT-6) |
|
@mriise, answers to the open decisions:
Decision 1 stays as recorded (a (Written by Claude Opus 5.5) |
Summary
Clients take tokens as configuration separate from the URL. The first is the connection credential and rides SETUP; each other token rides its own AUTH stream and widens the grant. Finishes the
quest/m1/auth/token-in-band.mdquest.Stacked on #6109. #6137 has landed. The branch carries #6137's commits, then this PR's own (from
feat(net): present the connection credential in SETUP), then two merges of #6109:5b48b53a8ports them onto its session-spanning auth handle, and9d4a891d2takes its headc0b800c7a, which already containsmainwith #6137. Review this PR's commits and those merges;mainsquash-merges, so the merge commits leave no trace.9d4a891d2takes #6109'srs/moq-ffi/src/auth.rs(both sides had collapsed the enum), keeps the token holder beside #6109's new redirect tracking in the reconnect loop, and dropstoken-in-band.mdfrombindings-polish.md's prerequisites.The merge with #6109:
collapses feat(bindings): read the session grant and present tokens in band #6137's temporary dispatch enum in
rs/moq-ffi/src/auth.rsontomoq_net::auth::{Handle, Watch, Auth}, the follow-up feat(bindings): read the session grant and present tokens in band #6137 listed;holds configured tokens once per connection on the shared handle (
rs/moq-tokio/src/connection/tokens.rs), told each dial's connection credential, instead of re-adding them per session, which would have presented each twice while a GOAWAY'd session drained;does the same in js/net: one holder per auth handle, so a reloading connection presents each configured token once.
Wire (moq-net): lite-07-wip gains a client-only Token setup parameter (0x6) that fills
server::Handshake::token, the way moq-transport's AUTHORIZATION TOKEN option already does. moq-transport clients now write that option too.Client::with_tokensets it.setup::Token'sDebugprints only its type and length.moq-tokio:
connect::Config::tokens(--connect-token,MOQ_CONNECT_TOKEN).?jwt=keeps it as the connection credential, and then every configured token rides AUTH. No secondjwtis appended, and SETUP never carries a different value.Client::tokens()returns aconnect::Tokenshandle for refresh:moq-ffi:
MoqClientConfig.tokensandMoqClient::set_tokens(see Decisions).Wrappers: Python
tokens=andset_tokens, GoWithTokensandSetTokens, Kotlin, Dart and Swift the same in their casing. Dart's raw bindings are regenerated.js/net: lite-07's Token parameter, the moq-transport AUTHORIZATION TOKEN option, a
tokensconnect option with the same credential rules, and a reloadingConnectionwhose token changes redial only after a refusal and swap AUTH tokens on a live session.Spec:
drafts/draft-lcurley-moq-lite.mdspecs the Token parameter in lite-07-wip SETUP.Docs:
doc/bin/cli.md,doc/bin/relay/auth.md(the URL token is one member of the union),doc/concept/transport.md, and everydoc/libclient page. The?jwt=form stays documented as the way to reach an older relay.Interop: the Rust client in
test/interop/interop.shmoves?jwt=to--connect-token, so it rides SETUP on the auth version. The binding clients keep the URL form, which covers the query path.Public API and wire impact
-wip. moq-transport clients now send AUTHORIZATION TOKEN in CLIENT_SETUP when configured.Client::with_token.server::Handshake::tokenis now also set on lite-07.connect::Config::tokens(the struct is#[non_exhaustive]),connect::{Token, Tokens, EmptyToken},Client::tokens().MoqClientConfig.tokens(breaking for a Rust struct literal;MIGRATE.mdentry added) andMoqClient::set_tokens.Decisions needed
User preferences recorded on 2026-10-22. These describe the intended direction; the existing setter and reconnect check still need implementation changes.
MoqClient::tokens() -> MoqTokenswithset(tokens), mirrored in each binding and matching moq-tokio'sClient::tokens(). Initial tokens remain onMoqClientConfig. Replace the currently implementedMoqClient::set_tokensAPI.Auth, distinguishing a token accepted by the peer over AUTH from one available only for request-carried authorization. The binding must read that stable result rather than infer support fromgrant() == None, which also occurs during reconnects. This records how presentation completed; it does not preserve permissions while disconnected.Auththe right place for this result? Recommended: preserveHandle::addand legacy request-token behavior. The alternative is to makeHandle::addrequire AUTH and redesign the request-token path.(Written by GPT-6)
Checks
9d4a891d2, on Windows:cargo check --all-targetsfor moq-net, moq-tokio, moq-ffi, moq-relay and moq-cli.cargo testfor moq-net, moq-tokio and moq-ffi passes. moq-relay passes except three tests that fail only in the full run (two installing the crypto provider, one waiting on a WebSocket close log) and pass alone. js/netbun run checkpasses;bun test src/auth src/connectionfails the same set as feat(auth): an auth handle that outlives its session #6109's head under this machine's Bun 1.1.17 (noonTestFinished), and runs 9 more passing tests than it.5b48b53a8, on Windows:cargo testfor moq-net (all), moq-tokio (all), moq-ffi (all), and moq-relay auth (88) pass. Before the merge, moq-tokio'skeylog::client_keylog_logs_only_the_clientfailed once; it also fails onmainon Windows.a_configured_token_without_auth_is_unsupported(before the merge; nowa_configured_token_without_auth_is_kept_for_a_later_session), and js/net'ssessions sharing an auth handle present each configured token once.uniffi-bindgen-dart;dart analyzeanddart testpass formoq_ffiandmoq.just py test(105) passes on5b48b53a8.5b48b53a8:bun run checkpasses; the token, reload, SETUP and auth tests (216) pass.just check(no Nix on this machine), the full js/net suite, Go, Kotlin, Swift,just test interop --all. moq-ffi's API is unchanged by the merge, so the bindings need no regeneration.Follow-ups
mainonce feat(auth): an auth handle that outlives its session #6109 lands (it is in the merge queue).quest/m1/auth/bindings-polish.md.(Written by Claude Opus 5.5)