diff --git a/scripts/generate-app-catalog.sh b/scripts/generate-app-catalog.sh index 265691f6..1a75eb74 100755 --- a/scripts/generate-app-catalog.sh +++ b/scripts/generate-app-catalog.sh @@ -143,6 +143,39 @@ for app_id, comps in STACK.items(): # into an AppManifest. Apps not already in SINGLE/STACK get a new entry whose # version comes from the manifest. A bad embed is harmless: the node validates and # falls back to its disk manifest. +# Embedded manifests must name a registry the DEPLOYED fleet trusts, which is +# not necessarily the one the repo names. apps/*/manifest.yml moved to the public +# domain in 8e814ca0, but releases/registry-trust-floor.json still lists only the +# OVH host โ€” the migration is ship-binary -> confirm-fleet -> promote-floor -> +# regenerate, and the later steps have not happened. Embedding the repo's host +# verbatim produced a catalog naming 78 untrusted refs, which would have made +# every install in the field fail with "not from a trusted registry". The signer +# refused it, which is how this was caught. +# +# So rewrite OUR registry host to whatever REGISTRY is generating against, and +# leave every other host (docker.io, ghcr.io, ...) untouched. When the floor is +# promoted, generating against the domain becomes a no-op here. +REGISTRY = os.environ.get("ARCHY_REGISTRY", "source.archipelago-foundation.org/lfg2025") + +_KNOWN_ARCHY_REGISTRY_HOSTS = ( + "source.archipelago-foundation.org/lfg2025", + "146.59.87.168:3000/lfg2025", +) + + +def _retarget_registry(node): + if isinstance(node, dict): + return {k: _retarget_registry(v) for k, v in node.items()} + if isinstance(node, list): + return [_retarget_registry(v) for v in node] + if isinstance(node, str): + for host in _KNOWN_ARCHY_REGISTRY_HOSTS: + if host != REGISTRY and node.startswith(host + "/"): + return REGISTRY + node[len(host):] + return node + return node + + embedded = 0 apps_dir = os.environ.get("APPS_DIR") if os.environ.get("EMBED_MANIFESTS") and apps_dir: @@ -159,7 +192,7 @@ if os.environ.get("EMBED_MANIFESTS") and apps_dir: continue entry = apps.setdefault(str(app_id), {}) entry.setdefault("version", str(app.get("version", "")) or "0") - entry["manifest"] = data + entry["manifest"] = _retarget_registry(data) embedded += 1 # Multi-version support (docs/bitcoin-multi-version-design.md ยง3 Phase 1): @@ -172,7 +205,6 @@ if os.environ.get("EMBED_MANIFESTS") and apps_dir: # image.sh (Phase 0) publishes more tagged images, e.g.: # {"version": "30.0", "image": f"{REGISTRY}/bitcoin:30.0"}, # {"version": "27.2", "image": f"{REGISTRY}/bitcoin:27.2", "deprecated": True, "eol": "2026-12-31"}, -REGISTRY = os.environ.get("ARCHY_REGISTRY", "source.archipelago-foundation.org/lfg2025") VERSIONS = { # Curated Core set (latest patch per major, current โ†’ 25). Images built + # verified (SHA-256 + OpenPGP, fail-closed) and pushed by diff --git a/scripts/image-versions.sh b/scripts/image-versions.sh index 1a5b4f11..646bc075 100644 --- a/scripts/image-versions.sh +++ b/scripts/image-versions.sh @@ -10,7 +10,16 @@ # to verify against the registry. # Archipelago app registries (primary + fallback) -ARCHY_REGISTRY="source.archipelago-foundation.org/lfg2025" +# +# Honour a caller-supplied ARCHY_REGISTRY instead of overwriting it. Catalog +# generation MUST be able to target a host inside releases/registry-trust-floor.json +# โ€” the hosts binaries already deployed to the fleet are known to trust. This +# file's default is the public domain, which the fleet does NOT trust yet: the +# migration is ship-binary -> confirm-fleet -> promote-in-floor -> regenerate, +# and only step 0 has happened. Generating the catalog against an untrusted host +# makes every install in the field fail with "not from a trusted registry"; the +# signer refuses to sign such a catalog, which is how this was caught. +ARCHY_REGISTRY="${ARCHY_REGISTRY:-source.archipelago-foundation.org/lfg2025}" # No fallback registry: the old tx1138 registry host was retired (2026-06-13); empty disables the fallback path. ARCHY_REGISTRY_FALLBACK=""