From 6dcdada371da7a9c1feeb7a8a21f1cd1d90c35c3 Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 16 Jul 2026 07:31:28 -0400 Subject: [PATCH] fix(iso): make baseline apps work on a fresh install with no internet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fresh offline installs came up without fedimint-clientd and filebrowser (they only appeared after connecting internet). Three root causes: - archipelago-load-images.service ran 'podman load' as root, but every container runs rootless as archipelago — bundled images landed in root's storage where the rootless runtime can't see them, so all container creation silently depended on registry pulls. The loader now loads into the archipelago user's storage (with linger + runtime-dir wait + system migrate). - The unbundled ISO bundled only filebrowser.tar; fmcd (fedimint-clientd) is a baseline first-boot app too and is now part of the unbundled core bundle. - first-boot's pull_with_fallback always hit the network; it now uses an already-loaded local image first and skips the pull entirely. Also: fedimint-clientd added to the UI's hardcoded curated-app fallback list (it was missing when all catalog fetches fail offline), plus its INSTALLED_ALIASES entry, and the stale fmcd bundling comment in image-versions.sh corrected. Co-Authored-By: Claude Fable 5 --- .../_archived/build-auto-installer-iso.sh | 58 ++++++++++++++----- neode-ui/src/views/discover/curatedApps.ts | 2 + scripts/first-boot-containers.sh | 7 +++ scripts/image-versions.sh | 7 ++- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/image-recipe/_archived/build-auto-installer-iso.sh b/image-recipe/_archived/build-auto-installer-iso.sh index d3078656..a85e8880 100755 --- a/image-recipe/_archived/build-auto-installer-iso.sh +++ b/image-recipe/_archived/build-auto-installer-iso.sh @@ -1349,21 +1349,29 @@ if [ "$UNBUNDLED" = "1" ]; then # Clean stale images from previous builds (e.g. bundled build tars leaking into unbundled) rm -rf "$IMAGES_DIR" mkdir -p "$IMAGES_DIR" - # FileBrowser is a core dependency (powers the Cloud file manager) — always bundle it - CORE_IMAGE="${FILEBROWSER_IMAGE}" - CORE_FILE="filebrowser.tar" - if [ -f "$IMAGES_DIR/$CORE_FILE" ]; then - echo " ✅ Using cached: $CORE_FILE" - else - echo " Pulling $CORE_IMAGE ($CONTAINER_PLATFORM)..." - if container_pull "$CORE_IMAGE"; then - $CONTAINER_CMD save "$CORE_IMAGE" -o "$IMAGES_DIR/$CORE_FILE" 2>/dev/null && \ - echo " ✅ Saved core: $CORE_FILE ($(du -h "$IMAGES_DIR/$CORE_FILE" | cut -f1))" || \ - echo " ⚠️ Failed to save $CORE_IMAGE" + # Core baseline apps created by first-boot-containers.sh even in + # unbundled mode — their images must ride on the ISO so a fresh install + # works with no internet: FileBrowser (Cloud file manager) and fmcd + # (fedimint-clientd, ecash/sats out of the box). + CORE_BUNDLE=" +${FILEBROWSER_IMAGE} filebrowser.tar +${FMCD_IMAGE} fmcd.tar +" + echo "$CORE_BUNDLE" | while read -r CORE_IMAGE CORE_FILE; do + [ -n "$CORE_IMAGE" ] || continue + if [ -f "$IMAGES_DIR/$CORE_FILE" ]; then + echo " ✅ Using cached: $CORE_FILE" else - echo " ⚠️ Failed to pull $CORE_IMAGE — Cloud will not work until installed" + echo " Pulling $CORE_IMAGE ($CONTAINER_PLATFORM)..." + if container_pull "$CORE_IMAGE"; then + $CONTAINER_CMD save "$CORE_IMAGE" -o "$IMAGES_DIR/$CORE_FILE" 2>/dev/null && \ + echo " ✅ Saved core: $CORE_FILE ($(du -h "$IMAGES_DIR/$CORE_FILE" | cut -f1))" || \ + echo " ⚠️ Failed to save $CORE_IMAGE" + else + echo " ⚠️ Failed to pull $CORE_IMAGE — baseline app won't work offline" + fi fi - fi + done else echo "📦 Step 3b: Bundling container images for offline use..." @@ -1472,6 +1480,13 @@ LOADSERVICE cat > "$WORK_DIR/load-container-images.sh" <<'LOADSCRIPT' #!/bin/bash # Load pre-bundled container images into Podman +# +# CRITICAL: all Archipelago containers run ROOTLESS as the archipelago user. +# This script runs as root (systemd oneshot), so a plain `podman load` here +# puts the images into root's storage where the rootless runtime can never +# see them — containers then silently depend on registry pulls, and a fresh +# install without internet gets no apps at all. Always load into the +# archipelago user's storage. IMAGES_DIR="/opt/archipelago/container-images" LOG_FILE="/var/log/archipelago-images.log" @@ -1483,21 +1498,32 @@ if [ ! -d "$IMAGES_DIR" ]; then exit 0 fi +ARCH_UID=$(id -u archipelago) +# Linger gives the archipelago user a runtime dir (/run/user/UID) at boot, +# before any login — required for rootless podman. +loginctl enable-linger archipelago 2>/dev/null || true +for _ in $(seq 1 30); do + [ -d "/run/user/$ARCH_UID" ] && break + sleep 1 +done +PODMAN="runuser -u archipelago -- env XDG_RUNTIME_DIR=/run/user/$ARCH_UID podman" +$PODMAN system migrate >> "$LOG_FILE" 2>&1 || true + for tarfile in "$IMAGES_DIR"/*.tar; do if [ -f "$tarfile" ]; then echo "$(date): Loading $(basename "$tarfile")..." >> "$LOG_FILE" - podman load -i "$tarfile" >> "$LOG_FILE" 2>&1 && \ + $PODMAN load -i "$tarfile" >> "$LOG_FILE" 2>&1 && \ echo "$(date): Successfully loaded $(basename "$tarfile")" >> "$LOG_FILE" || \ echo "$(date): Failed to load $(basename "$tarfile")" >> "$LOG_FILE" fi done # Ensure archy-net exists for mempool stack (db, api, frontend) -podman network create archy-net 2>/dev/null || true +$PODMAN network create archy-net 2>/dev/null || true echo "$(date): Container image load complete" >> "$LOG_FILE" echo "$(date): Available images:" >> "$LOG_FILE" -podman images >> "$LOG_FILE" 2>&1 +$PODMAN images >> "$LOG_FILE" 2>&1 LOADSCRIPT chmod +x "$WORK_DIR/load-container-images.sh" diff --git a/neode-ui/src/views/discover/curatedApps.ts b/neode-ui/src/views/discover/curatedApps.ts index ce038e53..3e0c6a5e 100644 --- a/neode-ui/src/views/discover/curatedApps.ts +++ b/neode-ui/src/views/discover/curatedApps.ts @@ -105,6 +105,7 @@ export function getCuratedAppList(): MarketplaceApp[] { { id: 'netbird', title: 'NetBird', version: '0.71.2', description: 'Self-hosted WireGuard mesh VPN control plane with dashboard, embedded identity provider, management API, signal, relay, and STUN.', icon: '/assets/img/app-icons/netbird.svg', author: 'NetBird', dockerImage: 'docker.io/netbirdio/dashboard:v2.38.0', repoUrl: 'https://github.com/netbirdio/netbird' }, { id: 'electrumx', title: 'ElectrumX', version: '1.18.0', description: 'Electrum protocol server. Index the blockchain for fast wallet lookups, privately.', icon: '/assets/img/app-icons/electrumx.png', author: 'Luke Childs', dockerImage: `${R}/electrumx:v1.18.0`, repoUrl: 'https://github.com/spesmilo/electrumx' }, { id: 'fedimint', title: 'Fedimint Guardian', version: '0.10.0', description: 'Federated Bitcoin mint. Private, scalable Bitcoin through federated guardians.', icon: '/assets/img/app-icons/fedimint.png', author: 'Fedimint', dockerImage: `${R}/fedimintd:v0.10.0`, repoUrl: 'https://github.com/fedimint/fedimint' }, + { id: 'fedimint-clientd', title: 'Fedimint Client', version: '0.8.0', description: 'Fedimint ecash client daemon (fmcd). Lets the node hold Fedimint ecash and join federations; the wallet talks to it over a local REST API.', icon: '/assets/img/app-icons/fedimint.png', author: 'Fedimint', dockerImage: `${R}/fmcd:0.8.1`, repoUrl: 'https://github.com/minmoto/fmcd' }, { id: 'indeedhub', title: 'Indeehub', version: '1.0.0', description: 'Bitcoin documentary streaming with Nostr identity. Stream sovereignty content.', icon: '/assets/img/app-icons/indeedhub.png', author: 'Indeehub Team', dockerImage: `${R}/indeedhub:1.0.0`, repoUrl: 'https://github.com/indeedhub/indeedhub' }, { id: 'nostrudel', title: 'noStrudel', version: '0.40.0', category: 'nostr', description: 'Feature-rich Nostr web client. Browse feeds, post notes, manage relays with NIP-07.', icon: '/assets/img/app-icons/nostrudel.svg', author: 'hzrd149', dockerImage: '', repoUrl: 'https://github.com/hzrd149/nostrudel', webUrl: 'https://nostrudel.ninja' }, { id: 'botfights', title: 'BotFights', version: '1.0.0', category: 'community', description: 'Bot arena + 2-player arcade fighter with controller support. AI bots battle in trivia, humans duke it out with controllers.', icon: '/assets/img/app-icons/botfights.svg', author: 'BotFights', dockerImage: `${R}/botfights:1.1.0`, repoUrl: 'https://botfights.net' }, @@ -122,6 +123,7 @@ export const INSTALLED_ALIASES: Record = { immich: ['immich-server', 'immich-app', 'immich_server'], nextcloud: ['nextcloud-aio', 'nextcloud-server'], fedimint: ['fedimint-gateway'], + 'fedimint-clientd': ['fedimint-clientd'], electrumx: ['electrumx'], grafana: ['grafana'], jellyfin: ['jellyfin'], diff --git a/scripts/first-boot-containers.sh b/scripts/first-boot-containers.sh index 3d51c005..9ea047e9 100755 --- a/scripts/first-boot-containers.sh +++ b/scripts/first-boot-containers.sh @@ -112,6 +112,13 @@ if [ -f "$UNBUNDLED_MARKER" ]; then # Helper: pull image with fallback registry pull_with_fallback() { local img="$1" + # Pre-bundled ISO images are already loaded into the rootless + # storage by archipelago-load-images.service — use them and skip + # the network entirely (fresh installs must work offline). + if $DOCKER image exists "$img" 2>/dev/null; then + log " Image already present locally: $img" + return 0 + fi log " Pulling $img..." if $DOCKER pull "$img" 2>>"$LOG"; then return 0 diff --git a/scripts/image-versions.sh b/scripts/image-versions.sh index d8f25951..3b6640cf 100644 --- a/scripts/image-versions.sh +++ b/scripts/image-versions.sh @@ -59,9 +59,10 @@ ADGUARDHOME_IMAGE="$ARCHY_REGISTRY/adguardhome:v0.107.55" FEDIMINT_IMAGE="$ARCHY_REGISTRY/fedimintd:v0.10.0" FEDIMINT_GATEWAY_IMAGE="$ARCHY_REGISTRY/gatewayd:v0.10.0" # fmcd = Fedimint client daemon (iroh-capable, fedimint-client 0.8.2). Built -# from minmoto/fmcd. NOT yet added to the bundled CONTAINER_IMAGES list / first- -# boot auto-create: bundling fleet-wide needs a fleet-reachable default -# federation first (the interim default is node-local). See docs/dual-ecash-design.md. +# from minmoto/fmcd. Bundled on the ISO in BOTH modes (full CONTAINER_IMAGES +# list and the unbundled core bundle) and auto-created by first-boot as a +# baseline app so ecash works offline out of the box. +# See docs/dual-ecash-design.md. FMCD_IMAGE="$ARCHY_REGISTRY/fmcd:0.8.1" # Ark (bark)