feat(cli)!: inspect a relay with moq ls and moq fetch - #4032
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#4059) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Carry main's new announce consumers over to the `announce::Event` shape (#4059): cluster, auth_lifetime, hidden_cluster, smoke, and announce_to_serve tests, plus the hidden-broadcast docs. `moq fetch` (#3965) already landed on main, so its quest leaves this line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ing main Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The subscriber origin asked the relay for every announcement, which hides dot-prefixed segments, so `moq fetch` never resolved `.stats/...` and timed out while HTTP `/fetch` served it. Scope the session to the broadcast, as `moq ls` scopes to its prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…EADME # Conflicts: # quest/m1/README.md # rs/moq-relay/src/connection.rs
Co-authored-by: Grok 4.7 <noreply@x.ai>
…pts (#4146) 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: 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>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
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: Grok 4.7 <noreply@x.ai>
…free port (#4198) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c40a2d8373
ℹ️ 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".
| // forward the stored chain as-is (no self push here). | ||
| let mut initial: Vec<(crate::PathOwned, Hops, crate::origin::Cost)> = Vec::new(); | ||
| while let Some(update) = announced.try_next() { | ||
| while let Some(event) = announced.try_next() { |
There was a problem hiding this comment.
Wait for Live before sending the initial count
When a downstream announce stream opens while this relay's origin is still replaying an upstream session, try_next() can return None before Event::Live. This then sends ANNOUNCE_OK with a partial or zero active count, causing the downstream cursor to emit Live and a non-following moq ls to exit before the remaining routes arrive. Defer the initial count until the origin actually yields Live, rather than treating a temporarily empty queue as caught up.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid gap, but not fixable this way. I tried holding the burst until the origin yields Live: relays in a mesh replay from each other, so each announce stream waits on the peer's own replay (directly, or around a triangle), and the cluster tests (goaway_cluster, cluster_unknown, lan_cluster_path_carries_broadcasts_both_ways) deadlock or time out. Breaking that cycle needs a design decision (for example, waiting only on sources that are not reachable through the requesting peer, or bounding the wait), so I left the empty-queue count as is and flagged it as a follow-up.
(Written by Claude Opus 5.5)
There was a problem hiding this comment.
Maintainer decision: won't fix. A relay must never block its answer on other nodes finishing their replay; waiting across the mesh is worse than this rare race. moq ls reports what is listed at the moment the count arrives.
(Written by Claude Opus 5.5)
| // Land before decoding past the boundary, so no live update | ||
| // enters the origin ahead of the marker. | ||
| Self::poll_landing(&mut self.replaying, &mut run.landing, waiter); | ||
| match stream.reader.poll_decode_maybe::<lite::AnnounceBroadcast>(&mut cx) { |
There was a problem hiding this comment.
Do not infer the replay boundary from a quiet timer
For Lite03/04, a runtime stall or packet gap longer than the 30 ms quiet window makes this call drop the replay guard before attempting to decode the next message, even when that announcement is already buffered. The route then appears after Live, so a non-following listing can silently return a partial snapshot; the IETF namespace path uses the same Quiet heuristic. Silence is not a reliable protocol boundary, so use an explicit boundary or refuse snapshot completeness on versions that cannot provide one.
AGENTS.md reference: AGENTS.md:L16-L16
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Leaving this as is. Lite03/04 carry no initial-set boundary on the wire, so a quiet window is the only signal short of never going live, which would make a non-following moq ls hang on those versions. The timer is the documented fallback; lite-05+ uses the ANNOUNCE_OK count, and an explicit boundary for moq-transport is tracked by the ietf-announce-count quest.
(Written by Claude Opus 5.5)
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>
…o waiter lists (#4216) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…imit (#4197) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
…de a browser (#4217) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ck (#4215) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: moq-bot[bot] <186640430+moq-bot[bot]@users.noreply.github.com>
…4120) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Jakub Migdał <j.migdal42@gmail.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
…f quests (#4229) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Patch bumps for unreleased behavior since #3879. Rust stays with release-plz. Go stays at 0.7 because the wrapper additions are compatible and CI owns that patch. Co-Authored-By: Grok <noreply@x.ai>
Co-authored-by: GPT-6 <noreply@openai.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: GPT-6 <noreply@openai.com>
#4170) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <grok-4.7@x.ai>
…kills (#4227) 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: GPT-6 <noreply@openai.com>
Co-authored-by: GPT-6 <noreply@openai.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # quest/m1/README.md
# Conflicts: # doc/lib/rs/moq-auth.md # quest/m1/README.md # rs/kio/src/loom.rs # rs/kio/src/waiter.rs # rs/moq-auth/Cargo.toml # rs/moq-auth/src/client.rs # rs/moq-cli/src/fetch.rs # rs/moq-net/src/lite/subscriber.rs # rs/moq-net/tests/support/mock.rs # rs/moq-relay/src/auth.rs # rs/moq-tokio/tests/broadcast.rs
main's max age test took the first announce event as the publisher's broadcast; on this line an empty initial set yields `Live` first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Landing pass:
(Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9796b47029
ℹ️ 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".
|
|
||
| // Scoped to the broadcast, so the relay announces it by name even when a hidden | ||
| // segment such as `.stats` would keep it out of an unscoped listing. | ||
| let pattern = moq_net::Pattern::subtree(&broadcast).with_context(|| format!("invalid broadcast `{broadcast}`"))?; |
There was a problem hiding this comment.
Handle maximum-depth broadcasts without appending
**
When --broadcast names a valid 32-segment path (Path::MAX_PARTS), Pattern::subtree appends a globstar as a 33rd segment and returns TooManySegments, so moq fetch now rejects that broadcast before connecting even though it was fetchable before this scoping change and is valid on the wire. Special-case maximum-depth paths with an exact literal pattern, since they cannot have valid descendants.
Useful? React with 👍 / 👎.
Targets
dev: this line carries a published moq-net break (#4059:AnnounceConsumer::nextreturnsannounce::Event).Problem
An operator inspecting a relay had to
curlits HTTP/announcedand/fetch, outside the session's own auth, and nothing showed how to watch the live set change or read the relay's stats.Approach
Livemarker once the initial set has arrived, so a listing knows when it is complete.moq lsto list what is live on a relay #4121:moq ls [prefix] [--follow] [--json]lists what is live over MoQ. (moq fetchlanded onmainin feat(cli): addmoq fetchto read one group of a track #3965.)moq fetchnow scopes its session to the broadcast, so a hidden broadcast such as.stats/node/<node>resolves by name instead of timing out. Regression test:a_hidden_broadcast_is_fetched_by_name.doc/bin/inspect.mdcoveringls,--follow,fetch, theircurlequivalents, and reading the stats track; linked fromdoc/bin/cli.md,doc/bin/relay/http.md, and the sidebar. Fixedcurl .../announced/(404) to/announcedincli.md.devthenmain. Conflict resolutions that carry main's fixes onto dev's refactors: fix(auth): keep accepted grants on fixed expiry deadlines #4237's fixed expiry deadline now lives in dev'slease::Producerclock (Grant::deadline()snapshot per accepted grant); fix(net): end a track with its session's error when the session dies #4120's session-death mock uses dev'skio::Shared; perf(kio): keep a parked waiter that quiet lists still hold #4240's waiter test drops a helper dev removed; main's newmax_agetest waits pastLive. Deletes the questline.Impact
announce::Updatebecomesannounce::Announce,announce::Kindis removed, andAnnounceConsumer::next,poll_next,try_next, and theStreamitem returnannounce::Event. No moq-ffi, libmoq, or JS API change.lsverb;fetchreaches hidden broadcasts.Alternatives
curlfor stats and leavemoq fetchunable to read them. Rejected: the fix is a scoped origin, the same onelsuses.Follow-ups
Livemarker is thejs-announce-caught-upquest; an explicit moq-transport boundary isietf-announce-count.moq lscan end early. Waiting for the origin'sLivedeadlocks cluster meshes (relays wait on each other's replay); needs its own quest.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code