#!/bin/bash # # Build Archipelago Bitcoin Node OS ISO - Debian Live Edition # Based on Debian Live for reliable USB boot (like StartOS) # # Usage: ./build-debian-iso.sh # set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" WORK_DIR="$SCRIPT_DIR/build/debian-iso" OUTPUT_DIR="$SCRIPT_DIR/results" DEBIAN_VERSION="bookworm" ARCH="amd64" # Start build timer BUILD_START=$(date +%s) echo "╔════════════════════════════════════════════════════════╗" echo "║ Building Archipelago - Debian Live Edition ║" echo "╚════════════════════════════════════════════════════════╝" echo "" echo "⏱️ Build started: $(date '+%H:%M:%S')" echo "" # Create directories mkdir -p "$WORK_DIR" mkdir -p "$OUTPUT_DIR" # Download Debian Live ISO if not exists BASE_ISO="$WORK_DIR/debian-live-12-${ARCH}-standard.iso" BASE_ISO_SIZE=369131520 # Expected size: ~352MB if [ ! -f "$BASE_ISO" ] || [ $(stat -f%z "$BASE_ISO" 2>/dev/null || stat -c%s "$BASE_ISO" 2>/dev/null || echo 0) -lt 100000000 ]; then echo "📥 Downloading Debian Live 12 (Bookworm) Standard ISO..." echo " Size: ~352MB | This is a one-time download (cached for future builds)" echo "" rm -f "$BASE_ISO" # Download with progress bar curl -# -L -o "$BASE_ISO" \ "https://sourceforge.net/projects/debian-live-respin-iso/files/standard/live-image-debian12.11-standard-20250522-amd64.hybrid.iso/download" # Verify download succeeded if [ -f "$BASE_ISO" ] && [ $(stat -f%z "$BASE_ISO" 2>/dev/null || stat -c%s "$BASE_ISO" 2>/dev/null) -gt 300000000 ]; then ISO_SIZE=$(du -h "$BASE_ISO" | awk '{print $1}') echo "" echo "✅ Downloaded Debian Live ISO ($ISO_SIZE)" echo " 📝 Cached at: $BASE_ISO" else echo "" echo "❌ Download failed or incomplete" exit 1 fi else ISO_SIZE=$(du -h "$BASE_ISO" | awk '{print $1}') echo "✅ Using cached Debian Live ISO ($ISO_SIZE)" echo " 📁 Location: $BASE_ISO" fi # Extract ISO echo "" echo "📦 Extracting Debian Live ISO..." ISO_CUSTOM="$WORK_DIR/custom" rm -rf "$ISO_CUSTOM" mkdir -p "$ISO_CUSTOM" cd "$ISO_CUSTOM" 7z x -y "$BASE_ISO" >/dev/null 2>&1 || 7z x -y "$BASE_ISO" echo "✅ Extracted ISO" # Add Archipelago files echo "" echo "📋 Adding Archipelago files..." ARCHIPELAGO_DIR="$ISO_CUSTOM/archipelago" mkdir -p "$ARCHIPELAGO_DIR" mkdir -p "$ARCHIPELAGO_DIR/bin" mkdir -p "$ARCHIPELAGO_DIR/scripts" # Copy the pre-built backend if it exists if [ -f "$SCRIPT_DIR/build/backend/archipelago" ]; then echo "🦀 Including Archipelago backend from build/backend..." cp "$SCRIPT_DIR/build/backend/archipelago" "$ARCHIPELAGO_DIR/bin/" chmod +x "$ARCHIPELAGO_DIR/bin/archipelago" elif [ -d "$SCRIPT_DIR/../core/target/release" ]; then echo "🦀 Including Archipelago backend from target/release..." cp "$SCRIPT_DIR/../core/target/release/archipelago" "$ARCHIPELAGO_DIR/bin/" 2>/dev/null || true chmod +x "$ARCHIPELAGO_DIR/bin/archipelago" 2>/dev/null || true fi # Copy the frontend build if it exists if [ -d "$SCRIPT_DIR/build/frontend" ]; then echo "🎨 Including Archipelago Web UI from build/frontend..." cp -r "$SCRIPT_DIR/build/frontend" "$ARCHIPELAGO_DIR/web-ui" elif [ -d "$SCRIPT_DIR/../web/dist/neode-ui" ]; then echo "🎨 Including Archipelago Web UI from web/dist..." cp -r "$SCRIPT_DIR/../web/dist/neode-ui" "$ARCHIPELAGO_DIR/web-ui" elif [ -d "$SCRIPT_DIR/../neode-ui/dist" ]; then echo "🎨 Including Archipelago frontend from neode-ui/dist..." cp -r "$SCRIPT_DIR/../neode-ui/dist" "$ARCHIPELAGO_DIR/web-ui" 2>/dev/null || true fi # Copy app manifests if [ -d "$SCRIPT_DIR/../apps" ]; then echo "📦 Including app manifests..." cp -r "$SCRIPT_DIR/../apps" "$ARCHIPELAGO_DIR/apps" 2>/dev/null || true fi # Copy setup scripts if [ -d "$SCRIPT_DIR/archipelago-scripts" ]; then echo "📜 Including setup scripts..." cp "$SCRIPT_DIR/archipelago-scripts/"*.sh "$ARCHIPELAGO_DIR/scripts/" 2>/dev/null || true chmod +x "$ARCHIPELAGO_DIR/scripts/"*.sh fi # Create main setup script for Archipelago cat > "$ARCHIPELAGO_DIR/setup-archipelago.sh" <<'SETUP_EOF' #!/bin/bash # # Archipelago Setup Script for Debian Live # echo "" echo "╔═══════════════════════════════════════════════════════════╗" echo "║ 🏝️ ARCHIPELAGO BITCOIN NODE OS - Debian Edition ║" echo "╚═══════════════════════════════════════════════════════════╝" echo "" # Find the boot media BOOT_MEDIA="" for dev in /run/live/medium /lib/live/mount/medium /cdrom /media/cdrom; do if [ -d "$dev/archipelago" ]; then BOOT_MEDIA="$dev" break fi done if [ -z "$BOOT_MEDIA" ]; then echo "❌ Could not find Archipelago files on boot media" echo " Looking in: /run/live/medium, /lib/live/mount/medium, /cdrom" exit 1 fi echo "📍 Found Archipelago at: $BOOT_MEDIA/archipelago" echo "" # Copy files to system if [ -d "$BOOT_MEDIA/archipelago/bin" ]; then echo "📋 Installing Archipelago binaries..." sudo cp -r "$BOOT_MEDIA/archipelago/bin/"* /usr/local/bin/ 2>/dev/null || true fi if [ -d "$BOOT_MEDIA/archipelago/apps" ]; then echo "📋 Installing app manifests..." sudo mkdir -p /etc/archipelago sudo cp -r "$BOOT_MEDIA/archipelago/apps" /etc/archipelago/ 2>/dev/null || true fi if [ -d "$BOOT_MEDIA/archipelago/scripts" ]; then echo "📋 Installing setup scripts..." sudo mkdir -p /opt/archipelago sudo cp -r "$BOOT_MEDIA/archipelago/scripts" /opt/archipelago/ 2>/dev/null || true sudo chmod +x /opt/archipelago/scripts/*.sh fi echo "" echo "✅ Archipelago installed!" echo "" # Automatically launch the menu sleep 1 exec /opt/archipelago/scripts/archipelago-menu.sh SETUP_EOF chmod +x "$ARCHIPELAGO_DIR/setup-archipelago.sh" # Create auto-start script that runs on login cat > "$ARCHIPELAGO_DIR/auto-start.sh" <<'AUTOSTART_EOF' #!/bin/bash # # Archipelago Auto-Start - Runs on first login # # Find boot media BOOT_MEDIA="" for dev in /run/live/medium /lib/live/mount/medium /cdrom; do if [ -d "$dev/archipelago" ]; then BOOT_MEDIA="$dev" break fi done # Install essential tools on first boot (required for disk installer) if [ ! -f /tmp/.archipelago-tools-installed ]; then clear echo "" echo " ╔═══════════════════════════════════════════════════════════╗" echo " ║ 🏝️ ARCHIPELAGO - First Boot Setup ║" echo " ╚═══════════════════════════════════════════════════════════╝" echo "" echo " 📦 Installing required tools..." echo "" # Update and install essential tools sudo apt-get update -qq 2>/dev/null sudo apt-get install -y parted debootstrap dosfstools e2fsprogs 2>/dev/null echo " ✅ Tools installed" touch /tmp/.archipelago-tools-installed sleep 1 fi # Get IP address IP=$(hostname -I 2>/dev/null | awk '{print $1}') # Start web UI in background if not already running if [ -n "$BOOT_MEDIA" ] && ! pgrep -f "http.server" >/dev/null; then WEB_UI_DIR="$BOOT_MEDIA/archipelago/web-ui" if [ -d "$WEB_UI_DIR" ]; then cd "$WEB_UI_DIR" nohup python3 -m http.server 80 --bind 0.0.0.0 >/dev/null 2>&1 & fi fi clear echo "" echo " ╔═══════════════════════════════════════════════════════════╗" echo " ║ ║" echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║" echo " ║ ║" echo " ║ Your sovereign Bitcoin infrastructure ║" echo " ║ ║" echo " ╚═══════════════════════════════════════════════════════════╝" echo "" if [ -n "$IP" ]; then echo " ┌─────────────────────────────────────────────────────────────┐" echo " │ 🌐 Web UI: http://$IP " echo " └─────────────────────────────────────────────────────────────┘" echo "" fi echo " Type 'archipelago' to open the setup menu" echo "" # Check if already set up if [ -f ~/.archipelago-setup-done ]; then return 2>/dev/null || exit 0 fi # First time - run setup automatically if [ -n "$BOOT_MEDIA" ] && [ -f "$BOOT_MEDIA/archipelago/setup-archipelago.sh" ]; then echo "" read -p " Press Enter to continue to setup menu..." bash "$BOOT_MEDIA/archipelago/setup-archipelago.sh" fi AUTOSTART_EOF chmod +x "$ARCHIPELAGO_DIR/auto-start.sh" # Create a simple 'archipelago-menu' command wrapper (don't overwrite the backend binary!) cat > "$ARCHIPELAGO_DIR/bin/archipelago-menu" <<'CMD_EOF' #!/bin/bash # Archipelago menu command - launches the setup menu if [ -f /opt/archipelago/scripts/archipelago-menu.sh ]; then exec /opt/archipelago/scripts/archipelago-menu.sh else # Find on boot media for dev in /run/live/medium /lib/live/mount/medium /cdrom; do if [ -f "$dev/archipelago/scripts/archipelago-menu.sh" ]; then exec bash "$dev/archipelago/scripts/archipelago-menu.sh" fi done echo "Archipelago menu not found. Run setup first:" echo " sh /run/live/medium/archipelago/setup-archipelago.sh" fi CMD_EOF chmod +x "$ARCHIPELAGO_DIR/bin/archipelago-menu" # Verify the real backend binary is there if [ -f "$ARCHIPELAGO_DIR/bin/archipelago" ]; then echo " Backend binary: $(file "$ARCHIPELAGO_DIR/bin/archipelago" | grep -o 'ELF.*' || echo 'included')" fi # Create SSH auto-start script for live environment mkdir -p "$ISO_CUSTOM/etc/live/config.conf.d" cat > "$ISO_CUSTOM/etc/live/config.conf.d/archipelago.conf" <<'LIVE_CONF_EOF' # Archipelago live config LIVE_HOSTNAME="archipelago" LIVE_USER_DEFAULT_GROUPS="audio cdrom dip floppy video plugdev netdev powerdev scanner bluetooth sudo" LIVE_CONF_EOF # Create rc.local to start SSH on boot (works in live environment) mkdir -p "$ISO_CUSTOM/etc/rc.local.d" cat > "$ISO_CUSTOM/etc/rc.local.d/archipelago-ssh.sh" <<'RCLOCAL_EOF' #!/bin/bash # Enable SSH in live environment # Install and start SSH if not running if ! systemctl is-active --quiet ssh 2>/dev/null; then # Try to start SSH (may already be installed) systemctl start ssh 2>/dev/null || true # If SSH not installed, install it if ! command -v sshd >/dev/null 2>&1; then apt-get update -qq apt-get install -y openssh-server systemctl start ssh fi fi # Set password for user (live user is typically 'user' with empty password) echo "user:archipelago" | chpasswd 2>/dev/null || true echo "root:archipelago" | chpasswd 2>/dev/null || true # Allow password auth sed -i 's/#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config 2>/dev/null sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config 2>/dev/null systemctl restart ssh 2>/dev/null || true RCLOCAL_EOF chmod +x "$ISO_CUSTOM/etc/rc.local.d/archipelago-ssh.sh" # Also create a systemd service that runs early mkdir -p "$ISO_CUSTOM/etc/systemd/system" cat > "$ISO_CUSTOM/etc/systemd/system/archipelago-ssh.service" <<'SERVICE_EOF' [Unit] Description=Archipelago SSH Setup After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/bin/bash -c 'apt-get update -qq && apt-get install -y openssh-server && echo "user:archipelago" | chpasswd && echo "root:archipelago" | chpasswd && sed -i "s/#PasswordAuthentication.*/PasswordAuthentication yes/" /etc/ssh/sshd_config && sed -i "s/PasswordAuthentication no/PasswordAuthentication yes/" /etc/ssh/sshd_config && systemctl restart ssh' RemainAfterExit=yes [Install] WantedBy=multi-user.target SERVICE_EOF # Enable the service mkdir -p "$ISO_CUSTOM/etc/systemd/system/multi-user.target.wants" ln -sf ../archipelago-ssh.service "$ISO_CUSTOM/etc/systemd/system/multi-user.target.wants/archipelago-ssh.service" # Create system-wide profile script that runs on ANY login mkdir -p "$ISO_CUSTOM/etc/profile.d" cat > "$ISO_CUSTOM/etc/profile.d/z99-archipelago.sh" <<'PROFILE_EOF' #!/bin/bash # Archipelago auto-start - runs on login (z99 = runs last) # Only run once per session if [ -n "$ARCHIPELAGO_STARTED" ]; then return 0 2>/dev/null || exit 0 fi export ARCHIPELAGO_STARTED=1 # Find archipelago directory BOOT_MEDIA="" for dev in /run/live/medium /lib/live/mount/medium /cdrom; do if [ -d "$dev/archipelago" ]; then BOOT_MEDIA="$dev" break fi done # Get IP address IP=$(hostname -I 2>/dev/null | awk '{print $1}') [ -z "$IP" ] && IP=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -1) # Show welcome banner ALWAYS clear echo "" echo " ╔═══════════════════════════════════════════════════════════╗" echo " ║ ║" echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║" echo " ║ ║" echo " ║ Your sovereign Bitcoin infrastructure ║" echo " ║ ║" echo " ╚═══════════════════════════════════════════════════════════╝" echo "" if [ -n "$IP" ]; then echo " ┌─────────────────────────────────────────────────────────────┐" echo " │ 🌐 Web UI: http://$IP:5678 │" echo " │ 📡 SSH: ssh user@$IP (password: archipelago) │" echo " └─────────────────────────────────────────────────────────────┘" echo "" fi if [ -z "$BOOT_MEDIA" ]; then echo " ⚠️ Boot media not found at /run/live/medium" echo "" echo " Manual commands:" echo " archipelago - Start backend server" echo " archipelago-menu - Open setup menu" echo "" return 0 2>/dev/null || exit 0 fi echo " 📍 Boot media: $BOOT_MEDIA" echo "" # Install archipelago commands if not present if [ ! -f /usr/local/bin/archipelago ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago" ]; then sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago" /usr/local/bin/ 2>/dev/null sudo chmod +x /usr/local/bin/archipelago 2>/dev/null fi if [ ! -f /usr/local/bin/archipelago-menu ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago-menu" ]; then sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago-menu" /usr/local/bin/ 2>/dev/null sudo chmod +x /usr/local/bin/archipelago-menu 2>/dev/null fi # Copy scripts to /opt if [ ! -d /opt/archipelago/scripts ] && [ -d "$BOOT_MEDIA/archipelago/scripts" ]; then sudo mkdir -p /opt/archipelago sudo cp -r "$BOOT_MEDIA/archipelago/scripts" /opt/archipelago/ 2>/dev/null sudo chmod +x /opt/archipelago/scripts/*.sh 2>/dev/null fi # Start web backend in background if available if command -v archipelago >/dev/null 2>&1; then if ! pgrep -f "archipelago" >/dev/null 2>&1; then echo " 🚀 Starting Archipelago backend on port 5678..." nohup archipelago >/tmp/archipelago.log 2>&1 & sleep 2 else echo " ✅ Archipelago backend already running" fi fi echo "" echo " Commands:" echo " archipelago-menu - Open interactive setup menu" echo " archipelago - Start/restart backend server" echo "" sleep 1 # Launch the menu automatically if [ -f /opt/archipelago/scripts/archipelago-menu.sh ]; then exec bash /opt/archipelago/scripts/archipelago-menu.sh elif [ -f "$BOOT_MEDIA/archipelago/scripts/archipelago-menu.sh" ]; then exec bash "$BOOT_MEDIA/archipelago/scripts/archipelago-menu.sh" fi PROFILE_EOF chmod +x "$ISO_CUSTOM/etc/profile.d/z99-archipelago.sh" # Also add to /etc/skel/.bashrc as fallback for new user sessions mkdir -p "$ISO_CUSTOM/etc/skel" cat >> "$ISO_CUSTOM/etc/skel/.bashrc" <<'BASHRC_EOF' # Archipelago auto-start if [ -z "$ARCHIPELAGO_STARTED" ] && [ -n "$PS1" ]; then export ARCHIPELAGO_STARTED=1 # Find boot media for dev in /run/live/medium /lib/live/mount/medium /cdrom; do if [ -d "$dev/archipelago" ]; then bash "$dev/archipelago/setup-archipelago.sh" 2>/dev/null || true break fi done fi BASHRC_EOF # CRITICAL: Make getty run our script directly after auto-login mkdir -p "$ISO_CUSTOM/etc/systemd/system/getty@tty1.service.d" cat > "$ISO_CUSTOM/etc/systemd/system/getty@tty1.service.d/override.conf" <<'GETTY_EOF' [Service] ExecStart= ExecStart=-/sbin/agetty --autologin user --login-program /bin/bash --login-options "-c 'source /etc/profile.d/z99-archipelago.sh; exec bash -i'" --noclear %I $TERM Type=idle GETTY_EOF # Create a live-config hook that adds our script to the actual user's .bashrc after user creation mkdir -p "$ISO_CUSTOM/lib/live/config" cat > "$ISO_CUSTOM/lib/live/config/9999-archipelago-autostart" <<'LIVECONFIG_EOF' #!/bin/bash # Live-config hook to add Archipelago auto-start to live user's bashrc set -e # This runs after the live user is created if [ -d /home/user ]; then cat >> /home/user/.bashrc <<'EOF' # Archipelago Auto-Start if [ -z "$ARCHIPELAGO_STARTED" ] && [ -n "$PS1" ]; then export ARCHIPELAGO_STARTED=1 # Find boot media BOOT_MEDIA="" for dev in /run/live/medium /lib/live/mount/medium /cdrom; do if [ -d "$dev/archipelago" ]; then BOOT_MEDIA="$dev" break fi done if [ -n "$BOOT_MEDIA" ]; then # Source the profile script if [ -f /etc/profile.d/z99-archipelago.sh ]; then bash /etc/profile.d/z99-archipelago.sh fi fi fi EOF chown user:user /home/user/.bashrc fi LIVECONFIG_EOF chmod +x "$ISO_CUSTOM/lib/live/config/9999-archipelago-autostart" cat > "$ISO_CUSTOM/etc/systemd/system/getty@tty1.service.d/override.conf" <<'GETTY_EOF' [Service] ExecStart= ExecStart=-/sbin/agetty --autologin user --noclear %I $TERM Type=idle GETTY_EOF # Modify GRUB config for Archipelago branding and auto-start echo "" echo "⚙️ Configuring boot..." if [ -f "$ISO_CUSTOM/boot/grub/grub.cfg" ]; then # Update branding sed -i.bak \ -e 's/Debian GNU\/Linux/Archipelago Bitcoin Node OS/g' \ -e 's/Live system/Archipelago Live/g' \ "$ISO_CUSTOM/boot/grub/grub.cfg" # Add components=live.persist boot parameter to enable persistence hooks # This will allow our scripts to run on boot sed -i '' -e 's/\(boot=live\)/\1 components=/' "$ISO_CUSTOM/boot/grub/grub.cfg" 2>/dev/null || true fi if [ -f "$ISO_CUSTOM/isolinux/menu.cfg" ]; then sed -i.bak \ -e 's/Debian GNU\/Linux/Archipelago Bitcoin Node OS/g' \ -e 's/Live system/Archipelago Live/g' \ "$ISO_CUSTOM/isolinux/menu.cfg" fi if [ -f "$ISO_CUSTOM/isolinux/live.cfg" ]; then sed -i.bak \ -e 's/Live system/Archipelago Live/g' \ "$ISO_CUSTOM/isolinux/live.cfg" fi # Create final ISO OUTPUT_ISO="$OUTPUT_DIR/archipelago-debian-12-x86_64.iso" echo "" echo "🔥 Creating final bootable ISO..." if command -v xorriso >/dev/null 2>&1; then # Create proper hybrid ISO with MBR for USB boot # Need to extract MBR from isolinux or use system syslinux # Check for isohdpfx.bin in various locations ISOHDPFX="" for path in \ "/usr/local/share/syslinux/isohdpfx.bin" \ "/usr/share/syslinux/isohdpfx.bin" \ "/opt/homebrew/share/syslinux/isohdpfx.bin" \ "$ISO_CUSTOM/isolinux/isohdpfx.bin"; do if [ -f "$path" ]; then ISOHDPFX="$path" break fi done if [ -z "$ISOHDPFX" ]; then echo "⚠️ No isohdpfx.bin found, extracting from isolinux.bin..." # Extract first 432 bytes from isolinux.bin as MBR dd if="$ISO_CUSTOM/isolinux/isolinux.bin" of="$WORK_DIR/isohdpfx.bin" bs=432 count=1 2>/dev/null ISOHDPFX="$WORK_DIR/isohdpfx.bin" fi echo " Using MBR: $ISOHDPFX" xorriso -as mkisofs -o "$OUTPUT_ISO" \ -volid "ARCHIPELAGO" \ -J -R \ -isohybrid-mbr "$ISOHDPFX" \ -c isolinux/boot.cat \ -b isolinux/isolinux.bin \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -eltorito-alt-boot \ -e boot/grub/efi.img \ -no-emul-boot \ -isohybrid-gpt-basdat \ "$ISO_CUSTOM" else echo "❌ xorriso not found. Please install it: brew install xorriso" exit 1 fi echo "" echo "✅ ISO created successfully!" echo "" # Calculate build time BUILD_END=$(date +%s) BUILD_DURATION=$((BUILD_END - BUILD_START)) BUILD_MINUTES=$((BUILD_DURATION / 60)) BUILD_SECONDS=$((BUILD_DURATION % 60)) ISO_SIZE=$(du -h "$OUTPUT_ISO" | cut -f1) ISO_MD5=$(md5 -q "$OUTPUT_ISO" 2>/dev/null || md5sum "$OUTPUT_ISO" | awk '{print $1}') echo "╔════════════════════════════════════════════════════════╗" echo "║ 🎉 Build Complete! ║" echo "╚════════════════════════════════════════════════════════╝" echo "" echo "📀 ISO File: $OUTPUT_ISO" echo "📏 Size: $ISO_SIZE" echo "🔐 MD5: $ISO_MD5" echo "⏱️ Build Time: ${BUILD_MINUTES}m ${BUILD_SECONDS}s" echo "🎯 Base: Debian 12 Live (Bookworm)" echo "" echo "🔥 Next Steps:" echo "" echo " 1. Flash to USB:" echo " cd image-recipe && ./write-usb-dd.sh /dev/diskN" echo "" echo " 2. Boot on target device" echo "" echo " 3. Auto-login as 'user' with menu launch" echo "" echo " 4. Access Web UI at http://:5678" echo "" echo " 5. SSH access: ssh user@ (password: archipelago)" echo ""