feat: container orchestration, branding overhaul, onboarding logging

Container orchestration:
- Health monitor with crash recovery and auto-restart
- Doctor service (periodic health checks via systemd timer)
- Reconcile service (desired-state convergence)
- Stack-aware install/uninstall with dependency tracking

Branding:
- Custom GRUB background (designer artwork, 1024x768)
- ISOLINUX boot menu: centered, orange accents, clean labels
- Terminal banners: adaptive width, basic ANSI colors, fits 80-col
- Removed auto-generated splash scripts (designer provides assets)
- GRUB theme: lowercase branding

Frontend:
- 401 handler clears localStorage immediately (prevents cascade)

Backend:
- Onboarding/auth logging ([onboarding] tag in journalctl)
- Cookie Secure flag logging for debugging HTTP/HTTPS issues

ISO fixes:
- Install log saved before unmount (was silently failing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-28 11:31:48 +00:00
parent 6311aa563e
commit 99400a7165
22 changed files with 626 additions and 763 deletions

View File

@@ -324,6 +324,10 @@ RUN mkdir -p /etc/archipelago/ssl && \
COPY archipelago.service /etc/systemd/system/archipelago.service
COPY archipelago-update.service /etc/systemd/system/archipelago-update.service
COPY archipelago-update.timer /etc/systemd/system/archipelago-update.timer
COPY archipelago-doctor.service /etc/systemd/system/archipelago-doctor.service
COPY archipelago-doctor.timer /etc/systemd/system/archipelago-doctor.timer
COPY archipelago-reconcile.service /etc/systemd/system/archipelago-reconcile.service
COPY archipelago-reconcile.timer /etc/systemd/system/archipelago-reconcile.timer
# Enable services
RUN systemctl enable NetworkManager || true && \
@@ -333,7 +337,9 @@ RUN systemctl enable NetworkManager || true && \
systemctl enable tor || true && \
systemctl enable tailscaled || true && \
systemctl enable chrony || true && \
systemctl enable archipelago-update.timer || true
systemctl enable archipelago-update.timer || true && \
systemctl enable archipelago-doctor.timer || true && \
systemctl enable archipelago-reconcile.timer || true
# Remove policy-rc.d so services can start on first boot
RUN rm -f /usr/sbin/policy-rc.d
@@ -393,6 +399,15 @@ NGINXCONF
echo " Using archipelago-update.service + timer from configs/"
fi
# Copy container doctor and reconciliation timers
if [ -f "$SCRIPT_DIR/configs/archipelago-doctor.service" ]; then
cp "$SCRIPT_DIR/configs/archipelago-doctor.service" "$WORK_DIR/archipelago-doctor.service"
cp "$SCRIPT_DIR/configs/archipelago-doctor.timer" "$WORK_DIR/archipelago-doctor.timer"
cp "$SCRIPT_DIR/configs/archipelago-reconcile.service" "$WORK_DIR/archipelago-reconcile.service"
cp "$SCRIPT_DIR/configs/archipelago-reconcile.timer" "$WORK_DIR/archipelago-reconcile.timer"
echo " Using container doctor + reconcile timers from configs/"
fi
# Use archipelago.service from configs/ (User=root for Podman container access)
if [ -f "$SCRIPT_DIR/configs/archipelago.service" ]; then
cp "$SCRIPT_DIR/configs/archipelago.service" "$WORK_DIR/archipelago.service"
@@ -519,11 +534,23 @@ if [ -n "\$INSTALLER_STARTED" ]; then
fi
export INSTALLER_STARTED=1
# Colors
O=\$'\\033[1;33m' # Bold yellow (orange accent)
W=\$'\\033[1;37m' # Bold white
D=\$'\\033[37m' # Dim
N=\$'\\033[0m' # Reset
# Center-print
TW=\$(tput cols 2>/dev/null || echo 60)
[ "\$TW" -gt 120 ] && TW=120
cc() { local s=\$(echo -e "\$1" | sed 's/\\x1b\\[[0-9;]*m//g'); local p=\$(( (TW - \${#s}) / 2 )); [ \$p -lt 0 ] && p=0; printf "%*s" "\$p" ""; echo -e "\$1"; }
sleep 1
clear
echo ""
echo " ARCHIPELAGO BITCOIN NODE OS"
echo " Automatic Installer"
cc "\${W}a r c h i p e l a g o\${N}"
cc "\${O}━━━━━━━━━━━━━━━━━━━━━\${N}"
cc "\${D}Automatic Installer\${N}"
echo ""
BOOT_MEDIA=""
@@ -535,17 +562,14 @@ for dev in /run/live/medium /lib/live/mount/medium /run/archiso /cdrom /media/cd
done
if [ -n "\$BOOT_MEDIA" ]; then
echo " Found installer at: \$BOOT_MEDIA"
cc "\${D}Found installer at: \$BOOT_MEDIA\${N}"
echo ""
echo " Press Enter to start installation, or Ctrl+C for shell..."
read
cc "Press Enter to install | \${W}Ctrl+C\${N} for shell"
read -s
bash "\$BOOT_MEDIA/archipelago/auto-install.sh"
else
echo " Installer not found on boot media."
echo " Checked: /run/live/medium, /run/archiso, /cdrom, /media/cdrom"
echo ""
echo " You can try manually:"
echo " sudo bash /path/to/archipelago/auto-install.sh"
cc "\${D}Installer not found on boot media.\${N}"
cc "\${D}Try: sudo bash /path/to/archipelago/auto-install.sh\${N}"
echo ""
fi
PROFILE
@@ -1349,26 +1373,62 @@ case "$(uname -m)" in
;;
esac
# Colors (use $'...' syntax for reliable escape code interpretation)
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
# Colors (basic ANSI — works on bare-metal Linux console)
RED=$'\033[31m'
GREEN=$'\033[32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'
ORANGE=$'\033[1;33m'
DIM=$'\033[37m'
CYAN=$'\033[36m'
WHITE=$'\033[1;37m'
NC=$'\033[0m'
# Adaptive centering
TW=$(tput cols 2>/dev/null || echo 60)
[ "$TW" -gt 120 ] && TW=120
cc() { local s=$(echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g'); local p=$(( (TW - ${#s}) / 2 )); [ $p -lt 0 ] && p=0; printf "%*s" "$p" ""; echo -e "$1"; }
hrule() { local len=$((TW > 50 ? 50 : TW - 4)); local hr=""; for i in $(seq 1 $len); do hr="${hr}─"; done; cc "${DIM}${hr}${NC}"; }
box() {
local bw=$((TW > 52 ? 52 : TW - 4))
local inner=$((bw - 2))
local top="╭"; local bot="╰"
for i in $(seq 1 $inner); do top="${top}─"; bot="${bot}─"; done
top="${top}╮"; bot="${bot}╯"
cc "${DIM}${top}${NC}"
}
boxend() {
local bw=$((TW > 52 ? 52 : TW - 4))
local inner=$((bw - 2))
local bot="╰"
for i in $(seq 1 $inner); do bot="${bot}─"; done
bot="${bot}╯"
cc "${DIM}${bot}${NC}"
}
boxline() {
local bw=$((TW > 52 ? 52 : TW - 4))
local inner=$((bw - 2))
local stripped=$(echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g')
local pad=$((inner - ${#stripped}))
[ $pad -lt 0 ] && pad=0
local right=""
for i in $(seq 1 $pad); do right="${right} "; done
cc "${DIM}│${NC} $1${right}${DIM}│${NC}"
}
clear
echo ""
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ ║${NC}"
echo -e "${BLUE}║ ${GREEN}🏝️ ARCHIPELAGO BITCOIN NODE OS${BLUE} ║${NC}"
echo -e "${BLUE}║ ║${NC}"
echo -e "${BLUE}║ ${NC}Automatic Installation${BLUE} ║${NC}"
echo -e "${BLUE}║ ║${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════════╝${NC}"
box
boxline ""
boxline "${WHITE}A R C H I P E L A G O${NC}"
boxline "${ORANGE}━━━━━━━━━━━━━━━━━━━━━${NC}"
boxline "${DIM}Automatic Installation${NC}"
boxline ""
boxend
echo ""
# Check required tools are present (should be bundled in ISO)
echo -e "${YELLOW}🔧 Checking installer tools...${NC}"
cc "${DIM}Checking installer tools...${NC}"
MISSING=""
command -v parted >/dev/null 2>&1 || MISSING="parted $MISSING"
command -v mkfs.vfat >/dev/null 2>&1 || MISSING="mkfs.vfat $MISSING"
@@ -1467,15 +1527,15 @@ if [ -z "$TARGET_DISK" ]; then
fi
echo ""
echo -e "${GREEN}✅ Target disk: $TARGET_DISK ($TARGET_SIZE)${NC}"
cc "${GREEN}target: ${WHITE}$TARGET_DISK ($TARGET_SIZE)${NC}"
echo ""
echo -e "${RED}⚠️ WARNING: ALL DATA ON $TARGET_DISK WILL BE ERASED${NC}"
cc "${RED}all data on $TARGET_DISK will be erased${NC}"
echo ""
echo "Press Enter to install Archipelago, or Ctrl+C to cancel..."
read
cc "${DIM}Press Enter to install | Ctrl+C to cancel${NC}"
read -s
echo ""
echo -e "${YELLOW}🔧 Installing Archipelago...${NC}"
cc "${DIM}Installing Archipelago...${NC}"
echo ""
# Unmount any existing partitions
@@ -2402,6 +2462,9 @@ date=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
type=unbundled
BUILDINFO
# Save install log BEFORE unmounting target
cp "$INSTALL_LOG" /mnt/target/var/log/archipelago-install.log 2>/dev/null || true
# Cleanup
sync
umount /mnt/target/run 2>/dev/null || true
@@ -2415,63 +2478,34 @@ cryptsetup close archipelago-data 2>/dev/null || true
umount /mnt/target 2>/dev/null || true
echo ""
echo -e "${GREEN} _${NC}"
echo -e "${GREEN} ,--.\\\`-. __${NC}"
echo -e "${GREEN} _,.\\\`. \\:/,\" \\\`-._${NC}"
echo -e "${GREEN} ,-*\" _,.-;-*\\\`-.+\"*._ )${NC}"
echo -e "${GREEN} ( ,.\"* ,-\" / \\\`. \\\\. \\\`.${NC}"
echo -e "${GREEN} ,\" ,;\" ,\"\\../\\ \\: \\${NC}"
echo -e "${GREEN} ( ,\"/ / \\\\.,' : )) /${NC}"
echo -e "${GREEN} \\ |/ / \\\\.,' / // ,'${NC}"
echo -e "${GREEN} \\_)\\ ,' \\\\.,' ( / )/${NC}"
echo -e "${GREEN} \\\` \\._,' \\\`\"${NC}"
echo -e "${GREEN} \\../${NC}"
echo -e "${GREEN} \\../${NC}"
echo -e "${GREEN} ~ ~\\../ ~~ ~~${NC}"
echo -e "${GREEN} ~~ ~~ \\../ ~~ ~ ~~${NC}"
echo -e "${GREEN} ~~ ~ ~~ __...---\\../-...__ ~~~ ~~${NC}"
echo -e "${GREEN} ~~~~ ~_,--' \\../ \\\`--.__ ~~ ~~${NC}"
echo -e "${GREEN} ~~~ __,--' \\\`\" \\\`--.__ ~~~${NC}"
echo -e "${GREEN}~~ ,--' \\\`--.${NC}"
echo -e "${GREEN} '------......______ ______......------\\\` ~~${NC}"
echo -e "${GREEN} ~~~ ~ ~~ ~ \\\`\\\`\\\`\\\`\\\`---\"\"\"\"\" ~~ ~ ~~${NC}"
echo -e "${GREEN} ~~~~ ~~ ~~~~ ~~~~~~ ~ ~~ ~~ ~~~ ~${NC}"
echo ""
echo -e "${GREEN} █████╗ ██████╗ ██████╗██╗ ██╗██╗██████╗ ███████╗██╗ █████╗ ██████╗ ██████╗ ${NC}"
echo -e "${GREEN} ██╔══██╗██╔══██╗██╔════╝██║ ██║██║██╔══██╗██╔════╝██║ ██╔══██╗██╔════╝ ██╔═══██╗${NC}"
echo -e "${GREEN} ███████║██████╔╝██║ ███████║██║██████╔╝█████╗ ██║ ███████║██║ ███╗██║ ██║${NC}"
echo -e "${GREEN} ██╔══██║██╔══██╗██║ ██╔══██║██║██╔═══╝ ██╔══╝ ██║ ██╔══██║██║ ██║██║ ██║${NC}"
echo -e "${GREEN} ██║ ██║██║ ██║╚██████╗██║ ██║██║██║ ███████╗███████╗██║ ██║╚██████╔╝╚██████╔╝${NC}"
echo -e "${GREEN} ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ${NC}"
box
boxline ""
boxline "${WHITE}A R C H I P E L A G O${NC}"
boxline "${GREEN}━━━━━━━━━━━━━━━━━━━━━${NC}"
boxline "${GREEN}Installation Complete${NC}"
boxline ""
boxend
echo ""
echo -e "${GREEN} 🏝️ BITCOIN NODE OS 🏝️${NC}"
cc "${DIM}After reboot, open the Web UI from any device on your network.${NC}"
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ✅ INSTALLATION COMPLETE! ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ Remove the USB drive and press Enter to reboot. ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ After reboot: ║${NC}"
echo -e "${GREEN}║ • Web UI: http://<IP> ║${NC}"
echo -e "${GREEN}║ • SSH: ssh archipelago@<IP> ║${NC}"
echo -e "${GREEN}║ • SSH Password: archipelago ║${NC}"
echo -e "${GREEN}║ • Web Password: password123 ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ Pre-loaded apps (ready to start via Web UI): ║${NC}"
echo -e "${GREEN}║ • Bitcoin Knots • LND • Home Assistant ║${NC}"
echo -e "${GREEN}║ • BTCPay Server • Mempool • Nostr Relays ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ Validate: bash /opt/archipelago/scripts/run-e2e-tests.sh ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════════╝${NC}"
cc "${DIM}Web UI:${NC} ${WHITE}http://<this machine's IP>${NC}"
cc "${DIM}SSH:${NC} ${DIM}ssh archipelago@<IP>${NC}"
cc "${DIM}Password:${NC} ${DIM}archipelago${NC}"
cc "${DIM}Web Login:${NC} ${DIM}password123${NC}"
echo ""
cc "${DIM}Pre-loaded apps (start via Web UI):${NC}"
cc "${DIM}Bitcoin Knots, LND, Home Assistant,${NC}"
cc "${DIM}BTCPay Server, Mempool, Nostr Relay${NC}"
echo ""
echo -e "${YELLOW} >>> REMOVE THE USB DRIVE NOW <<<${NC}"
hrule
echo ""
# Save install log to target disk for post-install debugging
cp "$INSTALL_LOG" /mnt/target/var/log/archipelago-install.log 2>/dev/null || true
cc "${YELLOW}>>> REMOVE THE USB DRIVE NOW <<<${NC}"
echo ""
# Install log already saved before unmount (above)
read -p "Press Enter to reboot (make sure USB is removed)..."
cc "${DIM}Press Enter to reboot${NC}"
read -s
# Suppress all error output during cleanup and reboot
exec 2>/dev/null
@@ -2590,22 +2624,25 @@ UI vesamenu.c32
PROMPT 0
TIMEOUT 0
MENU TITLE ARCHIPELAGO - Bitcoin Node OS
MENU TITLE Bitcoin Node OS
MENU BACKGROUND splash.png
MENU RESOLUTION 1024 768
MENU VSHIFT 14
MENU HSHIFT 6
MENU WIDTH 40
MENU VSHIFT 15
MENU HSHIFT 28
MENU WIDTH 26
MENU MARGIN 2
MENU ROWS 5
MENU TABMSG Press TAB to edit | https://archipelago.sh
MENU COLOR screen 37;40 #00000000 #00000000 none
MENU COLOR border 30;40 #00000000 #00000000 none
MENU COLOR title 1;37;40 #fffb923c #00000000 none
MENU COLOR sel 7;37;40 #ffffffff #80333333 std
MENU COLOR unsel 37;40 #ff999999 #00000000 none
MENU COLOR title 1;37;40 #80888888 #00000000 none
MENU COLOR sel 7;37;40 #ffffffff #c0181818 std
MENU COLOR unsel 37;40 #ffaaaaaa #00000000 none
MENU COLOR hotkey 1;37;40 #fffb923c #00000000 none
MENU COLOR hotsel 1;37;40 #fffb923c #80333333 std
MENU COLOR timeout_msg 37;40 #ff666666 #00000000 none
MENU COLOR hotsel 1;37;40 #fffb923c #c0181818 std
MENU COLOR timeout_msg 37;40 #ff555555 #00000000 none
MENU COLOR timeout 1;37;40 #fffb923c #00000000 none
MENU COLOR tabmsg 37;40 #ff666666 #00000000 none
MENU COLOR tabmsg 37;40 #ff444444 #00000000 none
MENU COLOR cmdmark 37;40 #00000000 #00000000 none
MENU COLOR cmdline 37;40 #00000000 #00000000 none
@@ -2618,7 +2655,7 @@ LABEL install
MENU DEFAULT
LABEL install-verbose
MENU LABEL Install Archipelago (verbose)
MENU LABEL Install (verbose output)
KERNEL /live/vmlinuz
APPEND initrd=/live/initrd.img boot=live components