diff --git a/clients/linux/src/spawn.rs b/clients/linux/src/spawn.rs index b57ecccd..babf91b0 100644 --- a/clients/linux/src/spawn.rs +++ b/clients/linux/src/spawn.rs @@ -50,6 +50,7 @@ fn plan_for(req: &ConnectRequest, fp_hex: &str, tofu: bool, opts: &SpawnOpts) -> fp_hex: Some(fp_hex.to_string()), mac: req.mac.clone(), id: None, + mgmt_port: None, // this shell resolves the library port itself (`mgmt_port_for`) }, req.launch.as_ref().map(|(id, _)| id.clone()), // A plain card click carries no one-off: the resolver honors the host's own binding diff --git a/clients/windows/src/discovery.rs b/clients/windows/src/discovery.rs index afce6dd5..94dd0afe 100644 --- a/clients/windows/src/discovery.rs +++ b/clients/windows/src/discovery.rs @@ -28,6 +28,11 @@ pub struct DiscoveredHost { /// `linux[/][/]`), sanitized — drives the host tile's OS mark and is /// persisted like `mac`. Empty if absent (older host). pub os: String, + /// The management API's port from the mDNS `mgmt` TXT — where the game library is served. + /// Persisted like `mac` (`trust::learn_mgmt_port`), and load-bearing rather than cosmetic: + /// a host moved off 47990 loses its library once mDNS is gone unless we write this down. + /// `None` if absent (older host) — resolve via `library::DEFAULT_MGMT_PORT`. + pub mgmt_port: Option, } /// Forces the running browse to re-query now — the hosts page's Refresh. Mirrors @@ -124,6 +129,7 @@ pub fn browse() -> (async_channel::Receiver, Rescan) { .filter(|s| !s.is_empty()) .collect(), os: pf_client_core::os::sanitize_os(&val("os")), + mgmt_port: val("mgmt").parse().ok(), }; if tx.send_blocking(host).is_err() { break; // UI gone — stop browsing diff --git a/clients/windows/src/spawn.rs b/clients/windows/src/spawn.rs index ab907c74..2ce34078 100644 --- a/clients/windows/src/spawn.rs +++ b/clients/windows/src/spawn.rs @@ -160,6 +160,7 @@ pub(crate) fn spawn_session( fp_hex: Some(fp_hex.to_string()), mac: Vec::new(), // wake ran before this spawn (initiate_waking) — not the plan's job id: None, + mgmt_port: None, // the library fetch runs in the shell (`Target`), never off a spawn plan }, launch.map(str::to_string), profile.map(str::to_string), diff --git a/clients/windows/src/trust.rs b/clients/windows/src/trust.rs index e16463c9..96976620 100644 --- a/clients/windows/src/trust.rs +++ b/clients/windows/src/trust.rs @@ -8,6 +8,6 @@ //! still load via a serde alias in core. pub use pf_client_core::trust::{ - hex, learn_mac, learn_os, load_or_create_identity, pair_error_message, parse_hex32, KnownHost, - KnownHosts, Settings, + hex, learn_mac, learn_mgmt_port, learn_os, load_or_create_identity, pair_error_message, + parse_hex32, KnownHost, KnownHosts, Settings, }; diff --git a/crates/pf-client-core/src/orchestrate.rs b/crates/pf-client-core/src/orchestrate.rs index 7eb07676..72c49e4b 100644 --- a/crates/pf-client-core/src/orchestrate.rs +++ b/crates/pf-client-core/src/orchestrate.rs @@ -38,6 +38,12 @@ pub struct HostTarget { pub fp_hex: Option, pub mac: Vec, pub id: Option, + /// The host's management-API port (saved store or live advert) — where the library is + /// served, distinct from `port` (the native QUIC plane). Carried on the target for the same + /// reason as `mac`: a front-end holding a plan has no `KnownHost` in hand, and resolving to + /// [`crate::library::DEFAULT_MGMT_PORT`] there is what made a moved mgmt port work on the + /// LAN but not over a VPN. `None` = unknown, fall back to the constant. + pub mgmt_port: Option, } impl From<&KnownHost> for HostTarget { @@ -49,6 +55,7 @@ impl From<&KnownHost> for HostTarget { fp_hex: (!h.fp_hex.is_empty()).then(|| h.fp_hex.clone()), mac: h.mac.clone(), id: h.id.clone(), + mgmt_port: h.mgmt_port, } } }