diff --git a/core/archipelago/src/appgate/identity.rs b/core/archipelago/src/appgate/identity.rs index 8bfc0142..240e160d 100644 --- a/core/archipelago/src/appgate/identity.rs +++ b/core/archipelago/src/appgate/identity.rs @@ -147,11 +147,18 @@ pub fn build_port_map() -> PortMap { let mut seen_apps: std::collections::HashSet = std::collections::HashSet::new(); for (app_id, value) in crate::container::app_catalog::catalog_manifest_values() { + // Ports-only overlay: unlike the install path, classification also + // accepts BUILD-SOURCE manifests. The on-node-built companion UIs + // are exactly the apps whose gate policy (session_passthrough, + // auth: gated) must arrive reliably, and their disk manifests + // proved stale or absent fleet-wide in the v1.7.125 rollout. The + // gate's binds fail safely on conflict with a differently-published + // container, so a fresher catalog can only tighten, never expose. let Some(manifest) = - crate::container::app_catalog::catalog_manifest_overlay(&app_id, value) + crate::container::app_catalog::catalog_manifest_ports_overlay(&app_id, value) else { - // Unparseable/invalid/build-source → the orchestrator falls back - // to disk for this app, so classification must too. + // Unparseable/invalid → the orchestrator falls back to disk for + // this app, so classification must too. continue; }; if seen_apps.insert(app_id) { diff --git a/core/archipelago/src/container/app_catalog.rs b/core/archipelago/src/container/app_catalog.rs index 63b46b30..94c02e93 100644 --- a/core/archipelago/src/container/app_catalog.rs +++ b/core/archipelago/src/container/app_catalog.rs @@ -256,6 +256,33 @@ pub fn catalog_manifest_overlay( Some(m) } +/// Like [`catalog_manifest_overlay`] but WITHOUT the build-source refusal — +/// for PORT CLASSIFICATION only, never for install/orchestration. +/// +/// The on-node-built companion UIs (lnd-ui, bitcoin-ui, electrs-ui, fips-ui) +/// are exactly the apps whose port policy (auth/bind/session_passthrough) +/// must reach the gate reliably, yet their build sources made the overlay +/// defer to DISK manifests — whose only delivery paths (frontend runtime +/// payload, per-node repo copies) proved stale or absent across the fleet in +/// the v1.7.125 rollout: nodes served ungated UIs or 401-dead panels until +/// hand-fixed. The signed catalog is fresher and operator-signed; and the +/// gate's address binds fail safely on conflict with a container that +/// publishes differently (logged as CANNOT PROTECT), so classifying from the +/// catalog cannot open anything the running container hasn't already opened. +pub fn catalog_manifest_ports_overlay( + app_id: &str, + value: serde_json::Value, +) -> Option { + let m: archipelago_container::manifest::AppManifest = serde_json::from_value(value).ok()?; + if m.app.id != app_id { + return None; + } + if m.validate().is_err() { + return None; + } + Some(m) +} + /// The catalog's default/latest version string for an app (the top-level /// `version` field), if covered. Used to decide whether an install-time /// selection should pin (older) or track-latest (default).