fix(iso): make baseline apps work on a fresh install with no internet
Some checks failed
Demo images / Build & push demo images (push) Failing after 1m36s

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 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-16 07:31:28 -04:00
parent 454c4bb25c
commit 6dcdada371
4 changed files with 55 additions and 19 deletions

View File

@ -1349,21 +1349,29 @@ if [ "$UNBUNDLED" = "1" ]; then
# Clean stale images from previous builds (e.g. bundled build tars leaking into unbundled) # Clean stale images from previous builds (e.g. bundled build tars leaking into unbundled)
rm -rf "$IMAGES_DIR" rm -rf "$IMAGES_DIR"
mkdir -p "$IMAGES_DIR" mkdir -p "$IMAGES_DIR"
# FileBrowser is a core dependency (powers the Cloud file manager) — always bundle it # Core baseline apps created by first-boot-containers.sh even in
CORE_IMAGE="${FILEBROWSER_IMAGE}" # unbundled mode — their images must ride on the ISO so a fresh install
CORE_FILE="filebrowser.tar" # works with no internet: FileBrowser (Cloud file manager) and fmcd
if [ -f "$IMAGES_DIR/$CORE_FILE" ]; then # (fedimint-clientd, ecash/sats out of the box).
echo " ✅ Using cached: $CORE_FILE" CORE_BUNDLE="
else ${FILEBROWSER_IMAGE} filebrowser.tar
echo " Pulling $CORE_IMAGE ($CONTAINER_PLATFORM)..." ${FMCD_IMAGE} fmcd.tar
if container_pull "$CORE_IMAGE"; then "
$CONTAINER_CMD save "$CORE_IMAGE" -o "$IMAGES_DIR/$CORE_FILE" 2>/dev/null && \ echo "$CORE_BUNDLE" | while read -r CORE_IMAGE CORE_FILE; do
echo " ✅ Saved core: $CORE_FILE ($(du -h "$IMAGES_DIR/$CORE_FILE" | cut -f1))" || \ [ -n "$CORE_IMAGE" ] || continue
echo " ⚠️ Failed to save $CORE_IMAGE" if [ -f "$IMAGES_DIR/$CORE_FILE" ]; then
echo " ✅ Using cached: $CORE_FILE"
else 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
fi done
else else
echo "📦 Step 3b: Bundling container images for offline use..." echo "📦 Step 3b: Bundling container images for offline use..."
@ -1472,6 +1480,13 @@ LOADSERVICE
cat > "$WORK_DIR/load-container-images.sh" <<'LOADSCRIPT' cat > "$WORK_DIR/load-container-images.sh" <<'LOADSCRIPT'
#!/bin/bash #!/bin/bash
# Load pre-bundled container images into Podman # 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" IMAGES_DIR="/opt/archipelago/container-images"
LOG_FILE="/var/log/archipelago-images.log" LOG_FILE="/var/log/archipelago-images.log"
@ -1483,21 +1498,32 @@ if [ ! -d "$IMAGES_DIR" ]; then
exit 0 exit 0
fi 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 for tarfile in "$IMAGES_DIR"/*.tar; do
if [ -f "$tarfile" ]; then if [ -f "$tarfile" ]; then
echo "$(date): Loading $(basename "$tarfile")..." >> "$LOG_FILE" 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): Successfully loaded $(basename "$tarfile")" >> "$LOG_FILE" || \
echo "$(date): Failed to load $(basename "$tarfile")" >> "$LOG_FILE" echo "$(date): Failed to load $(basename "$tarfile")" >> "$LOG_FILE"
fi fi
done done
# Ensure archy-net exists for mempool stack (db, api, frontend) # 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): Container image load complete" >> "$LOG_FILE"
echo "$(date): Available images:" >> "$LOG_FILE" echo "$(date): Available images:" >> "$LOG_FILE"
podman images >> "$LOG_FILE" 2>&1 $PODMAN images >> "$LOG_FILE" 2>&1
LOADSCRIPT LOADSCRIPT
chmod +x "$WORK_DIR/load-container-images.sh" chmod +x "$WORK_DIR/load-container-images.sh"

View File

@ -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: '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: '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', 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: '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: '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' }, { 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<string, string[]> = {
immich: ['immich-server', 'immich-app', 'immich_server'], immich: ['immich-server', 'immich-app', 'immich_server'],
nextcloud: ['nextcloud-aio', 'nextcloud-server'], nextcloud: ['nextcloud-aio', 'nextcloud-server'],
fedimint: ['fedimint-gateway'], fedimint: ['fedimint-gateway'],
'fedimint-clientd': ['fedimint-clientd'],
electrumx: ['electrumx'], electrumx: ['electrumx'],
grafana: ['grafana'], grafana: ['grafana'],
jellyfin: ['jellyfin'], jellyfin: ['jellyfin'],

View File

@ -112,6 +112,13 @@ if [ -f "$UNBUNDLED_MARKER" ]; then
# Helper: pull image with fallback registry # Helper: pull image with fallback registry
pull_with_fallback() { pull_with_fallback() {
local img="$1" 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..." log " Pulling $img..."
if $DOCKER pull "$img" 2>>"$LOG"; then if $DOCKER pull "$img" 2>>"$LOG"; then
return 0 return 0

View File

@ -59,9 +59,10 @@ ADGUARDHOME_IMAGE="$ARCHY_REGISTRY/adguardhome:v0.107.55"
FEDIMINT_IMAGE="$ARCHY_REGISTRY/fedimintd:v0.10.0" FEDIMINT_IMAGE="$ARCHY_REGISTRY/fedimintd:v0.10.0"
FEDIMINT_GATEWAY_IMAGE="$ARCHY_REGISTRY/gatewayd:v0.10.0" FEDIMINT_GATEWAY_IMAGE="$ARCHY_REGISTRY/gatewayd:v0.10.0"
# fmcd = Fedimint client daemon (iroh-capable, fedimint-client 0.8.2). Built # 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- # from minmoto/fmcd. Bundled on the ISO in BOTH modes (full CONTAINER_IMAGES
# boot auto-create: bundling fleet-wide needs a fleet-reachable default # list and the unbundled core bundle) and auto-created by first-boot as a
# federation first (the interim default is node-local). See docs/dual-ecash-design.md. # baseline app so ecash works offline out of the box.
# See docs/dual-ecash-design.md.
FMCD_IMAGE="$ARCHY_REGISTRY/fmcd:0.8.1" FMCD_IMAGE="$ARCHY_REGISTRY/fmcd:0.8.1"
# Ark (bark) # Ark (bark)