Skip to content

feat(net)!: announcements carry availability; a let-go is offline, not END - #8375

Draft
kixelated wants to merge 6 commits into
mainfrom
quest/m2/cluster-routing/routes/availability
Draft

kixelated wants to merge 6 commits into
mainfrom
quest/m2/cluster-routing/routes/availability

Conversation

@kixelated

@kixelated kixelated commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Announcements now report availability. When this side lets go of a route, readers get a distinct offline event instead of the origin's END. This covers Rust, js/net, and every binding. Implements quest/m2/cluster-routing/routes/availability.md, building on the Listing API.

Draft, waiting on one maintainer decision (below). Unit and integration tests pass and just check passes, but every finish X -> Y lane of just test interop --all fails: a cleanly finished broadcast reaches readers through a relay as Offline, not Absent.

Waiting

Maintainer: choose how a token-scoped lite-07 session certifies withdrawals, so a clean finish ENDs across a relay. The quest file keeps this item in its own ## Waiting section.

Scoped lite-07 sessions never certify, so no END crosses a relay

The router reports END only for a claim a floor certifies. Anything else is let go and goes offline. That rule predates this PR. Before this PR, every unserve was reported as END anyway, so the gap never showed.

A token-scoped publisher answers the relay's request for "" with a floor that certifies nothing, because its grant (finish-…/**) does not cover "" (Answer::certified). The relay answering a scoped reader hits the same thing: the reader's limit doesn't cover "", and the relay only knows the origin beneath finish-…/. Every token-scoped publisher and reader has this shape, so on moq.pro every clean finish would read as offline.

What I tried and reverted: certify "the requested prefix as the grant shows it", with the router passing per-region floors to the answer. That fixed interop, but it's unsound. A receiver doesn't necessarily mirror the presenter's grant: a relay with no limit has scope None. A grant shrink then removes a claim without bumping the revision, so the receiver keeps the stale claim. auth::lite_07::tokens_union_and_withdrawing_one_shrinks_it caught it.

Options:

  1. (Recommended) The subscriber narrows its ANNOUNCE_REQUEST to its scope's single region. That region is where its limit and its subscribe grant meet. It reopens the request when that region changes, so a widening renewal isn't lost. Floors keep their exact whole-prefix meaning, there's no wire change, and the draft gains one SHOULD. A multi-region scope still certifies nothing. Best done as its own prerequisite PR, with this one rebased on it.
  2. Floors carry regions on the wire (lite-07-wip). This reverses epic decision 10 ("a marker, not a region list").
  3. Land this PR with the finish lanes accepting offline, and do 1 later. Not recommended: until then readers can't tell a finished broadcast from a lost one, which is the point of this quest.

Public API impact (breaking, no wire change)

  • Rust: RouteEvent::Offline; announce::Event::{Unavailable, Available, Offline} beside Present/Absent; RouteInfo::available().
  • JS: listing event kinds unavailable | available | offline, route kind offline, RouteInfo.available.
  • FFI: MoqAnnounceEvent::{Unavailable, Available, Offline}, MoqRouteInfo.available. C ABI: MOQ_ANNOUNCE_KIND_{OFFLINE,UNAVAILABLE,AVAILABLE}, moq_route_info.available. Go, Python, Kotlin, Swift and Dart aliases and docs are mirrored.

Impact / migration

An exhaustive match on announce::Event or RouteEvent needs the new arms. To keep the old behavior, treat Offline like Absent/End and ignore Available/Unavailable.

Choices made without asking

  • Kept the name Absent and did not rename it to Ended.
  • Availability rides on Update/RouteInfo; the listing turns flips into Available/Unavailable.
  • A session drop, an auth limit loss, or a narrower interest goes offline. moq-transport ends a route only on NAMESPACE_DONE or a clean stream close.
  • An unavailable route ranks with draining routes.
  • Overlap with moq-dev/moq#8369: whichever lands second turns RouteEvent::Offline into its route kind.

Suggested follow-ups

  • The Waiting item above. Recommendation: option 1 as a prerequisite PR.

Checks

  • just check: pass. Rust and JS unit and integration tests: pass.
  • just test interop --all: fails in the finish lanes only, cause above.

Caused by

None: this is a feature.

Models

Size: M
Implementer: Claude Code, Opus 5.5, unknown effort

(Written by Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 5 commits November 9, 2026 07:18
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>
@kixelated kixelated changed the title Announcement availability feat(net)!: announcements carry availability; a let-go is offline, not END Nov 9, 2026
…certification

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

This branch has not been deployed

No deployments
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