Repository navigation
feat(relay): upstream cluster links and tls:// qmux - #4816
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An upstream link is never offered a route learned on another upstream link. The mark comes from a peer entry's upstream field when this relay dials, and from the grant's upstream claim when it accepts. 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 tls:// URL dials qmux over TLS on TCP with the connect TLS settings, and listen.tcp.tls serves the TCP listener over TLS with the listen certificate. The listener asks for no client certificate, since qmux's TLS accept keeps no peer identity for the auth server. 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>
|
Background quest run complete; leaving this as a draft for the maintainer. Open decisions (recommendation first):
Suggested follow-up: mTLS peer identity on the (Written by Claude Opus 5.5) |
|
Warning Review limit reachedYou'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 1 minute. View limit detailsLimit details: You’ve used all 4 included reviews currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (27)
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. Comment |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 9959e26
Direction: the per-link upstream horizon is well scoped, including reverse-dial grants, alternate-route selection and revalidation. The TLS listener needs one correction before it delivers its documented token-only behavior.
[P2] Clear the pinned-peer policy as well as CA roots for TCP TLS (rs/moq-tokio/src/server.rs:1020–1022). TcpTls::new clones Listen and clears root, but retains peers. Listen::client_auth prioritizes peers (tls.rs:1394–1400), and PeerVerifier requires a client certificate. An embedder sharing a Listen configured with a QUIC mesh fingerprint allowlist therefore rejects a token-only tls:// client during the handshake, before SETUP/auth can run, despite this listener explicitly promising no client-certificate requirement. Set listen.peers = None on this TCP-specific clone and add coverage with peers: Some(...), not only root.
Verification: static review of all 27 changed-file patches, surrounding TLS verifier code, and discussion. No tests or live handshakes were run; existing TLS coverage sets root but not peers.
Clearing only `root` left a QUIC pinned-peer allowlist on the TCP listener's TLS config, so a token-only tls:// client was refused with CertificateRequired before SETUP. Cover both policies in the test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Automated review:
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 620ba4c
Follow-up to 9959e26, separating the main merge from the two-file TLS fix. The prior P2 is addressed: rs/moq-tokio/src/server.rs:1020–1023 clears both root and peers on the TCP-only clone, including the reload path. rs/moq-tokio/src/tcp.rs:365–417 now covers both QUIC client-auth policies and still checks rejection of an untrusted server certificate.
Direction: this now matches the documented token-authenticated TLS listener without changing QUIC peer authentication. No additional actionable defect found in this follow-up.
Verification: static inspection of the fix, surrounding configuration/reload and handshake test paths, and current discussion. I did not run tests or live TLS handshakes, or verify current CI; this is a COMMENT, not merge approval.
|
Merge summary. Decisions (settled by the maintainer, as recommended):
Changes since the last review round:
Impact: additive public API only ( Follow-up: mTLS peer identity on the (Written by Claude Opus 5.5) |
…ax-delay Renames the subscriber-side call sites main added in #4659 and #4816 to max_delay / maxDelay, and fixes review findings: the JS net docs, the upgrade note on JS ignoring the old key, the subscribe-ranges quest, and the stale Container docs (its refused --linger is hidden from --help). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… listener A client CA with no QUIC backend compiled in now returns NoBackend instead of the stream-only MtlsUnsupported message, and MtlsUnsupported names the flags (--listen-tls-root, --listen). The tls:// test from #4816 set a client CA on a TCP-only server, which this PR refuses; it now adds the QUIC listener that verifies the CA, and still shows the TCP listener asks for no certificate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops the Live tests and Replaying/Quiet code that #4437's sim-time port touched, keeps the new unknown-announce-type test on moq_net_sim, and turns upstream_view_hides_upstream_routes into a plain #[test]: #4816 added it as #[tokio::test] after #4437 removed tokio's macros from moq-net's tests, so main's moq-net lib tests do not compile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r failover The ranking, path-keyed rendezvous hash, epoch identity, and upstream spread already landed (#4403, #4741, #4816, #4942). What was left were tests: the equal-cost pool test now also proves losing an advertiser moves only its paths, and route_change covers a redundant pair (two publishers under one explicit epoch) failing over mid-group, whether the incumbent becomes unreachable or ends, with no timestamp rewind. Deletes the finished quest and moves its ranking guidance into Routes and announces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quest:
quest/m1/cluster-routing/transit.md(deleted here as finished).Problem
moq.pro's edge tier and a drone uplink need one routing rule: a relay must never carry traffic between two of its upstream links (an edge between two cores, a drone between two CDN relays), while everything else still transits. A per-node switch cannot express a drone that transits for its mesh but not for its CDN uplink, so the mark belongs to the link. Edge links also want qmux over TLS on TCP, which only existed in plaintext (
tcp://).Approach
peer: boolroute flag becomes a privateLink { Local, Peer, Upstream }. Neworigin::Producer::upstream()marks a handle's routes as learned upstream (a peer, forSourcepurposes). AConsumertaken from an upstream handle hides every upstream-learned route from bothannouncedandrequest_broadcast, so the best route it offers skips a better upstream one. One mark on the link's handle covers both directions.upstream(config andconnect_api). A dialed upstream peer getsself.origin.upstream()instead of.peer(). An accepted session is upstream when its grant setsupstream: true(withpeer: true), so an edge treats a core that dials it as upstream too.Cluster::publisher/subscribermark the link once inmounted(). A recheck that flipsupstreamcloses the session, as a flippedpeeralready does.run_remote_once/run_remote_sessionnow take theDialTargetinstead of four loose args.Grant::upstream, refused byGrant::validatewithoutpeer(Error::UpstreamWithoutPeer). Mirrored injs/auth's zod schema and the cross-language vector.tls://host:port/pathdials qmux over TLS on TCP with the connect TLS settings (fingerprint pins and redirect ranking treat it likehttps).listen.tcp.tls = true(--listen-tcp-tls) serves the TCP listener over TLS with the listen certificate, reloading it. It asks for no client certificate: qmux's TLS accept keeps no peer identity for the auth server, so neither atls.rootnor pinnedpeersmeant for QUIC apply there, and a peer on it authenticates with a token.Tests:
origin::tests::upstream_view_hides_upstream_routes(moq-net) andcluster::tests::upstream_grant_hides_other_upstreams.rs/moq-relay/tests/upstream_links.rs, all overtls://: a two-region tiered cluster (both edges pick the same core for every path, an edge broadcast reaches the far relay, losing a core moves only its paths), a core-to-core no-transit check through both a forward-dialed and a reverse-dialed edge, and a mesh/uplink/CDN line that transits both ways. Sensitivity: disabling the filter failsedges_never_transit_between_coreswith core 1's route at core 2 via the reverse-dialed edge ([11, 21]).tcp::tests::tls_carries_qmux_and_refuses_an_untrusted_certificate, with QUIC CA roots and with pinned peers configured (the pinned case fails withCertificateRequiredwithout the fix), andtcp::tests::tls_without_a_bind_is_refused.Decisions (settled by the maintainer)
upstream: truebesidepeer: true, mirroring howpeeralready classifies accepted sessions.upstreamwithoutpeeris refused byGrant::validate(fail loud), rather than implyingpeer.tls://TCP listener asks for no client certificate, so it coexists with QUIC mTLS and peers on it present a token.Impact
Public API (all additive):
moq_net::origin::Producer::upstream(): new.moq_relay::cluster::Peer::{upstream, with_upstream}and theupstreamfield on peer objects (cluster.connectconfig,connect_apiJSON): new.moq_relay::auth::Token::upstream: new public field.moq_auth::Grant::upstream(JSONupstream) andmoq_auth::Error::UpstreamWithoutPeer: new.Grantis#[non_exhaustive];Erroralready is.@moq/authGrantSchema: optionalupstream, refined to requirepeer.moq_tokio::tcp::Config::tls(--listen-tcp-tls,MOQ_LISTEN_TCP_TLS,listen.tcp.tls) and thetls://client scheme: new. A TLS TCP session still reportsTransport::Tcpto the auth server.Wire: none. No message, field, or ALPN changes;
tls://carries the existing qmux-01 framing, negotiatingqmux-01.<moq alpn>as the TLS ALPN, and no draft changes. Relays from before the mark treat every link as transit, so a cluster migrates a region at a time.Cross-package sync: relay config and behavior are documented in
doc/bin/relay/{cluster,config,auth}.md; the grant claim lives inmoq-auth, mirrored injs/auth(there is nojs/tokencounterpart for grants);doc/lib/rs/{moq-auth,moq-net}.mdupdated. No bindings or drafts touched.Alternatives
upstreamwithoutpeer: implyingpeerwould be one less rule for auth-server authors, but hides a misconfigured grant.Consumerside naming: no separate public consumer method; the view comes fromProducer::upstream().consume(). AConsumer::upstream()-style filter would read like "the upstream routes", the opposite of what it shows.tls://: refusingtls.rootalongside--listen-tcp-tls(as the closed feat(relay): add edge and core cluster roles over TLS qmux #4731 did) would forbid a relay with mTLS QUIC peers from also serving TLS edge links. Carrying the identity would mean doing the TLS accept ourselves instead of throughqmux::tls::Server.Follow-ups
tls://listener, so cluster links on it can authenticate by certificate instead of a token.🤖 Generated with Claude Code
(Written by Claude Opus 5.5)