You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Redesign the entire moq-cli surface into a unified endpoint model: moq-cli becomes a media router that wires endpoints onto one shared Origin. This replaces the current per-purpose subcommands (publish/subscribe/serve/accept/hls) and folds in the network gateways (RTMP/SRT/WebRTC) under one grammar.
This is the follow-up to the library work already landed:
Direction verb (import = into MoQ / export = out of MoQ). It's the pivot: it declares direction and separates the MoQ side (left) from the other endpoints (right), so "which is source / which is sink" is unambiguous.
Other endpoints (after the verb, protocol-prefixed so there are no flag collisions and multiple can compose):
Everything is an endpoint on one shared Origin. No flag is conditional/ignored — passing a flag always adds that endpoint. That's what makes the flag model safe (vs. the mutually-exclusive-modes-as-flags that caused "args in arbitrary places, silently ignored").
No backdoor — listeners are directional. A --rtmp-listen under import accepts pushes only and rejects plays; under export it serves plays only. The operator declares direction; the connecting peer can't choose. (Library follow-up: moq_rtmp::run/moq_srt::run are bidirectional today — the directional server just refuses the wrong request type.)
import/export don't share an endpoint set.--stdin/--hls-connect are import-only (sources); --stdout/--hls-listen are export-only (sinks); only the foreign --rtmp/srt/rtc-connect/listen appear under both. Implement as enum Direction { Import(ImportEndpoints), Export(ExportEndpoints) } with a shared flattened ForeignEndpoints sub-struct for the rtmp/srt/rtc flags.
Naming: bare --client/--server for MoQ (so the TLS sub-flags --client-tls-*/--server-tls-*/--tls-* from moq-native line up); protocol-prefixed --<proto>-connect/--<proto>-listen for everything else. No collisions.
help_heading grouping per protocol so the (necessarily larger) --help stays legible.
Fan-out is free (multiple foreign endpoints after the verb = simulcast). Cross-protocol or mixed-direction in one process is out of scope — that's what the relay is for (run two processes joined by a relay). Direction is a single verb per invocation.
Broadcast rides in the --client URL path. For an importlistener the name comes from the protocol (RTMP app/key, SRT streamid); for --server hosting, TBD (see open questions).
Build the clap surface first (Cli + Direction + Import/Export + ForeignEndpoints, help_heading) and get --help reviewed before wiring runtime.
Wire the shared-Origin runtime: spawn each endpoint as a task feeding/draining the Origin; MoQ side is the reconnect/accept.
Make the listeners directional (library tweak in moq-rtmp/moq-srt).
Do the repo-wide ripple + rewrite doc/bin/cli.md.
just check.
Open questions
--server (MoQ host) mode: how is the broadcast named for --stdin/dial endpoints when there's no --client URL path? (A --broadcast fallback flag, or require the URL path form only with --client?)
Should --client and --server be combinable (a "relay node" that hosts and forwards)? The shared-Origin model allows it; decide whether to expose it now.
Format handling for --stdin/--stdout: reuse the existing PublishFormat/SubscribeFormat enums as the flag value.
(Filed by Claude Opus 4.8 — captures a long design discussion; the grammar and decisions above are settled, the open questions are for the build.)
Summary
Redesign the entire
moq-clisurface into a unified endpoint model: moq-cli becomes a media router that wires endpoints onto one sharedOrigin. This replaces the current per-purpose subcommands (publish/subscribe/serve/accept/hls) and folds in the network gateways (RTMP/SRT/WebRTC) under one grammar.This is the follow-up to the library work already landed:
moq-rtmp/moq-srt/moq-rtcto library-only crates (merged).Client/dial) library role (the "client half").The grammar
Origin's link to the MoQ network):--client <relay-url>— dial a relay. URL path is the broadcast, e.g.https://cdn.moq.dev/demo/bbb.hang. Carriesmoq_native::ClientConfig(--client-*).--server <bind>— host MoQ sessions (moq-nativeServer). Carriesmoq_native::tls::Server(--tls-*,--server-tls-*).import= into MoQ /export= out of MoQ). It's the pivot: it declares direction and separates the MoQ side (left) from the other endpoints (right), so "which is source / which is sink" is unambiguous.--stdin <format>(import-only),--stdout <format>(export-only).--hls-connect <playlist-url>(import-only),--hls-listen <addr>+ TLS/part-target/window (export-only).--rtmp-connect <url>/--rtmp-listen <addr>(+--rtmp-prefix, RTMPS viamoq_native::tls::Server).--srt-connect <url>/--srt-listen <addr>(+--srt-latency,--srt-prefix).--rtc-connect <url>/--rtc-listen <addr>(+--rtc-udp-bind,--rtc-public-addr,--rtc-tls-*). WHIP/WHEP is implied by direction + connect/listen (import: listen=WHIP server, connect=WHEP client; export: listen=WHEP server, connect=WHIP client), so no--whip-*/--whep-*.Command mapping (old → new)
publish --url R --broadcast N ts--client R/N import --stdin tssubscribe --url R --broadcast N --format ts--client R/N export --stdout tsserve --broadcast N ts--server <bind> import --stdin tsaccept --broadcast N--server <bind> export --stdout <fmt>hls --url R import --playlist P--client R/N import --hls-connect Phls --url R export --listen L--client R export --hls-listen L--client R import --rtmp-listen [::]:1935--client R/N export --rtmp-connect rtmps://twitch/KEY--client R/N export --rtmp-connect … --srt-connect …Key design decisions (settled)
Origin. No flag is conditional/ignored — passing a flag always adds that endpoint. That's what makes the flag model safe (vs. the mutually-exclusive-modes-as-flags that caused "args in arbitrary places, silently ignored").--rtmp-listenunderimportaccepts pushes only and rejects plays; underexportit serves plays only. The operator declares direction; the connecting peer can't choose. (Library follow-up:moq_rtmp::run/moq_srt::runare bidirectional today — the directional server just refuses the wrong request type.)import/exportdon't share an endpoint set.--stdin/--hls-connectare import-only (sources);--stdout/--hls-listenare export-only (sinks); only the foreign--rtmp/srt/rtc-connect/listenappear under both. Implement asenum Direction { Import(ImportEndpoints), Export(ExportEndpoints) }with a shared flattenedForeignEndpointssub-struct for the rtmp/srt/rtc flags.--client/--serverfor MoQ (so the TLS sub-flags--client-tls-*/--server-tls-*/--tls-*from moq-native line up); protocol-prefixed--<proto>-connect/--<proto>-listenfor everything else. No collisions.help_headinggrouping per protocol so the (necessarily larger)--helpstays legible.--clientURL path. For animportlistener the name comes from the protocol (RTMP app/key, SRT streamid); for--serverhosting, TBD (see open questions).Library APIs to wire
moq-clipublish.rs(PublishFormat) /subscribe.rs(SubscribeFormat, catalog).moq_hls::import/moq_hls::Server(export router).moq_rtmp::run(listen, make directional) /moq_rtmp::Client::{publish,pull}(connect, from moq-rtmp/moq-srt: add client (dial-out) role #1982).moq_srt::run(listen, make directional) /moq_srt::dial::{publish,pull}(connect, from moq-rtmp/moq-srt: add client (dial-out) role #1982).moq_rtc::{Server, Client}(routers / dial).moq_native::Client/Serverreconnect/accept, sharedOrigin.rtmp://host[:1935]/<app>/<key>,srt://host:port?streamid=…&latency=…, mirrorresolve_pathinmoq-rtmp/src/listen.rs.The ripple (don't forget)
This is a breaking change to every documented moq-cli invocation. Update:
doc/bin/cli.md— full rewrite.demo/justfiles (just pub, etc.) and configs.test/smoke/— the interop matrix usesmoq-cli publish/subscribe.doc/(grepmoq-cli publish|subscribe|serve|accept|hls).Suggested approach
main(after moq-rtmp/moq-srt: add client (dial-out) role #1982 lands) so the dial-outClient/dialare available.Direction+Import/Export+ForeignEndpoints,help_heading) and get--helpreviewed before wiring runtime.Originruntime: spawn each endpoint as a task feeding/draining the Origin; MoQ side is the reconnect/accept.doc/bin/cli.md.just check.Open questions
--server(MoQ host) mode: how is the broadcast named for--stdin/dial endpoints when there's no--clientURL path? (A--broadcastfallback flag, or require the URL path form only with--client?)--clientand--serverbe combinable (a "relay node" that hosts and forwards)? The shared-Origin model allows it; decide whether to expose it now.--stdin/--stdout: reuse the existingPublishFormat/SubscribeFormatenums as the flag value.(Filed by Claude Opus 4.8 — captures a long design discussion; the grammar and decisions above are settled, the open questions are for the build.)