diff --git a/core/archipelago/src/main.rs b/core/archipelago/src/main.rs index 4e2d093d..0b5d71e7 100644 --- a/core/archipelago/src/main.rs +++ b/core/archipelago/src/main.rs @@ -198,6 +198,24 @@ async fn main() -> Result<()> { (Some(trait_obj), Some(dev)) } else { let prod = Arc::new(ProdContainerOrchestrator::new(config.clone()).await?); + // Pull the freshest signed app-catalog BEFORE loading manifests, so any + // registry-embedded manifest (the origin-wins overlay in load_manifests) + // is in place on THIS boot — not a restart later. Without this the boot + // would overlay the previous run's cached catalog and a newly-published + // app (e.g. a registry-only install) wouldn't appear until the next + // restart. Bounded + best-effort: on timeout/unreachable origin the + // last-cached catalog (or the disk manifests) still load — registry is + // an overlay on top of disk, never a hard dependency. + match tokio::time::timeout( + std::time::Duration::from_secs(25), + crate::container::app_catalog::refresh_catalog(&config.data_dir), + ) + .await + { + Ok(Ok(n)) => info!("🛰️ app-catalog refreshed before manifest load ({n} apps)"), + Ok(Err(e)) => tracing::debug!("app-catalog pre-load refresh failed (using cache): {e}"), + Err(_) => tracing::debug!("app-catalog pre-load refresh timed out (using cache)"), + } // Best-effort manifest load; a missing /opt/archipelago/apps is // logged inside load_manifests and not fatal. match prod.load_manifests().await { diff --git a/scripts/generate-app-catalog.sh b/scripts/generate-app-catalog.sh index 61ac1796..fa3cf181 100755 --- a/scripts/generate-app-catalog.sh +++ b/scripts/generate-app-catalog.sh @@ -14,16 +14,16 @@ # # Usage: # scripts/generate-app-catalog.sh [output-path] -# EMBED_MANIFESTS=1 scripts/generate-app-catalog.sh # also embed full manifests +# EMBED_MANIFESTS=0 scripts/generate-app-catalog.sh # version/image only (legacy) # # then publish: push releases/app-catalog.json to the OVH gitea (raw URL). # -# EMBED_MANIFESTS (opt-in, default off): also embed each app's full -# apps//manifest.yml into its catalog entry's `manifest` field, so nodes can +# EMBED_MANIFESTS (default ON, 2026-06-23): embed each app's full +# apps//manifest.yml into its catalog entry's `manifest` field, so nodes # install from the signed registry alone (no OTA-shipped disk manifest). Consumed # by container::app_catalog + the orchestrator's load_manifests overlay -# (origin-wins, disk = fallback). See docs/registry-manifest-design.md. Kept -# opt-in during the migration window so a routine catalog regen never changes -# what phase-1 nodes install until we deliberately turn it on. +# (origin-wins, disk = fallback). See docs/registry-manifest-design.md. The +# migration window is over — every regen now embeds; set EMBED_MANIFESTS=0 only +# to reproduce the old version/image-only catalog. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -36,7 +36,7 @@ source "$ROOT/scripts/image-versions.sh" set +a UPDATED="$(date -u +%Y-%m-%d)" OUT="$OUT" APPS_DIR="$ROOT/apps" \ -EMBED_MANIFESTS="${EMBED_MANIFESTS:-}" python3 - <<'PY' +EMBED_MANIFESTS="${EMBED_MANIFESTS:-1}" python3 - <<'PY' import glob import json, os