Skip to content

feat(moq-net)!: gate broadcast announcements on a liveness flag - #2384

Closed
kixelated wants to merge 1 commit into
devfrom
claude/broadcaster-announcements-api-c3e029
Closed

feat(moq-net)!: gate broadcast announcements on a liveness flag#2384
kixelated wants to merge 1 commit into
devfrom
claude/broadcaster-announcements-api-c3e029

Conversation

@kixelated

@kixelated kixelated commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

What

Separate a broadcast's existence (registered in the origin tree, reachable by a FETCH) from its liveness (advertised to announced() subscribers), and drive announcement off a new per-broadcast liveness flag.

Publishing now registers a broadcast offline: publish_broadcast / create_broadcast make it FETCH-routable but do not announce it. A producer calls set_live(true) to advertise it and set_live(false) to stop advertising while it stays routable.

Why

Today the only way to reach a broadcast is via an announcement, so "exists" and "advertised" are the same thing. We want to route FETCH requests to offline broadcasts eventually, which means announcements should signal liveness, not existence. This PR makes that split: existence lives in the origin tree (what request_broadcast / a FETCH resolves against), and the announce stream is gated on the liveness flag. It also removes the accidental "announce before the catalog exists" race for callers that populate then go live.

How

  • broadcast::{Producer,Dynamic}::set_live(bool) / is_live() and Consumer::is_live() back a liveness flag on the broadcast.
  • The origin registers existence synchronously on publish, then a small per-broadcast reconciler watches the flag: it announces a live active route and unannounces one that goes offline or closes. Existence stays in the tree throughout; a promoted backup announces only if it is live.
  • Route selection among broadcasts at the same path (active vs backup, hop/cost ordering) is unchanged.

API impact

Additive everywhere; nothing renamed or removed:

Layer New
rs/moq-net broadcast::{Producer,Dynamic}::{set_live,is_live}; on broadcast::Consumer is_live + the async observer live_changed / poll_live
rs/moq-ffi MoqBroadcastProducer::set_live
rs/libmoq moq_publish_set_live (new moq.h symbol)
py / swift / go / js set_live / setLive / SetLive / Broadcast.live + setLive

Breaking (behavioral, not signature): in rust and js, publishing no longer announces on its own — callers must set_live(true). Hence this targets dev. The FFI-based bindings keep behavior: their announce verb marks the broadcast live internally, so announce still means "discoverable now" (additive only there).

No wire change / no IETF draft update: announce already is the wire liveness signal. cpp/obs unaffected (the C ABI change is a new function).

Consumer observer: broadcast::Consumer exposes an async live_changed(last) -> Option<bool> / poll_live (mirroring closed/poll_closed; None on close), so a rust consumer can react to a broadcast going offline — parity with the js Consumer.live signal. The origin's own unannounce reconciler drives off this same observer.

Interim API note

set_live is a standalone flag for now. Once #2241 lands the mutable broadcast::Route { hops, cost }, live should fold in as a third field (Route { hops, cost, live }) reconciled through the same route observer, dropping this PR's separate liveness channel and reconcile_publish reconciler. live is a genuine third axis, not cost = ∞: cost orders announced routes, live=false means don't announce but stay FETCH-routable.

Tests

  • moq-net: new test_liveness_gates_announcement (offline = registered-but-not-announced and still FETCH-routable; toggling live drives announce/unannounce; unpublish removes it). Existing origin/announce suite updated for offline-by-default.
  • Verified green: moq-net, moq-native, moq-rtmp, moq-relay, moq-srt, moq-hls, and @moq/net (js, incl. a wire-level regression).
  • cargo doc -D warnings (public items, CI-equivalent) clean.

🤖 Generated with Claude Code

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

Separate a broadcast's *existence* (registered in the origin tree, reachable
by a FETCH) from its *liveness* (advertised to `announced()` subscribers).
Publishing now registers a broadcast **offline**: `publish_broadcast` /
`create_broadcast` make it FETCH-routable but do not announce it until a
producer flips it live. This keeps an offline broadcast routable without
advertising it, the foundation for routing FETCH requests to offline
broadcasts.

Announcement is driven off the flag: the origin gates its announce stream on
liveness via a per-broadcast reconciler, announcing a live route and
unannouncing one that goes offline or closes, while existence stays in the
tree. Route selection (active vs backup at a path) is unchanged.

New surface (additive):
- rust: `broadcast::Producer::{set_live, is_live}`,
  `broadcast::Dynamic::{set_live, is_live}`, and on `broadcast::Consumer`
  `is_live` plus the async observer `live_changed` / `poll_live` (mirrors
  `closed`/`poll_closed`, resolves `None` on close) so a consumer can react to
  a broadcast going offline, matching the js `Consumer.live` signal
- ffi: `MoqBroadcastProducer::set_live`; libmoq: `moq_publish_set_live`
- py `set_live`, swift `setLive`, go `SetLive`, js `Broadcast.live` +
  `setLive`

The FFI-based bindings' high-level `announce` verb marks the broadcast live
internally, so binding behavior is preserved (announce = discoverable now).

BREAKING CHANGE: in rust and js, publishing a broadcast no longer announces
it on its own; callers must call `set_live(true)` (or, in bindings, keep using
`announce`, which now implies liveness). No wire-format change: announce
already is the wire liveness signal, so no IETF draft update.

Interim API: `set_live` is a standalone flag for now. Once #2241 lands the
mutable `broadcast::Route { hops, cost }`, fold `live` in as a third field and
reconcile it through the same route observer, dropping the separate channel
and reconciler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated
kixelated force-pushed the claude/broadcaster-announcements-api-c3e029 branch from 08b616d to 11df534 Compare July 18, 2026 03:08
@kixelated kixelated closed this Jul 18, 2026
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