Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/setup/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ These land with the next breaking release, not the 2026-09-23 train.
`SourceMap` (#4667) are gone, along with the importers' `live()`. Publish the
source's own timestamps and let the catalog clock map them to wall time;
pin that mapping with `Config::with_clock` when the source's zero is known.
- **`--cluster-mesh` and `--cluster-linger` are unknown flags.** moq-relay
0.17 refuses them by name; later relays reject them, and TOML `mesh` and
`linger`, like any unknown setting. `MOQ_CLUSTER_MESH` and
`MOQ_CLUSTER_LINGER` are no longer read, so drop them from the environment.
In Rust, `cluster::Config` has no `mesh` or `linger` field.
- **moq-mux data producers take a broadcast-clock `Timestamp`.** `json` and
`binary` `Snapshot::update` and `Stream::append` take `Timed<_, Timestamp>`
instead of `Timed<_, Instant>`, and publish it as given. Convert a capture
Expand Down
1 change: 0 additions & 1 deletion quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ blocks. The quests that gated m0 lines moved under them.
- [Hard fork](/quest/m1/quic/fork/README.md) - quinn hard-forked in-tree as `moq-quic`, ranked ahead of transport-seam and perf; the rest of the QUIC line follows it
- [Cluster routing](/quest/m1/cluster-routing/README.md) - any node routes toward a broadcast's origin over CDN and P2P links alike, with per-origin routes and path-less announces
- [One route cost](/quest/m1/route-cost.md) - Warm and Cold collapse to one static route cost
- [Delete removed cluster flags](/quest/m1/cluster-shims.md) - `mesh` and `linger` leave `cluster::Config` and their flags become unknown
- [Track tail interop](/quest/m1/track-tail-interop.md) - a Rust publisher ending a track with a group in flight is read to its end by the JS subscriber, and the reverse, in `just test interop`
- [Request linger](/quest/m1/request-linger.md) - an upstream FETCH or SUBSCRIBE outlives its last reader by a short linger, so quick re-requests don't churn upstream
- [Flat questlines](/quest/m1/quest-flat-lines.md) - moq pins the current quest CLI, lands its questline branches on main, and retires them
Expand Down
17 changes: 0 additions & 17 deletions quest/m1/cluster-shims.md

This file was deleted.

6 changes: 0 additions & 6 deletions rs/moq-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1702,12 +1702,6 @@ mod tests {
assert!(err.contains(reported), "{err}");
}

// Gossip discovery is removed, so its flag is refused for every verb.
let Err(err) = Invocation::try_parse_from(["moq", "--cluster-mesh", "auth", "generate"]) else {
panic!("--cluster-mesh must be refused");
};
assert!(err.to_string().contains("--cluster-mesh"), "{err}");

#[cfg(unix)]
{
for (flag, value, reported) in [
Expand Down
2 changes: 1 addition & 1 deletion rs/moq-relay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Relays can be joined together to proxy announcements and subscriptions. A viewer
- `--cluster-connect-api <url-or-path>` fetches that list from an endpoint or file instead, reloading it without a restart.
- `--cluster-lan` finds peers on the local network over mDNS.

A relay only dials peers listed this way or found on the LAN. `--cluster-mesh` gossip discovery was removed; the relay errors at startup if it is set.
A relay only dials peers listed this way or found on the LAN.

See [doc/bin/relay/cluster.md](https://github.com/moq-dev/moq/blob/main/doc/bin/relay/cluster.md) for the full walkthrough, including topology trade-offs and authentication.

Expand Down
6 changes: 4 additions & 2 deletions rs/moq-relay/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,10 @@ mod tests {
let before = spawned();

// `--cluster-id 0` is rejected, so the cache is dropped before it is attached.
let mut cluster = crate::cluster::Config::default();
cluster.id = Some(0);
let cluster = crate::cluster::Config {
id: Some(0),
..Default::default()
};
assert!(attach(&governed(), cluster).is_err(), "cluster id 0 is rejected");

settle().await;
Expand Down
117 changes: 2 additions & 115 deletions rs/moq-relay/src/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,23 +609,6 @@ pub struct Config {
)]
pub node: Option<String>,

/// Released spelling of the removed gossip discovery, kept so
/// [`Self::deprecated`] can refuse it. Any value, boolean or the older URL
/// form, is refused.
#[doc(hidden)]
#[usage(
name = "cluster-mesh",
long = "cluster-mesh",
env = "MOQ_CLUSTER_MESH",
setting = "cluster.mesh",
default_missing = "true",
num_args = 0..=1,
require_equals = true,
hide = true,
)]
#[serde(default, deserialize_with = "deserialize_bool_or_string")]
pub mesh: Option<String>,

/// LAN discovery over mDNS (`[cluster.lan]`).
#[cfg(feature = "cluster-lan")]
#[usage(flatten)]
Expand Down Expand Up @@ -654,43 +637,12 @@ pub struct Config {
setting = "cluster.tier"
)]
pub tier: Option<String>,
/// Released spelling, kept so [`Self::deprecated`] can name that linger is gone.
#[doc(hidden)]
#[usage(skip)]
#[serde(with = "crate::duration::serde_option")]
pub linger: Option<std::time::Duration>,

#[usage(
name = "cluster-linger",
long = "cluster-linger",
env = "MOQ_CLUSTER_LINGER",
setting = "cluster.linger",
hide = true
)]
#[serde(default, rename = "__cli_linger", skip_serializing_if = "Option::is_none")]
linger_arg: Option<crate::duration::Duration>,
}

