fix(appgate): classify ports from the catalog even for on-node-built apps

The port map deferred to DISK manifests for any app with a build source
— which is exactly the four companion UIs (lnd-ui, bitcoin-ui,
electrs-ui, fips-ui). Their disk manifests reach nodes only via the
frontend runtime payload or a per-node repo checkout, and in the
v1.7.125 rollout both proved stale or entirely absent: one node had no
checkout at all, others restored an older payload over apps/ at every
boot. Result: session_passthrough never reached the gate, so the node's
own screens 401'd on every data call, and on nodes whose UI rebuilt
from a stale context the app held its port UNGATED.

Classification now uses a ports-only overlay that accepts build-source
manifests (install/orchestration still defers to disk — unchanged). The
signed catalog is the freshest, operator-signed source, and the gate's
address binds fail safely against a container publishing differently
(logged CANNOT PROTECT), so this can only tighten policy, never expose.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-06 09:17:10 -04:00
co-authored by Claude Fable 5
parent 83d7234824
commit 6c0fc366b3
2 changed files with 37 additions and 3 deletions
+10 -3
View File
@@ -147,11 +147,18 @@ pub fn build_port_map() -> PortMap {
let mut seen_apps: std::collections::HashSet<String> = 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) {