feat: replace Debian Live with custom debootstrap ISO base + branding

Major ISO build overhaul on dev-iso branch:

- Replace ~800MB Debian Live download with debootstrap --variant=minbase
  (~150MB installer squashfs built from scratch)
- Custom initramfs with archipelago-mount hook for boot media detection
- Systemd service auto-starts installer (replaces profile.d hack)
- GRUB + ISOLINUX configs written from scratch (no Debian Live dependency)
- EFI boot image built with grub-mkimage (no more MBR extraction)
- Archipelago GRUB theme: dark background, Bitcoin orange accents
- Theme installed on both installer ISO and target system
- Rootfs optimizations: --no-install-recommends, strip docs/man/locales,
  remove firmware-misc-nonfree/wget/htop, add explicit font deps
- Separate CI workflow (build-iso-dev.yml) for dev-iso branch
- Includes pre-existing fixes from main (build-iso.yml, middleware, Login)

Target: sub-2GB unbundled ISO (down from 3.9GB)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-27 18:19:49 +00:00
parent 39857c775a
commit 7183ebfa2b
4 changed files with 802 additions and 381 deletions

View File

@@ -31,7 +31,7 @@
# build-auto-installer-iso.sh — Main orchestrator (config, CLI args, step sequencing)
# lib/
# rootfs.sh — Step 1: Build root filesystem via Docker (~185 lines)
# installer-env.sh — Step 2: Download/extract Debian Live base ISO (~80 lines)
# installer-env.sh — Step 2: Build minimal installer via debootstrap (~80 lines)
# components.sh — Step 3: Add Archipelago components (binary, configs, web UI) (~120 lines)
# container-images.sh — Step 3b: Bundle container images for offline install (~330 lines)
# auto-install-script.sh — Step 4: Generate the embedded auto-install.sh (~615 lines)
@@ -133,36 +133,42 @@ check_tools() {
missing="$missing docker-or-podman"
fi
if ! command -v xorriso >/dev/null 2>&1; then
missing="$missing xorriso"
for tool in xorriso mksquashfs; do
if ! command -v $tool >/dev/null 2>&1; then
missing="$missing $tool"
fi
done
# Check for isolinux MBR (needed for hybrid USB boot)
if [ ! -f /usr/lib/ISOLINUX/isohdpfx.bin ] && [ ! -f /usr/share/syslinux/isohdpfx.bin ]; then
missing="$missing isolinux"
fi
if ! command -v 7z >/dev/null 2>&1 && ! command -v 7za >/dev/null 2>&1; then
missing="$missing p7zip-full"
fi
if [ -n "$missing" ]; then
echo "Missing required tools:$missing"
echo "Missing required tools:$missing"
if [ "$can_install" = true ]; then
echo " 📦 Auto-installing missing dependencies..."
echo " Auto-installing missing dependencies..."
apt-get update -qq
if [[ "$missing" == *"xorriso"* ]]; then
apt-get install -y xorriso
fi
if [[ "$missing" == *"p7zip-full"* ]]; then
apt-get install -y p7zip-full
if [[ "$missing" == *"mksquashfs"* ]]; then
apt-get install -y squashfs-tools
fi
if [[ "$missing" == *"isolinux"* ]]; then
apt-get install -y isolinux syslinux-common
fi
if [[ "$missing" == *"docker-or-podman"* ]]; then
echo " Installing podman..."
apt-get install -y podman
CONTAINER_CMD="podman"
fi
echo " Dependencies installed successfully!"
echo " Dependencies installed successfully!"
else
echo " Install with: sudo apt install xorriso podman"
echo " Install with: sudo apt install xorriso squashfs-tools isolinux podman"
echo " Or run this script with sudo to auto-install"
exit 1
fi
@@ -226,7 +232,7 @@ RUN echo "deb http://deb.debian.org/debian bookworm main non-free-firmware" > /e
rm -f /etc/apt/sources.list.d/debian.sources
# Install all packages we need including nginx, podman, tor, and openssl (for self-signed certs)
RUN apt-get update && apt-get install -y \
RUN apt-get update && apt-get install -y --no-install-recommends \
${LINUX_IMAGE_PKG} \
${GRUB_EFI_PKG} \
${GRUB_EFI_SIGNED_PKG} \
@@ -241,9 +247,7 @@ RUN apt-get update && apt-get install -y \
podman \
tor \
curl \
wget \
git \
htop \
vim-tiny \
ca-certificates \
openssl \
@@ -254,19 +258,26 @@ RUN apt-get update && apt-get install -y \
cryptsetup \
firmware-realtek \
firmware-iwlwifi \
firmware-misc-nonfree \
intel-microcode \
amd64-microcode \
xorg \
chromium \
unclutter \
fonts-liberation \
xfonts-base \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Strip docs, man pages, and unused locales
RUN find /usr/share/doc -depth -type f ! -name copyright -delete 2>/dev/null || true && \
find /usr/share/doc -empty -delete 2>/dev/null || true && \
rm -rf /usr/share/man /usr/share/info /usr/share/lintian /usr/share/linda && \
find /usr/share/locale -maxdepth 1 -mindepth 1 ! -name 'en_US' ! -name 'locale.alias' -exec rm -rf {} + 2>/dev/null || true
# Install Tailscale from official repo
RUN curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null && \
curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list && \
apt-get update && apt-get install -y tailscale && \
apt-get update && apt-get install -y --no-install-recommends tailscale && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# Configure locale
@@ -418,88 +429,249 @@ else
fi
# =============================================================================
# STEP 2: Create installer environment
# STEP 2: Build minimal installer environment (replaces Debian Live)
# =============================================================================
echo ""
echo "📦 Step 2: Creating installer environment..."
echo "Step 2: Building minimal installer environment via debootstrap..."
# Download Debian Live as our installer base
BASE_ISO="$WORK_DIR/debian-live-installer.iso"
EXPECTED_SIZE=350000000 # ~350MB min (Debian 12 Live standard ~600MB)
INSTALLER_ISO="$WORK_DIR/installer-iso"
INSTALLER_SQUASHFS="$WORK_DIR/installer-squashfs"
rm -rf "$INSTALLER_ISO" "$INSTALLER_SQUASHFS"
mkdir -p "$INSTALLER_ISO/live" "$INSTALLER_ISO/archipelago"
mkdir -p "$INSTALLER_ISO/boot/grub" "$INSTALLER_ISO/isolinux"
mkdir -p "$INSTALLER_ISO/EFI/BOOT"
# Check if file exists and is complete
if [ -f "$BASE_ISO" ]; then
CURRENT_SIZE=$(stat -f%z "$BASE_ISO" 2>/dev/null || stat -c%s "$BASE_ISO" 2>/dev/null || echo 0)
if [ "$CURRENT_SIZE" -ge "$EXPECTED_SIZE" ]; then
echo " ✅ Debian Live base already downloaded"
else
echo " Found incomplete download ($(($CURRENT_SIZE / 1024 / 1024))MB), removing..."
rm -f "$BASE_ISO"
# Build the installer filesystem inside a container
# This creates: vmlinuz, initrd.img, filesystem.squashfs
echo " Building installer rootfs with debootstrap (this takes a few minutes)..."
$CONTAINER_CMD run --rm --privileged --platform $CONTAINER_PLATFORM \
-v "$WORK_DIR:/output" \
-e DEB_ARCH="$DEB_ARCH" \
-e LIB_DIR="$LIB_DIR" \
debian:bookworm bash -c '
set -e
apt-get update -qq
apt-get install -y -qq debootstrap squashfs-tools initramfs-tools dosfstools mtools \
grub-efi-amd64-bin grub-pc-bin grub-common isolinux syslinux-common
echo " [container] Running debootstrap --variant=minbase..."
debootstrap --variant=minbase --arch=${DEB_ARCH} \
--include=systemd,systemd-sysv,udev,dbus,bash,coreutils,mount,util-linux,\
kmod,procps,iproute2,ca-certificates,gdisk,\
cryptsetup,cryptsetup-initramfs,parted,dosfstools,e2fsprogs,\
linux-image-${DEB_ARCH},grub-efi-${DEB_ARCH},grub-pc-bin,\
pciutils,usbutils,less,nano \
bookworm /installer http://deb.debian.org/debian
echo " [container] Configuring installer environment..."
# Set hostname
echo "archipelago-installer" > /installer/etc/hostname
# Set root password
echo "root:archipelago" | chroot /installer chpasswd
# Auto-login on tty1
mkdir -p /installer/etc/systemd/system/getty@tty1.service.d
cat > /installer/etc/systemd/system/getty@tty1.service.d/autologin.conf <<GETTY
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM
GETTY
# Create the installer auto-start systemd service
cat > /installer/etc/systemd/system/archipelago-installer.service <<SVCFILE
[Unit]
Description=Archipelago Auto-Installer
After=multi-user.target systemd-logind.service
Wants=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/archipelago-start-installer
StandardInput=tty
StandardOutput=tty
StandardError=tty
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
SVCFILE
chroot /installer systemctl enable archipelago-installer.service
# Create the installer start wrapper
cat > /installer/usr/local/bin/archipelago-start-installer <<WRAPPER
#!/bin/bash
sleep 2
clear
echo ""
echo " ARCHIPELAGO BITCOIN NODE OS"
echo " Automatic Installer"
echo ""
BOOT_MEDIA=""
for dev in /run/archiso /cdrom /media/cdrom /mnt/iso; do
if [ -f "\$dev/archipelago/auto-install.sh" ]; then
BOOT_MEDIA="\$dev"
break
fi
done
if [ -n "\$BOOT_MEDIA" ]; then
echo " Found installer at: \$BOOT_MEDIA"
echo ""
echo " Press Enter to start installation, or Ctrl+C for shell..."
read
exec bash "\$BOOT_MEDIA/archipelago/auto-install.sh"
else
echo " Installer not found on boot media."
echo " Checked: /run/archiso, /cdrom, /media/cdrom, /mnt/iso"
echo ""
echo " Dropping to shell for manual recovery..."
exec /bin/bash
fi
WRAPPER
chmod +x /installer/usr/local/bin/archipelago-start-installer
if [ ! -f "$BASE_ISO" ]; then
echo " Downloading Debian Live base (352MB)..."
echo " (This may take 5-10 minutes depending on network speed)"
# Use wget without -O so --continue actually works
# Download with the ugly SourceForge filename, then rename
# Use Debian 12 (Bookworm) to match the rootfs — NOT Debian 13 (Trixie)
ISO_URL="https://cdimage.debian.org/cdimage/archive/12.10.0-live/${DEB_ARCH}/iso-hybrid/debian-live-12.10.0-${DEB_ARCH}-standard.iso"
if command -v wget >/dev/null 2>&1; then
cd "$WORK_DIR"
wget --tries=10 --read-timeout=120 --continue --progress=bar:force \
--no-check-certificate \
"$ISO_URL" || {
echo " ❌ Download failed or incomplete"
echo " Partial file kept - run script again to continue"
exit 1
}
# Find the downloaded file — wget may use URL filename or follow redirects
FOUND_ISO=$(find "$WORK_DIR" -maxdepth 1 -name "debian-live-12*.iso" -o -name "download" 2>/dev/null | head -1)
if [ -n "$FOUND_ISO" ] && [ -f "$FOUND_ISO" ]; then
mv "$FOUND_ISO" "$BASE_ISO"
else
echo " ❌ Downloaded file not found in $WORK_DIR"
echo " Files present:"
ls -la "$WORK_DIR"/*.iso 2>/dev/null || echo " (no .iso files)"
exit 1
fi
else
# Fallback to curl (no resume support)
curl -L --location-trusted --retry 10 --retry-delay 5 \
--connect-timeout 60 --max-time 1800 \
-o "$BASE_ISO" \
"$ISO_URL" || {
echo " ❌ Download failed"
rm -f "$BASE_ISO"
exit 1
}
# Custom initramfs hook: mount ISO boot media at /run/archiso
mkdir -p /installer/etc/initramfs-tools/hooks
cat > /installer/etc/initramfs-tools/hooks/archipelago <<HOOK
#!/bin/sh
set -e
PREREQ=""
prereqs() { echo "\$PREREQ"; }
case "\$1" in prereqs) prereqs; exit 0;; esac
. /usr/share/initramfs-tools/hook-functions
# Ensure mount helpers and filesystem tools are in initramfs
copy_exec /bin/mount
copy_exec /bin/umount
copy_exec /bin/findfs 2>/dev/null || true
copy_exec /sbin/blkid
manual_add_modules iso9660 vfat squashfs overlay
HOOK
chmod +x /installer/etc/initramfs-tools/hooks/archipelago
mkdir -p /installer/etc/initramfs-tools/scripts/local-bottom
cat > /installer/etc/initramfs-tools/scripts/local-bottom/archipelago-mount <<INITSCRIPT
#!/bin/sh
PREREQ=""
prereqs() { echo "\$PREREQ"; }
case "\$1" in prereqs) prereqs; exit 0;; esac
. /scripts/functions
# Try to find and mount the Archipelago boot media
mkdir -p /run/archiso
log_begin_msg "Searching for Archipelago boot media..."
# Try CD-ROM first, then USB partitions
for dev in /dev/sr0 /dev/sd??* /dev/nvme*p*; do
[ -b "\$dev" ] 2>/dev/null || continue
mount -o ro "\$dev" /run/archiso 2>/dev/null || continue
if [ -d /run/archiso/archipelago ]; then
log_end_msg 0
echo "Found Archipelago media on \$dev"
exit 0
fi
# Verify download size
FINAL_SIZE=$(stat -f%z "$BASE_ISO" 2>/dev/null || stat -c%s "$BASE_ISO" 2>/dev/null || echo 0)
if [ "$FINAL_SIZE" -lt "$EXPECTED_SIZE" ]; then
echo " ❌ Download incomplete: got $(($FINAL_SIZE / 1024 / 1024))MB, expected 352MB"
umount /run/archiso 2>/dev/null || true
done
log_end_msg 1
echo "Archipelago boot media not found (will retry from userspace)"
INITSCRIPT
chmod +x /installer/etc/initramfs-tools/scripts/local-bottom/archipelago-mount
# Strip docs and man pages from installer
rm -rf /installer/usr/share/man/* /installer/usr/share/doc/*
rm -rf /installer/var/lib/apt/lists/* /installer/var/cache/apt/*
# Extract kernel
KVER=$(ls /installer/lib/modules/ | sort -V | tail -1)
echo " [container] Kernel version: $KVER"
cp /installer/boot/vmlinuz-$KVER /output/vmlinuz
# Build initramfs with our custom hooks
chroot /installer update-initramfs -c -k $KVER
cp /installer/boot/initrd.img-$KVER /output/initrd.img
# Create squashfs
echo " [container] Creating installer squashfs..."
mksquashfs /installer /output/filesystem.squashfs -comp xz -Xbcj x86 -noappend -quiet
# Build GRUB EFI image
echo " [container] Building GRUB EFI image..."
grub-mkimage -O x86_64-efi -o /output/BOOTX64.EFI -p /boot/grub \
part_gpt part_msdos fat iso9660 udf normal boot linux search \
search_fs_uuid search_fs_file search_label configfile echo cat \
ls test true loopback gfxterm gfxmenu font png
# Create EFI FAT image (4MB)
dd if=/dev/zero of=/output/efi.img bs=1M count=4 2>/dev/null
mkfs.vfat /output/efi.img >/dev/null
mmd -i /output/efi.img ::/EFI ::/EFI/BOOT
mcopy -i /output/efi.img /output/BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
# Copy ISOLINUX files for legacy BIOS boot
cp /usr/lib/ISOLINUX/isolinux.bin /output/isolinux.bin
cp /usr/lib/syslinux/modules/bios/ldlinux.c32 /output/ldlinux.c32
cp /usr/lib/syslinux/modules/bios/menu.c32 /output/menu.c32 2>/dev/null || true
cp /usr/lib/syslinux/modules/bios/libutil.c32 /output/libutil.c32 2>/dev/null || true
cp /usr/lib/ISOLINUX/isohdpfx.bin /output/isohdpfx.bin
# Generate GRUB fonts for theme
echo " [container] Generating GRUB fonts..."
apt-get install -y -qq fonts-dejavu-core grub-common >/dev/null 2>&1
mkdir -p /output/grub-fonts
grub-mkfont -s 12 -o /output/grub-fonts/dejavu_12.pf2 /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
grub-mkfont -s 14 -o /output/grub-fonts/dejavu_14.pf2 /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
grub-mkfont -s 16 -o /output/grub-fonts/dejavu_16.pf2 /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
grub-mkfont -s 24 -o /output/grub-fonts/dejavu_24.pf2 /usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf
echo " [container] Done!"
'
# Verify artifacts
for artifact in vmlinuz initrd.img filesystem.squashfs BOOTX64.EFI efi.img isolinux.bin isohdpfx.bin; do
if [ ! -f "$WORK_DIR/$artifact" ]; then
echo " FATAL: Missing build artifact: $artifact"
exit 1
fi
echo " ✅ Download complete ($(($FINAL_SIZE / 1024 / 1024))MB)"
done
# Place artifacts into ISO directory structure
cp "$WORK_DIR/vmlinuz" "$INSTALLER_ISO/live/vmlinuz"
cp "$WORK_DIR/initrd.img" "$INSTALLER_ISO/live/initrd.img"
cp "$WORK_DIR/filesystem.squashfs" "$INSTALLER_ISO/live/filesystem.squashfs"
cp "$WORK_DIR/BOOTX64.EFI" "$INSTALLER_ISO/EFI/BOOT/BOOTX64.EFI"
cp "$WORK_DIR/efi.img" "$INSTALLER_ISO/boot/grub/efi.img"
cp "$WORK_DIR/isolinux.bin" "$INSTALLER_ISO/isolinux/isolinux.bin"
cp "$WORK_DIR/ldlinux.c32" "$INSTALLER_ISO/isolinux/ldlinux.c32"
cp "$WORK_DIR/menu.c32" "$INSTALLER_ISO/isolinux/menu.c32" 2>/dev/null || true
cp "$WORK_DIR/libutil.c32" "$INSTALLER_ISO/isolinux/libutil.c32" 2>/dev/null || true
# Install GRUB theme
THEME_SRC="$SCRIPT_DIR/branding/grub-theme"
THEME_DST="$INSTALLER_ISO/boot/grub/themes/archipelago"
mkdir -p "$THEME_DST"
if [ -f "$THEME_SRC/theme.txt" ]; then
cp "$THEME_SRC/theme.txt" "$THEME_DST/"
echo " Installed GRUB theme from branding/grub-theme/"
fi
# Install generated fonts
if [ -d "$WORK_DIR/grub-fonts" ]; then
cp "$WORK_DIR/grub-fonts/"*.pf2 "$THEME_DST/"
# Also copy unicode font for GRUB to load
cp "$WORK_DIR/grub-fonts/dejavu_16.pf2" "$INSTALLER_ISO/boot/grub/font.pf2"
fi
echo " Extracting installer base..."
INSTALLER_ISO="$WORK_DIR/installer-iso"
rm -rf "$INSTALLER_ISO"
mkdir -p "$INSTALLER_ISO"
cd "$INSTALLER_ISO"
# 7z returns exit code 2 for warnings (symlinks in ISO) — check for key files instead
7z x -y "$BASE_ISO" >/dev/null 2>&1 || 7za x -y "$BASE_ISO" >/dev/null 2>&1 || bsdtar -xf "$BASE_ISO" 2>/dev/null || true
if [ ! -d "$INSTALLER_ISO/live" ] || [ ! -f "$INSTALLER_ISO/live/vmlinuz" ]; then
echo " ❌ Failed to extract ISO. Install p7zip-full: sudo apt install p7zip-full"
exit 1
fi
echo " Installer squashfs: $(du -h "$INSTALLER_ISO/live/filesystem.squashfs" | cut -f1)"
echo " Kernel: $(du -h "$INSTALLER_ISO/live/vmlinuz" | cut -f1)"
echo " Initrd: $(du -h "$INSTALLER_ISO/live/initrd.img" | cut -f1)"
echo " Step 2 complete (custom minimal base, no Debian Live)"
# =============================================================================
# STEP 3: Add Archipelago components
@@ -1152,7 +1324,7 @@ echo ""
# Find boot media
BOOT_MEDIA=""
for dev in /run/live/medium /lib/live/mount/medium /cdrom; do
for dev in /run/archiso /cdrom /media/cdrom /run/live/medium /lib/live/mount/medium /mnt/iso; do
if [ -d "$dev/archipelago" ]; then
BOOT_MEDIA="$dev"
break
@@ -1694,8 +1866,9 @@ while true; do
--disable-save-password-bubble \
--disable-suggestions-service \
--password-store=basic \
--disable-features=TranslateUI,PasswordManagerOnboarding \
--disable-features=TranslateUI,PasswordManagerOnboarding,AutofillServerCommunication,PasswordManagerEnabled \
--disable-component-update \
--credentials_enable_service=false \
--user-data-dir=/home/archipelago/.config/chromium-kiosk
sleep 3
done
@@ -1869,10 +2042,8 @@ elif [ -n "${GRUB_BIOS_TARGET}" ]; then
echo " Skipping Legacy BIOS bootloader (machine supports UEFI)"
fi
# Remove any live-boot artifacts that could cause squashfs errors on boot
# These can sneak in from Docker base image or package dependencies
echo " Cleaning live-boot artifacts..."
chroot /mnt/target apt-get remove -y --purge live-boot live-boot-initramfs-tools live-config 2>/dev/null || true
# Clean any stale live-boot artifacts (should not exist in the custom rootfs,
# but clean up defensively in case Docker base image pulled them in)
rm -f /mnt/target/etc/initramfs-tools/conf.d/live-boot* 2>/dev/null || true
rm -f /mnt/target/usr/share/initramfs-tools/scripts/live* 2>/dev/null || true
rm -f /mnt/target/usr/share/initramfs-tools/hooks/live* 2>/dev/null || true
@@ -1880,6 +2051,14 @@ rm -f /mnt/target/usr/share/initramfs-tools/hooks/live* 2>/dev/null || true
# Suppress os-prober warning in GRUB
echo "GRUB_DISABLE_OS_PROBER=true" >> /mnt/target/etc/default/grub
# Install Archipelago GRUB theme on target system
if [ -d "$BOOT_MEDIA/boot/grub/themes/archipelago" ]; then
mkdir -p /mnt/target/boot/grub/themes/archipelago
cp "$BOOT_MEDIA/boot/grub/themes/archipelago/"* /mnt/target/boot/grub/themes/archipelago/
echo 'GRUB_THEME="/boot/grub/themes/archipelago/theme.txt"' >> /mnt/target/etc/default/grub
echo " Installed Archipelago GRUB theme on target"
fi
# Regenerate initramfs — the one from Docker export is corrupt/incomplete
# (Docker builds have limited /proc, /sys, /dev so initramfs generation fails silently)
echo " Regenerating initramfs..."
@@ -1888,8 +2067,12 @@ chroot /mnt/target update-initramfs -u -k all
chroot /mnt/target update-grub
# Install udev rule for mesh radio stable naming (/dev/mesh-radio)
if [ -f /cdrom/99-mesh-radio.rules ]; then
cp /cdrom/99-mesh-radio.rules /mnt/target/etc/udev/rules.d/99-mesh-radio.rules
MESH_RULES=""
for p in "$BOOT_MEDIA/99-mesh-radio.rules" /cdrom/99-mesh-radio.rules "$BOOT_MEDIA/archipelago/configs/99-mesh-radio.rules"; do
[ -f "$p" ] && MESH_RULES="$p" && break
done
if [ -n "$MESH_RULES" ]; then
cp "$MESH_RULES" /mnt/target/etc/udev/rules.d/99-mesh-radio.rules
echo " Installed mesh radio udev rule"
fi
@@ -2207,12 +2390,13 @@ read -p "Press Enter to reboot (make sure USB is removed)..."
exec 2>/dev/null
# Try to eject the USB boot media to prevent booting back into installer
BOOT_DEV=$(findmnt -n -o SOURCE /run/live/medium 2>/dev/null || findmnt -n -o SOURCE /lib/live/mount/medium 2>/dev/null || echo "")
BOOT_DEV=$(findmnt -n -o SOURCE /run/archiso 2>/dev/null || findmnt -n -o SOURCE /cdrom 2>/dev/null || findmnt -n -o SOURCE /run/live/medium 2>/dev/null || echo "")
if [ -n "$BOOT_DEV" ]; then
BOOT_DISK=$(lsblk -no PKNAME "$BOOT_DEV" 2>/dev/null | head -1)
if [ -n "$BOOT_DISK" ]; then
umount -l /run/archiso 2>/dev/null || true
umount -l /cdrom 2>/dev/null || true
umount -l /run/live/medium 2>/dev/null || true
umount -l /lib/live/mount/medium 2>/dev/null || true
eject "/dev/$BOOT_DISK" 2>/dev/null || true
fi
fi
@@ -2230,227 +2414,100 @@ fi
chmod +x "$ARCH_DIR/auto-install.sh"
# =============================================================================
# STEP 5: Configure auto-start installer on boot
# STEP 5: Configure boot loader and ISO structure
# =============================================================================
echo ""
echo "📦 Step 5: Configuring auto-start..."
echo "Step 5: Configuring boot loaders..."
# Create a squashfs overlay module that Debian Live will automatically load
# This is the proper way to add files to the live filesystem
OVERLAY_DIR="$WORK_DIR/overlay-root"
rm -rf "$OVERLAY_DIR"
mkdir -p "$OVERLAY_DIR/etc/profile.d"
mkdir -p "$OVERLAY_DIR/etc/skel"
mkdir -p "$OVERLAY_DIR/usr/local/bin"
mkdir -p "$OVERLAY_DIR/usr/sbin"
mkdir -p "$OVERLAY_DIR/sbin"
# The installer squashfs (from Step 2) already contains:
# - systemd service for auto-starting the installer
# - auto-login on tty1
# - custom initramfs hook for mounting boot media at /run/archiso
# - all partitioning tools (parted, mkfs.*, cryptsetup)
#
# Step 5 just needs to create the GRUB and ISOLINUX boot configs.
# Download and extract required tools AND their libraries for offline use
echo " Downloading partitioning tools for offline use..."
TOOLS_DIR="$WORK_DIR/tools-extract"
rm -rf "$TOOLS_DIR"
mkdir -p "$TOOLS_DIR"
# Create GRUB configuration
echo " Writing GRUB config..."
cat > "$INSTALLER_ISO/boot/grub/grub.cfg" <<'GRUBCFG'
set timeout=5
set default=0
$CONTAINER_CMD run --rm --platform $CONTAINER_PLATFORM \
-v "$TOOLS_DIR:/output" \
debian:bookworm \
bash -c '
apt-get update -qq
apt-get install -y -qq parted dosfstools e2fsprogs
# Copy binaries
cp /usr/sbin/parted /output/
cp /usr/sbin/mkfs.vfat /output/ 2>/dev/null || cp /sbin/mkfs.vfat /output/ 2>/dev/null || true
cp /usr/sbin/mkfs.ext4 /output/ 2>/dev/null || cp /sbin/mkfs.ext4 /output/ 2>/dev/null || true
cp /usr/sbin/mke2fs /output/ 2>/dev/null || cp /sbin/mke2fs /output/ 2>/dev/null || true
cp /sbin/mkfs.fat /output/ 2>/dev/null || true
# Copy required shared libraries for parted
mkdir -p /output/lib
cp /lib/${LIB_DIR}/libparted.so* /output/lib/ 2>/dev/null || true
cp /usr/lib/${LIB_DIR}/libparted.so* /output/lib/ 2>/dev/null || true
cp /lib/${LIB_DIR}/libreadline.so* /output/lib/ 2>/dev/null || true
cp /usr/lib/${LIB_DIR}/libreadline.so* /output/lib/ 2>/dev/null || true
cp /lib/${LIB_DIR}/libdevmapper.so* /output/lib/ 2>/dev/null || true
cp /usr/lib/${LIB_DIR}/libdevmapper.so* /output/lib/ 2>/dev/null || true
# List what parted actually needs
ldd /usr/sbin/parted 2>/dev/null | grep "=>" | awk "{print \$3}" | while read lib; do
[ -f "$lib" ] && cp "$lib" /output/lib/ 2>/dev/null || true
done
echo "Libraries bundled:"
ls -la /output/lib/
'
# Copy tools to overlay
cp "$TOOLS_DIR/parted" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
cp "$TOOLS_DIR/mkfs.vfat" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
cp "$TOOLS_DIR/mkfs.fat" "$OVERLAY_DIR/sbin/" 2>/dev/null || true
cp "$TOOLS_DIR/mkfs.ext4" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
cp "$TOOLS_DIR/mke2fs" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
# Copy shared libraries
mkdir -p "$OVERLAY_DIR/usr/lib/${LIB_DIR}"
cp "$TOOLS_DIR/lib/"*.so* "$OVERLAY_DIR/usr/lib/${LIB_DIR}/" 2>/dev/null || true
chmod +x "$OVERLAY_DIR/usr/sbin/"* 2>/dev/null || true
chmod +x "$OVERLAY_DIR/sbin/"* 2>/dev/null || true
echo " ✅ Partitioning tools and libraries bundled"
# Create the auto-start profile script
cat > "$OVERLAY_DIR/etc/profile.d/z99-archipelago-installer.sh" <<'AUTOSTART'
#!/bin/bash
# Auto-start Archipelago installer on login
# Only run once
if [ -n "$INSTALLER_STARTED" ]; then
return 0 2>/dev/null || exit 0
# Load font for graphical menu
if loadfont /boot/grub/font.pf2; then
set gfxmode=auto
insmod gfxterm
insmod png
terminal_output gfxterm
fi
export INSTALLER_STARTED=1
# Give system a moment to settle
sleep 1
clear
echo ""
echo " ╔═══════════════════════════════════════════════════════════════════╗"
echo " ║ ║"
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS - INSTALLER ║"
echo " ║ ║"
echo " ╚═══════════════════════════════════════════════════════════════════╝"
echo ""
# Find boot media
BOOT_MEDIA=""
for dev in /run/live/medium /lib/live/mount/medium /cdrom /media/cdrom; do
if [ -f "$dev/archipelago/auto-install.sh" ]; then
BOOT_MEDIA="$dev"
break
fi
done
if [ -n "$BOOT_MEDIA" ]; then
echo " Found installer at: $BOOT_MEDIA"
echo ""
echo " Press Enter to start installation, or Ctrl+C for shell..."
read
sudo bash "$BOOT_MEDIA/archipelago/auto-install.sh"
# Archipelago GRUB theme
if [ -f /boot/grub/themes/archipelago/theme.txt ]; then
# Load theme fonts
loadfont /boot/grub/themes/archipelago/dejavu_12.pf2
loadfont /boot/grub/themes/archipelago/dejavu_14.pf2
loadfont /boot/grub/themes/archipelago/dejavu_16.pf2
loadfont /boot/grub/themes/archipelago/dejavu_24.pf2
set theme=/boot/grub/themes/archipelago/theme.txt
else
echo " ⚠️ Installer not found on boot media."
echo ""
echo " Checked: /run/live/medium, /lib/live/mount/medium, /cdrom"
echo ""
echo " You can try manually:"
echo " sudo bash /path/to/archipelago/auto-install.sh"
echo ""
fi
AUTOSTART
chmod +x "$OVERLAY_DIR/etc/profile.d/z99-archipelago-installer.sh"
# Also create .bashrc that sources the profile script (belt and suspenders)
cat > "$OVERLAY_DIR/etc/skel/.bashrc" <<'BASHRC'
# ~/.bashrc: executed by bash(1) for non-login shells.
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# Source profile.d scripts
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r "$i" ]; then
. "$i"
fi
done
fi
BASHRC
# Create a wrapper script that can be called directly
cat > "$OVERLAY_DIR/usr/local/bin/archipelago-install" <<'WRAPPER'
#!/bin/bash
# Archipelago installer wrapper
BOOT_MEDIA=""
for dev in /run/live/medium /lib/live/mount/medium /cdrom /media/cdrom; do
if [ -f "$dev/archipelago/auto-install.sh" ]; then
BOOT_MEDIA="$dev"
break
fi
done
if [ -n "$BOOT_MEDIA" ]; then
exec sudo bash "$BOOT_MEDIA/archipelago/auto-install.sh"
else
echo "Installer not found. Searched:"
echo " /run/live/medium, /lib/live/mount/medium, /cdrom, /media/cdrom"
exit 1
fi
WRAPPER
chmod +x "$OVERLAY_DIR/usr/local/bin/archipelago-install"
# Create the squashfs module
# Debian Live automatically loads all .squashfs files from live/ directory
echo " Creating overlay squashfs module..."
LIVE_DIR="$INSTALLER_ISO/live"
mkdir -p "$LIVE_DIR"
# Check if mksquashfs is available (may need to use Docker on macOS)
if command -v mksquashfs >/dev/null 2>&1; then
mksquashfs "$OVERLAY_DIR" "$LIVE_DIR/99-archipelago.squashfs" -comp xz -noappend
else
# Use $CONTAINER_CMD to create squashfs on macOS
echo " Using $CONTAINER_CMD to create squashfs..."
$CONTAINER_CMD run --rm --platform $CONTAINER_PLATFORM \
-v "$OVERLAY_DIR:/overlay:ro" \
-v "$LIVE_DIR:/output" \
debian:bookworm \
bash -c "apt-get update && apt-get install -y squashfs-tools && mksquashfs /overlay /output/99-archipelago.squashfs -comp xz -noappend"
set menu_color_normal=light-gray/black
set menu_color_highlight=white/dark-gray
fi
echo " ✅ Created overlay module: 99-archipelago.squashfs"
menuentry "Install Archipelago" --hotkey=i {
linux /live/vmlinuz quiet
initrd /live/initrd.img
}
# Modify GRUB config - update branding and ensure components are loaded
if [ -f "$INSTALLER_ISO/boot/grub/grub.cfg" ]; then
echo " Configuring GRUB..."
sed -i.bak \
-e 's/Debian GNU\/Linux/Archipelago Installer/g' \
-e 's/Live system/Install Archipelago/g' \
"$INSTALLER_ISO/boot/grub/grub.cfg"
# Ensure 'components' parameter is present (loads additional squashfs modules)
# Also add 'username=user' to ensure consistent username
if ! grep -q "components" "$INSTALLER_ISO/boot/grub/grub.cfg"; then
sed -i 's/boot=live/boot=live components/' "$INSTALLER_ISO/boot/grub/grub.cfg"
fi
fi
menuentry "Install Archipelago (verbose)" --hotkey=v {
linux /live/vmlinuz
initrd /live/initrd.img
}
if [ -f "$INSTALLER_ISO/isolinux/live.cfg" ]; then
echo " Configuring ISOLINUX..."
sed -i.bak \
-e 's/Debian GNU\/Linux/Archipelago Installer/g' \
-e 's/Live system/Install Archipelago/g' \
"$INSTALLER_ISO/isolinux/live.cfg"
# Add components parameter
if ! grep -q "components" "$INSTALLER_ISO/isolinux/live.cfg"; then
sed -i 's/boot=live/boot=live components/' "$INSTALLER_ISO/isolinux/live.cfg"
fi
fi
menuentry "Boot from local disk" --hotkey=b {
set root=(hd0)
chainloader +1
}
GRUBCFG
if [ -f "$INSTALLER_ISO/isolinux/menu.cfg" ]; then
sed -i.bak \
-e 's/Debian GNU\/Linux/Archipelago Installer/g' \
"$INSTALLER_ISO/isolinux/menu.cfg"
fi
# Create ISOLINUX configuration (legacy BIOS boot)
echo " Writing ISOLINUX config..."
cat > "$INSTALLER_ISO/isolinux/isolinux.cfg" <<'ISOCFG'
UI menu.c32
PROMPT 0
TIMEOUT 50
MENU TITLE ARCHIPELAGO INSTALLER
MENU COLOR border 30;44 #40ffffff #00000000 std
MENU COLOR title 1;36;44 #ff00b7ff #00000000 std
MENU COLOR sel 7;37;40 #ffffffff #ff333333 std
MENU COLOR unsel 37;44 #ffaaaaaa #00000000 std
DEFAULT install
LABEL install
MENU LABEL Install Archipelago
KERNEL /live/vmlinuz
APPEND initrd=/live/initrd.img quiet
MENU DEFAULT
LABEL install-verbose
MENU LABEL Install Archipelago (verbose)
KERNEL /live/vmlinuz
APPEND initrd=/live/initrd.img
LABEL local
MENU LABEL Boot from local disk
LOCALBOOT 0x80
ISOCFG
echo " Step 5 complete (GRUB + ISOLINUX configured)"
# =============================================================================
# STEP 6: Create final ISO
# =============================================================================
echo ""
echo "📦 Step 6: Creating bootable ISO..."
echo "Step 6: Creating bootable ISO..."
if [ "$UNBUNDLED" = "1" ]; then
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-unbundled-${ARCH}.iso"
@@ -2458,60 +2515,27 @@ else
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-${ARCH}.iso"
fi
# Extract MBR from original Debian Live ISO (most reliable for hybrid boot)
# This preserves the exact MBR that makes the ISO work as a USB drive in Balena Etcher
echo " Extracting hybrid MBR from original Debian Live ISO..."
# Use ISOLINUX isohdpfx.bin for hybrid USB boot (built in Step 2)
ISOHDPFX="$WORK_DIR/isohdpfx.bin"
dd if="$BASE_ISO" bs=1 count=432 of="$ISOHDPFX" 2>/dev/null
# Verify we got a valid MBR (should be 432 bytes)
ISOHDPFX_SIZE=$(stat -c%s "$ISOHDPFX" 2>/dev/null || stat -f%z "$ISOHDPFX" 2>/dev/null || echo 0)
if [ "$ISOHDPFX_SIZE" -ne 432 ]; then
echo " ⚠️ MBR extraction unexpected size ($ISOHDPFX_SIZE), trying syslinux paths..."
if [ ! -f "$ISOHDPFX" ]; then
# Fallback to system-installed copy
for path in \
"/usr/lib/ISOLINUX/isohdpfx.bin" \
"/usr/share/syslinux/isohdpfx.bin" \
"/usr/local/share/syslinux/isohdpfx.bin"; do
if [ -f "$path" ]; then
ISOHDPFX="$path"
echo " Using $path"
echo " Using system isohdpfx.bin: $path"
break
fi
done
fi
# Find the EFI boot image — 7z may extract it to different locations
EFI_IMG=""
for efi_path in \
"$INSTALLER_ISO/boot/grub/efi.img" \
"$INSTALLER_ISO/EFI/boot/efi.img" \
"$INSTALLER_ISO/efi.img"; do
if [ -f "$efi_path" ]; then
EFI_IMG="$efi_path"
break
fi
done
# EFI boot image was built in Step 2
EFI_IMG="$INSTALLER_ISO/boot/grub/efi.img"
# If no standalone efi.img, check for [BOOT] directory from 7z extraction
if [ -z "$EFI_IMG" ] && [ -d "$INSTALLER_ISO/[BOOT]" ]; then
# 7z extracts El Torito boot images into [BOOT]/ — the EFI image is usually entry 2
for entry in "$INSTALLER_ISO/[BOOT]/"*; do
# EFI images are typically > 1MB FAT filesystems
if [ -f "$entry" ]; then
entry_size=$(stat -c%s "$entry" 2>/dev/null || stat -f%z "$entry" 2>/dev/null || echo 0)
if [ "$entry_size" -gt 1048576 ]; then
mkdir -p "$INSTALLER_ISO/boot/grub"
cp "$entry" "$INSTALLER_ISO/boot/grub/efi.img"
EFI_IMG="$INSTALLER_ISO/boot/grub/efi.img"
echo " Recovered EFI image from [BOOT] directory"
break
fi
fi
done
fi
if [ -z "$EFI_IMG" ]; then
echo " ⚠️ No EFI boot image found — ISO will only support Legacy BIOS boot"
if [ ! -f "$EFI_IMG" ]; then
echo " WARNING: No EFI boot image — ISO will only support Legacy BIOS boot"
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-iso-level 3 \
@@ -2523,8 +2547,6 @@ if [ -z "$EFI_IMG" ]; then
-partition_offset 16 \
"$INSTALLER_ISO"
else
# Make EFI path relative to INSTALLER_ISO for xorriso
EFI_REL="${EFI_IMG#$INSTALLER_ISO/}"
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-iso-level 3 \
@@ -2534,7 +2556,7 @@ else
-b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-eltorito-alt-boot \
-e "$EFI_REL" \
-e boot/grub/efi.img \
-no-emul-boot \
-isohybrid-gpt-basdat \
-partition_offset 16 \
@@ -2543,43 +2565,19 @@ fi
echo ""
if [ "$UNBUNDLED" = "1" ]; then
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ ✅ UNBUNDLED AUTO-INSTALLER ISO CREATED! ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo "UNBUNDLED AUTO-INSTALLER ISO CREATED"
echo ""
echo "📀 Output: $OUTPUT_ISO"
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
echo ""
echo "🔥 Lightweight installer — apps downloaded on-demand!"
echo ""
echo "Features:"
echo " • Pre-built system (no internet needed during install)"
echo " • Auto-detects internal disk"
echo " • One-button installation"
echo " • Boots directly to Archipelago after install"
echo " • NO pre-bundled apps (smaller ISO)"
echo " • Install any app from the Marketplace (internet required)"
echo " Output: $OUTPUT_ISO"
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
echo ""
echo " Lightweight installer -- apps downloaded on-demand from Marketplace"
else
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ ✅ AUTO-INSTALLER ISO CREATED! ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo "AUTO-INSTALLER ISO CREATED"
echo ""
echo "📀 Output: $OUTPUT_ISO"
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
echo ""
echo "🔥 This is a StartOS-like automatic installer!"
echo ""
echo "Features:"
echo " • Pre-built system (no internet needed during install)"
echo " • Auto-detects internal disk"
echo " • One-button installation"
echo " • Boots directly to Archipelago after install"
echo " • Pre-bundled container apps:"
echo " - Bitcoin Knots v29"
echo " - LND v0.18.4"
echo " - Home Assistant"
echo " Output: $OUTPUT_ISO"
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
echo ""
echo " Full installer with pre-bundled container apps"
fi
echo "To create USB:"
echo " 1. Flash with: sudo dd if=$OUTPUT_ISO of=/dev/rdiskX bs=4m"