Skip to content

Commit bc2f110

Browse files
npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgytaPerci
andcommitted
Add embedded SDK knobs for Sprout relay mesh
Two opt-in seams the Sprout v1 mesh integration needs: - disable_iroh_relays(bool): when true, embedded runtime selects an explicitly disabled relay policy, which uses RelayMode::Disabled, skips public relay URL fallback, skips raw STUN, and avoids the 5s endpoint.online() wait that cannot succeed without a home relay. Default false preserves existing behavior. - EmbeddedNodeHandle::join_token(token): forwards an invite token over the runtime control channel to node.join_with_retry so an already-running embedded node can dial a new EndpointAddr without restart. Handled in both auto and passive/client runtime loops. Purely additive; existing defaults and startup join_tokens behavior remain unchanged. Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co>
1 parent 304d970 commit bc2f110

7 files changed

Lines changed: 111 additions & 15 deletions

File tree

‎crates/mesh-llm-host-runtime/src/api/state.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ impl Serialize for PublicationState {
4343
}
4444

4545
pub enum RuntimeControlRequest {
46+
Join {
47+
invite_token: String,
48+
resp: tokio::sync::oneshot::Sender<anyhow::Result<()>>,
49+
},
4650
Load {
4751
spec: String,
4852
resp: tokio::sync::oneshot::Sender<anyhow::Result<RuntimeLoadResponse>>,

‎crates/mesh-llm-host-runtime/src/cli/mod.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ pub(crate) struct Cli {
536536
#[arg(long = "relay-auth", value_parser = parse_relay_auth_pair, hide = true)]
537537
pub(crate) relay_auth: Vec<(String, String)>,
538538

539+
/// Disable iroh relay registration. Direct addresses and explicit EndpointAddr dials still work.
540+
#[arg(long, hide = true)]
541+
pub(crate) disable_iroh_relays: bool,
542+
539543
/// Bind QUIC to a fixed UDP port (for NAT port forwarding).
540544
#[arg(long, hide = true)]
541545
pub(crate) bind_port: Option<u16>,

‎crates/mesh-llm-host-runtime/src/mesh/mod.rs‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ pub struct RelayConfig<'a> {
399399
pub enum RelayPolicy {
400400
#[default]
401401
DefaultPublic,
402+
ExplicitlyDisabled,
402403
Disabled,
403404
}
404405

@@ -408,7 +409,7 @@ impl RelayPolicy {
408409
}
409410

410411
fn uses_raw_stun(self) -> bool {
411-
matches!(self, Self::DefaultPublic)
412+
matches!(self, Self::DefaultPublic | Self::ExplicitlyDisabled)
412413
}
413414
}
414415

@@ -557,7 +558,7 @@ fn filter_endpoint_addr_for_bind_ip(
557558

558559
fn effective_relay_urls(policy: RelayPolicy, relay_urls: &[String]) -> Vec<String> {
559560
match policy {
560-
RelayPolicy::Disabled => Vec::new(),
561+
RelayPolicy::Disabled | RelayPolicy::ExplicitlyDisabled => Vec::new(),
561562
RelayPolicy::DefaultPublic if relay_urls.is_empty() => vec![
562563
"https://usw1-2.relay.michaelneale.mesh-llm.iroh.link./".into(),
563564
"https://aps1-1.relay.michaelneale.mesh-llm.iroh.link./".into(),
@@ -588,12 +589,15 @@ mod relay_policy_tests {
588589
}
589590

590591
#[test]
591-
fn disabled_policy_uses_no_relays_or_raw_stun() {
592+
fn disabled_policy_uses_no_relays_but_explicit_disable_keeps_raw_stun() {
592593
let custom = vec!["https://relay.example/".to_string()];
593594

594595
assert!(effective_relay_urls(RelayPolicy::Disabled, &custom).is_empty());
596+
assert!(effective_relay_urls(RelayPolicy::ExplicitlyDisabled, &custom).is_empty());
595597
assert!(!RelayPolicy::Disabled.uses_relay());
598+
assert!(!RelayPolicy::ExplicitlyDisabled.uses_relay());
596599
assert!(!RelayPolicy::Disabled.uses_raw_stun());
600+
assert!(RelayPolicy::ExplicitlyDisabled.uses_raw_stun());
597601
}
598602
}
599603

@@ -2032,7 +2036,12 @@ fn relay_mode_for_startup(relay: RelayConfig<'_>) -> iroh::endpoint::RelayMode {
20322036
tracing::info!("Relay: {:?}", urls);
20332037
iroh::endpoint::RelayMode::Custom(relay_map_from_urls(&urls, relay.auths))
20342038
} else {
2035-
tracing::info!("Relay: disabled by LAN-only discovery mode");
2039+
let reason = match relay.policy {
2040+
RelayPolicy::ExplicitlyDisabled => "disabled by embedded config",
2041+
RelayPolicy::Disabled => "disabled by LAN-only discovery mode",
2042+
RelayPolicy::DefaultPublic => unreachable!("default public uses relays"),
2043+
};
2044+
tracing::info!("Relay: {reason}");
20362045
iroh::endpoint::RelayMode::Disabled
20372046
}
20382047
}

‎crates/mesh-llm-host-runtime/src/runtime/mod.rs‎

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ pub(crate) struct EmbeddedRuntimeOptions {
172172
pub(crate) discovery_mode: EmbeddedRuntimeDiscoveryMode,
173173
pub(crate) relay: Vec<String>,
174174
pub(crate) relay_auth: Vec<(String, String)>,
175+
pub(crate) disable_iroh_relays: bool,
175176
pub(crate) nostr_relay: Vec<String>,
176177
pub(crate) region: Option<String>,
177178
pub(crate) node_name: Option<String>,
@@ -3511,6 +3512,7 @@ fn cli_from_embedded_options(options: EmbeddedRuntimeOptions) -> Cli {
35113512
};
35123513
cli.relay = options.relay;
35133514
cli.relay_auth = options.relay_auth;
3515+
cli.disable_iroh_relays = options.disable_iroh_relays;
35143516
cli.nostr_relay = options.nostr_relay;
35153517
cli.region = options.region;
35163518
cli.name = options.node_name;
@@ -5646,7 +5648,7 @@ pub(crate) async fn run_plugin_mcp(cli: &Cli) -> Result<()> {
56465648
mesh::RelayConfig {
56475649
urls: &cli.relay,
56485650
auths: &relay_auths,
5649-
policy: relay_policy_for_mesh_discovery_mode(cli.mesh_discovery_mode),
5651+
policy: relay_policy_for_runtime(cli),
56505652
},
56515653
mesh::QuicBindSelection {
56525654
ip: cli.bind_ip,
@@ -5884,7 +5886,7 @@ async fn start_run_auto_node_and_plugins(
58845886
mesh::RelayConfig {
58855887
urls: &cli.relay,
58865888
auths: &relay_auths,
5887-
policy: relay_policy_for_mesh_discovery_mode(cli.mesh_discovery_mode),
5889+
policy: relay_policy_for_runtime(cli),
58885890
},
58895891
mesh::QuicBindSelection {
58905892
ip: cli.bind_ip,
@@ -5915,6 +5917,14 @@ async fn start_run_auto_node_and_plugins(
59155917
Ok((node, channels, plugin_manager))
59165918
}
59175919

5920+
fn relay_policy_for_runtime(cli: &Cli) -> mesh::RelayPolicy {
5921+
if cli.disable_iroh_relays {
5922+
mesh::RelayPolicy::ExplicitlyDisabled
5923+
} else {
5924+
relay_policy_for_mesh_discovery_mode(cli.mesh_discovery_mode)
5925+
}
5926+
}
5927+
59185928
fn relay_policy_for_mesh_discovery_mode(
59195929
mode: mesh_discovery::MeshDiscoveryMode,
59205930
) -> mesh::RelayPolicy {
@@ -7367,6 +7377,11 @@ async fn run_auto_handle_control_request(
73677377
cmd: api::RuntimeControlRequest,
73687378
) -> bool {
73697379
match cmd {
7380+
api::RuntimeControlRequest::Join { invite_token, resp } => {
7381+
let result = ctx.node.join_with_retry(&invite_token).await;
7382+
let _ = resp.send(result);
7383+
false
7384+
}
73707385
api::RuntimeControlRequest::Load { spec, resp } => {
73717386
let result = run_auto_load_runtime_model(ctx, spec).await;
73727387
let _ = resp.send(result);
@@ -8410,15 +8425,22 @@ async fn run_passive_listener_loop(
84108425
return Ok(Some(model_name));
84118426
}
84128427
Some(cmd) = control_rx.recv() => {
8413-
if let api::RuntimeControlRequest::Shutdown { source } = cmd {
8414-
shutdown_passive_runtime(
8415-
&node,
8416-
&plugin_manager,
8417-
&mut console_server_handle,
8418-
source,
8419-
)
8420-
.await;
8421-
return Ok(None);
8428+
match cmd {
8429+
api::RuntimeControlRequest::Shutdown { source } => {
8430+
shutdown_passive_runtime(
8431+
&node,
8432+
&plugin_manager,
8433+
&mut console_server_handle,
8434+
source,
8435+
)
8436+
.await;
8437+
return Ok(None);
8438+
}
8439+
api::RuntimeControlRequest::Join { invite_token, resp } => {
8440+
let result = node.join_with_retry(&invite_token).await;
8441+
let _ = resp.send(result);
8442+
}
8443+
_ => {}
84228444
}
84238445
}
84248446
signal = wait_shutdown_signal() => {
@@ -8928,6 +8950,19 @@ mod tests {
89288950
);
89298951
}
89308952

8953+
#[test]
8954+
fn explicit_disable_iroh_relays_overrides_nostr_relay_policy() {
8955+
let mut cli = Cli::parse_from(["mesh-llm"]);
8956+
cli.mesh_discovery_mode = mesh_discovery::MeshDiscoveryMode::Nostr;
8957+
cli.disable_iroh_relays = true;
8958+
8959+
assert_eq!(
8960+
relay_policy_for_runtime(&cli),
8961+
mesh::RelayPolicy::ExplicitlyDisabled
8962+
);
8963+
assert!(!relay_policy_for_runtime(&cli).uses_relay());
8964+
}
8965+
89318966
#[test]
89328967
fn mdns_discovery_does_not_start_relay_health_monitor() {
89338968
assert!(!should_start_relay_health_monitor(

‎crates/mesh-llm-host-runtime/src/sdk.rs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,22 @@ impl EmbeddedServeHandle {
8383
})
8484
}
8585

86+
pub async fn join_token(&self, invite_token: impl Into<String>) -> Result<()> {
87+
let control_tx = self
88+
.control_tx
89+
.as_ref()
90+
.context("embedded mesh runtime control channel is unavailable")?;
91+
let (resp, rx) = tokio::sync::oneshot::channel();
92+
control_tx
93+
.send(crate::api::RuntimeControlRequest::Join {
94+
invite_token: invite_token.into(),
95+
resp,
96+
})
97+
.map_err(|_| anyhow::anyhow!("embedded mesh runtime control channel is closed"))?;
98+
rx.await
99+
.context("embedded mesh runtime join response dropped")?
100+
}
101+
86102
pub async fn stop(mut self) -> Result<()> {
87103
if !self.request_shutdown("sdk") && !self.task_finished() {
88104
anyhow::bail!("embedded mesh runtime control channel is unavailable");
@@ -200,6 +216,7 @@ fn embedded_runtime_options(
200216
EmbeddedMeshDiscoveryMode::Mdns => crate::runtime::EmbeddedRuntimeDiscoveryMode::Mdns,
201217
},
202218
relay: config.network.iroh_relays.clone(),
219+
disable_iroh_relays: config.network.disable_iroh_relays,
203220
relay_auth: config
204221
.network
205222
.iroh_relay_auth
@@ -711,6 +728,7 @@ mod tests {
711728
.max_vram_gb(3.0)
712729
.iroh_relay("https://relay.example")
713730
.iroh_relay_auth("https://relay.example", "token")
731+
.disable_iroh_relays(true)
714732
.nostr_relay("wss://nostr.example")
715733
.bind_port(17777)
716734
.owner_key("/tmp/sprout-owner.json")
@@ -742,6 +760,7 @@ mod tests {
742760
options.relay_auth,
743761
vec![("https://relay.example".to_string(), "token".to_string())]
744762
);
763+
assert!(options.disable_iroh_relays);
745764
assert_eq!(options.nostr_relay, vec!["wss://nostr.example".to_string()]);
746765
assert_eq!(options.bind_port, Some(17777));
747766
}

‎crates/mesh-llm-host-runtime/src/sdk/embedded_config.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub struct EmbeddedMeshNetworkConfig {
9191
pub node_name: Option<String>,
9292
pub iroh_relays: Vec<String>,
9393
pub iroh_relay_auth: BTreeMap<String, String>,
94+
pub disable_iroh_relays: bool,
9495
pub nostr_relays: Vec<String>,
9596
pub bind_ip: Option<IpAddr>,
9697
pub bind_port: Option<u16>,
@@ -110,6 +111,7 @@ impl Default for EmbeddedMeshNetworkConfig {
110111
node_name: None,
111112
iroh_relays: Vec::new(),
112113
iroh_relay_auth: BTreeMap::new(),
114+
disable_iroh_relays: false,
113115
nostr_relays: Vec::new(),
114116
bind_ip: None,
115117
bind_port: None,
@@ -312,6 +314,11 @@ impl EmbeddedMeshNodeBuilder {
312314
self
313315
}
314316

317+
pub fn disable_iroh_relays(mut self, disabled: bool) -> Self {
318+
self.config.network.disable_iroh_relays = disabled;
319+
self
320+
}
321+
315322
pub fn nostr_relay(mut self, url: impl Into<String>) -> Self {
316323
self.config.network.nostr_relays.push(url.into());
317324
self
@@ -485,6 +492,7 @@ pub struct EmbeddedServeConfig {
485492
pub discovery_mode: EmbeddedMeshDiscoveryMode,
486493
pub relay: Vec<String>,
487494
pub relay_auth: BTreeMap<String, String>,
495+
pub disable_iroh_relays: bool,
488496
pub nostr_relay: Vec<String>,
489497
pub region: Option<String>,
490498
pub node_name: Option<String>,
@@ -515,6 +523,7 @@ impl Default for EmbeddedServeConfig {
515523
discovery_mode: EmbeddedMeshDiscoveryMode::Nostr,
516524
relay: Vec::new(),
517525
relay_auth: BTreeMap::new(),
526+
disable_iroh_relays: false,
518527
nostr_relay: Vec::new(),
519528
region: None,
520529
node_name: None,
@@ -555,6 +564,7 @@ impl From<EmbeddedServeConfig> for EmbeddedMeshNodeConfig {
555564
node_name: config.node_name,
556565
iroh_relays: config.relay,
557566
iroh_relay_auth: config.relay_auth,
567+
disable_iroh_relays: config.disable_iroh_relays,
558568
nostr_relays: config.nostr_relay,
559569
bind_ip: config.bind_ip,
560570
bind_port: config.bind_port,
@@ -587,6 +597,7 @@ impl From<EmbeddedMeshNodeConfig> for EmbeddedServeConfig {
587597
discovery_mode: config.network.discovery_mode,
588598
relay: config.network.iroh_relays,
589599
relay_auth: config.network.iroh_relay_auth,
600+
disable_iroh_relays: config.network.disable_iroh_relays,
590601
nostr_relay: config.network.nostr_relays,
591602
region: config.network.region,
592603
node_name: config.network.node_name,

‎crates/mesh-llm-sdk/src/lib.rs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub struct NetworkConfig {
7373
pub node_name: Option<String>,
7474
pub iroh_relays: Vec<String>,
7575
pub iroh_relay_auth: BTreeMap<String, String>,
76+
pub disable_iroh_relays: bool,
7677
pub nostr_relays: Vec<String>,
7778
pub bind_ip: Option<IpAddr>,
7879
pub bind_port: Option<u16>,
@@ -92,6 +93,7 @@ impl Default for NetworkConfig {
9293
node_name: None,
9394
iroh_relays: Vec::new(),
9495
iroh_relay_auth: BTreeMap::new(),
96+
disable_iroh_relays: false,
9597
nostr_relays: Vec::new(),
9698
bind_ip: None,
9799
bind_port: None,
@@ -230,6 +232,11 @@ macro_rules! impl_common_builder_methods {
230232
self
231233
}
232234

235+
pub fn disable_iroh_relays(mut self, disabled: bool) -> Self {
236+
self.config.network.disable_iroh_relays = disabled;
237+
self
238+
}
239+
233240
pub fn nostr_relay(mut self, url: impl Into<String>) -> Self {
234241
self.config.network.nostr_relays.push(url.into());
235242
self
@@ -409,6 +416,10 @@ impl EmbeddedNodeHandle {
409416
self.inner.status().await.map(EmbeddedNodeStatus::from)
410417
}
411418

419+
pub async fn join_token(&self, token: impl Into<String>) -> Result<()> {
420+
self.inner.join_token(token).await
421+
}
422+
412423
pub async fn stop(self) -> Result<()> {
413424
self.inner.stop().await
414425
}
@@ -640,6 +651,7 @@ fn host_config(parts: EmbeddedNodeParts) -> mesh_llm_host_runtime::sdk::Embedded
640651
node_name: parts.network.node_name,
641652
iroh_relays: parts.network.iroh_relays,
642653
iroh_relay_auth: parts.network.iroh_relay_auth,
654+
disable_iroh_relays: parts.network.disable_iroh_relays,
643655
nostr_relays: parts.network.nostr_relays,
644656
bind_ip: parts.network.bind_ip,
645657
bind_port: parts.network.bind_port,
@@ -700,6 +712,7 @@ mod tests {
700712
.api_port(19337)
701713
.console_port(13131)
702714
.discovery_mode(MeshDiscoveryMode::Mdns)
715+
.disable_iroh_relays(true)
703716
.log_format(LogFormat::Pretty)
704717
.build();
705718

@@ -708,6 +721,7 @@ mod tests {
708721
assert_eq!(config.http.api_port, 19337);
709722
assert_eq!(config.http.console_port, 13131);
710723
assert_eq!(config.network.discovery_mode, MeshDiscoveryMode::Mdns);
724+
assert!(config.network.disable_iroh_relays);
711725
assert_eq!(config.log_format, LogFormat::Pretty);
712726
}
713727

0 commit comments

Comments
 (0)