impl Config {
/// Released spellings this config was parsed from, each paired with what replaced it.
pub fn deprecated(&self) -> moq_tokio::cli::Deprecated {
let mut found = moq_tokio::cli::Deprecated::default();
if self.linger.is_some() || self.linger_arg.is_some() {
found.changed(
"--cluster-linger",
Some("MOQ_CLUSTER_LINGER"),
"(removed)",
"a broadcast closes as soon as its last publisher is lost",
);
}
if self.mesh.is_some() {
found.changed(
"--cluster-mesh",
Some("MOQ_CLUSTER_MESH"),
"--cluster-connect or --cluster-connect-api",
"gossip discovery is removed; list every peer this relay dials",
);
}
if self.connect.iter().any(|peer| is_legacy_peer(peer.url())) {
found.changed(
"--cluster-connect",
Expand Down Expand Up @@ -1966,31 +1918,6 @@ pub(crate) fn canonicalize_peer_key(peer: &str) -> String {
}
}

/// Deserialize a field that accepts either a TOML boolean or string into an
/// `Option<String>` (booleans become `"true"` / `"false"`). Lets the removed
/// `cluster.mesh` parse in both its released forms, `mesh = true` and
/// `mesh = "<url>"`, so it is refused by name rather than as an unknown type.
fn deserialize_bool_or_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize as _;

#[derive(serde::Deserialize)]
#[serde(untagged)]
enum BoolOrString {
Bool(bool),
Str(String),
}

Ok(
Option::<BoolOrString>::deserialize(deserializer)?.map(|value| match value {
BoolOrString::Bool(value) => value.to_string(),
BoolOrString::Str(value) => value,
}),
)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -2587,24 +2514,6 @@ mod tests {
task.abort();
}

/// Gossip discovery is removed, so every released `--cluster-mesh` form, the
/// boolean and the older self-URL, stops construction and names the
/// replacement instead of leaving a relay without the peers it expected.
#[tokio::test]
async fn removed_mesh_is_refused() {
for value in ["true", "false", "rendezvous.example.com:4443"] {
let err = new_cluster(Config {
mesh: Some(value.to_string()),
..Default::default()
})
.err()
.expect("mesh must be refused");
let msg = format!("{err}");
assert!(msg.contains("--cluster-mesh / MOQ_CLUSTER_MESH"), "{value}: {msg}");
assert!(msg.contains("--cluster-connect"), "{value}: {msg}");
}
}

/// A valid `cluster.id` is used verbatim as the relay's Hop ID, giving the
/// node a stable identity across restarts.
#[tokio::test]
Expand Down Expand Up @@ -2710,26 +2619,6 @@ mod tests {
);
}

/// A TOML `mesh` in either released type is refused by name at load, so a
/// config file that relied on gossip stops instead of starting without peers.
#[test]
fn toml_mesh_is_refused() {
// Usage reads the environment while parsing, so serialize with the tests
// that mutate it.
let _env = crate::test_env::EnvGuard::lock();

let dir = std::env::temp_dir().join("moq-relay-cluster-test");
std::fs::create_dir_all(&dir).unwrap();
for (name, value) in [("bool", "true"), ("url", "\"us-east.example.com:4443\"")] {
let path = dir.join(format!("cluster-mesh-{name}-toml.toml"));
std::fs::write(&path, format!("[cluster]\nmesh = {value}\n")).unwrap();

let args = vec![std::ffi::OsString::from("moq-relay"), std::ffi::OsString::from(&path)];
let err = RelayConfig::parse_and_merge(args).expect_err("mesh must be refused");
assert!(err.to_string().contains("--cluster-mesh"), "{name}: {err}");
}
}

/// Static config accepts the object form beside bare URLs, through the same
/// type the connect API parses.
#[test]
Expand Down Expand Up @@ -2783,17 +2672,15 @@ mod tests {
assert!(!is_legacy_peer("https://cdn.example.com/?jwt=abc"));
}

/// Linger and a bare `--cluster-connect` host still parse so the process can
/// name what replaced them, but they configure nothing and must stop a run.
/// A bare `--cluster-connect` host still parses so the process can name what
/// replaced it, but it configures nothing and must stop a run.
#[test]
fn released_cluster_spellings_are_reported_not_applied() {
let config = Config {
linger: Some(std::time::Duration::from_secs(5)),
connect: vec![Peer::new("root.example.com:4443")],
..Default::default()
};
let reported = config.deprecated().to_string();
assert!(reported.contains("--cluster-linger / MOQ_CLUSTER_LINGER"), "{reported}");
assert!(
reported.contains("a full URL like https://host/?jwt=TOKEN"),
"{reported}"
Expand Down
19 changes: 0 additions & 19 deletions rs/moq-relay/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,25 +704,6 @@ duration = "30s"
toml::to_string(&unset).expect("serialize None");
}

/// A released TOML value still survives the merge so the refusal can name it.
#[test]
fn cli_does_not_clobber_toml_linger() {
let _env = EnvGuard::clear(&["MOQ_CLUSTER_LINGER"]);

let toml = r#"
[cluster]
linger = "30s"
"#;
let dir = std::env::temp_dir().join("moq-relay-config-test");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("linger-toml-wins.toml");
std::fs::write(&path, toml).unwrap();

let args = vec![std::ffi::OsString::from("moq-relay"), std::ffi::OsString::from(&path)];
let err = Config::parse_and_merge(args).expect_err("must refuse").to_string();
assert!(err.contains("--cluster-linger"), "{err}");
}

/// Preferred addresses loaded from TOML survive when the CLI omits them.
#[test]
fn cli_does_not_clobber_toml_preferred_addresses() {
Expand Down
6 changes: 0 additions & 6 deletions rs/moq-relay/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,12 @@ struct Cluster {
#[usage(env = "MOQ_CLUSTER_NODE", cli("--cluster-node"))]
node: Option<String>,

#[usage(env = "MOQ_CLUSTER_MESH", cli("--cluster-mesh"))]
mesh: Option<String>,

#[usage(env = "MOQ_CLUSTER_TOKEN", cli("--cluster-token"))]
token: Option<String>,

#[usage(env = "MOQ_CLUSTER_TIER", cli("--cluster-tier"))]
tier: Option<String>,

#[usage(env = "MOQ_CLUSTER_LINGER", cli("--cluster-linger"))]
linger: Option<String>,

#[cfg(feature = "cluster-lan")]
#[usage(flatten)]
lan: ClusterLan,
Expand Down
2 changes: 0 additions & 2 deletions rs/moq-relay/tests/released_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ const RELEASED: &[(&str, Option<&str>)] = &[
("cluster-connect", Some("MOQ_CLUSTER_CONNECT")),
("cluster-connect-api", Some("MOQ_CLUSTER_CONNECT_API")),
("cluster-id", Some("MOQ_CLUSTER_ID")),
("cluster-linger", Some("MOQ_CLUSTER_LINGER")),
("cluster-mesh", Some("MOQ_CLUSTER_MESH")),
("cluster-node", Some("MOQ_CLUSTER_NODE")),
("cluster-tier", Some("MOQ_CLUSTER_TIER")),
("cluster-token", Some("MOQ_CLUSTER_TOKEN")),
Expand Down
Loading