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:
@@ -1,18 +1,39 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Archipelago Main Menu
|
||||
# Interactive setup wizard for Archipelago Bitcoin Node OS
|
||||
# archipelago main menu
|
||||
# interactive setup for archipelago bitcoin node os
|
||||
#
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# Colors (256-color — works on Linux console with fbcon)
|
||||
O=$'\033[38;5;208m' # Orange
|
||||
W=$'\033[1;37m' # Bold white
|
||||
D=$'\033[38;5;242m' # Dim
|
||||
C=$'\033[38;5;37m' # Cyan
|
||||
G=$'\033[38;5;35m' # Green
|
||||
R=$'\033[38;5;196m' # Red
|
||||
Y=$'\033[38;5;220m' # Yellow
|
||||
N=$'\033[0m' # Reset
|
||||
|
||||
# Adaptive centering
|
||||
get_width() { TW=$(tput cols 2>/dev/null || echo 60); [ "$TW" -gt 120 ] && TW=120; }
|
||||
get_width
|
||||
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"; }
|
||||
|
||||
# Box helpers (Claude-style rounded corners)
|
||||
bw() { echo $((TW > 52 ? 52 : TW - 4)); }
|
||||
btop() { local w=$(bw); local t="╭"; for i in $(seq 1 $((w-2))); do t="${t}─"; done; cc "${D}${t}╮${N}"; }
|
||||
bbox() { local w=$(bw); local s=$(echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g'); local pad=$((w - 2 - ${#s})); [ $pad -lt 0 ] && pad=0; local r=""; for i in $(seq 1 $pad); do r="${r} "; done; cc "${D}│${N} $1${r}${D}│${N}"; }
|
||||
bbot() { local w=$(bw); local b="╰"; for i in $(seq 1 $((w-2))); do b="${b}─"; done; cc "${D}${b}╯${N}"; }
|
||||
hrule() { local len=$((TW > 50 ? 50 : TW - 4)); local hr=""; for i in $(seq 1 $len); do hr="${hr}─"; done; cc "${D}${hr}${N}"; }
|
||||
|
||||
# Install required tools on first run (for live mode)
|
||||
install_required_tools() {
|
||||
if [ -f /tmp/.archipelago-tools-installed ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if we need to install tools
|
||||
|
||||
local NEED_TOOLS=0
|
||||
for tool in parted debootstrap mkfs.ext4 mkfs.vfat; do
|
||||
if ! command -v $tool >/dev/null 2>&1; then
|
||||
@@ -20,74 +41,61 @@ install_required_tools() {
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if [ $NEED_TOOLS -eq 1 ]; then
|
||||
echo ""
|
||||
echo " 📦 Installing required tools (first run)..."
|
||||
cc "${D}installing required tools...${N}"
|
||||
echo ""
|
||||
sudo apt-get update -qq 2>/dev/null
|
||||
sudo apt-get install -y parted debootstrap dosfstools e2fsprogs 2>/dev/null
|
||||
echo " ✅ Tools installed"
|
||||
cc "${G}tools installed${N}"
|
||||
echo ""
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
|
||||
touch /tmp/.archipelago-tools-installed
|
||||
}
|
||||
|
||||
# Run tool installation at startup
|
||||
install_required_tools
|
||||
|
||||
show_banner() {
|
||||
get_width
|
||||
clear
|
||||
echo ""
|
||||
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||
echo " ║ ║"
|
||||
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║"
|
||||
echo " ║ ║"
|
||||
echo " ║ Your sovereign Bitcoin infrastructure ║"
|
||||
echo " ║ ║"
|
||||
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||
btop
|
||||
bbox ""
|
||||
bbox "${W}a r c h i p e l a g o${N}"
|
||||
bbox "${O}━━━━━━━━━━━━━━━━━━━━━${N}"
|
||||
bbox "${D}bitcoin node os${N}"
|
||||
bbox ""
|
||||
bbot
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_status() {
|
||||
echo " System Status:"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
|
||||
# Check if we're in live mode
|
||||
if [ -d /run/live ]; then
|
||||
echo " Mode: 🔴 Live (changes won't persist)"
|
||||
cc "${R}live mode${N} ${D}(changes won't persist)${N}"
|
||||
else
|
||||
echo " Mode: 🟢 Installed"
|
||||
cc "${G}installed${N}"
|
||||
fi
|
||||
|
||||
# Check Podman
|
||||
if command -v podman >/dev/null 2>&1; then
|
||||
echo " Podman: 🟢 Installed"
|
||||
else
|
||||
echo " Podman: 🔴 Not installed"
|
||||
echo ""
|
||||
|
||||
local podman_ok=0
|
||||
command -v podman >/dev/null 2>&1 && podman_ok=1
|
||||
|
||||
if [ $podman_ok -eq 1 ] && podman ps 2>/dev/null | grep -q bitcoind; then
|
||||
local blocks=$(podman exec bitcoind bitcoin-cli getblockcount 2>/dev/null || echo "syncing")
|
||||
cc "${G}bitcoin${N} ${D}running ($blocks blocks)${N}"
|
||||
elif [ $podman_ok -eq 1 ] && podman ps -a 2>/dev/null | grep -q bitcoind; then
|
||||
cc "${Y}bitcoin${N} ${D}stopped${N}"
|
||||
fi
|
||||
|
||||
# Check Bitcoin Core
|
||||
if podman ps 2>/dev/null | grep -q bitcoind; then
|
||||
BLOCKS=$(podman exec bitcoind bitcoin-cli getblockcount 2>/dev/null || echo "syncing")
|
||||
echo " Bitcoin: 🟢 Running (blocks: $BLOCKS)"
|
||||
elif podman ps -a 2>/dev/null | grep -q bitcoind; then
|
||||
echo " Bitcoin: 🟡 Stopped"
|
||||
else
|
||||
echo " Bitcoin: ⚪ Not configured"
|
||||
|
||||
if [ $podman_ok -eq 1 ] && podman ps 2>/dev/null | grep -q lnd; then
|
||||
cc "${G}lightning${N} ${D}running${N}"
|
||||
elif [ $podman_ok -eq 1 ] && podman ps -a 2>/dev/null | grep -q lnd; then
|
||||
cc "${Y}lightning${N} ${D}stopped${N}"
|
||||
fi
|
||||
|
||||
# Check LND
|
||||
if podman ps 2>/dev/null | grep -q lnd; then
|
||||
echo " Lightning: 🟢 Running"
|
||||
elif podman ps -a 2>/dev/null | grep -q lnd; then
|
||||
echo " Lightning: 🟡 Stopped"
|
||||
else
|
||||
echo " Lightning: ⚪ Not configured"
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
@@ -95,126 +103,112 @@ main_menu() {
|
||||
while true; do
|
||||
show_banner
|
||||
show_status
|
||||
|
||||
# Show Web UI URL prominently
|
||||
|
||||
# Connection info
|
||||
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)
|
||||
|
||||
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||
|
||||
if [ -n "$IP" ]; then
|
||||
# Check if backend is running
|
||||
if pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||
echo " │ 🌐 Web UI: http://$IP:5678 (running) │"
|
||||
cc "${C}web ui${N} ${W}http://$IP${N}"
|
||||
else
|
||||
echo " │ 🌐 Web UI: http://$IP:5678 (not started) │"
|
||||
cc "${C}web ui${N} ${D}http://$IP${N} ${Y}(not started)${N}"
|
||||
fi
|
||||
echo " │ 📡 SSH: ssh user@$IP (password: archipelago) │"
|
||||
cc "${C}ssh${N} ${D}archipelago@$IP${N}"
|
||||
else
|
||||
echo " │ 🌐 Web UI: (no network) │"
|
||||
cc "${D}no network detected${N}"
|
||||
fi
|
||||
echo " └─────────────────────────────────────────────────────────────┘"
|
||||
|
||||
echo ""
|
||||
|
||||
echo " Main Menu:"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
hrule
|
||||
echo ""
|
||||
echo " r) Refresh - Update IP/status (no restart needed)"
|
||||
echo " w) Open Web UI - Launch graphical interface"
|
||||
cc "${D}r${N} refresh status ${D}w${N} start web ui"
|
||||
echo ""
|
||||
echo " 1) Install to Disk - Permanently install Archipelago"
|
||||
echo " 2) Setup Bitcoin Core - Configure Bitcoin full node"
|
||||
echo " 3) Setup Lightning (LND) - Configure Lightning Network"
|
||||
echo " 4) Setup BTCPay Server - Bitcoin payment processor"
|
||||
echo " 5) View Logs - Monitor running services"
|
||||
echo " 6) Network Settings - Configure networking"
|
||||
echo " 7) System Info - View system information"
|
||||
cc "${O}1${N} install to disk ${O}5${N} view logs"
|
||||
cc "${O}2${N} setup bitcoin core ${O}6${N} network settings"
|
||||
cc "${O}3${N} setup lightning ${O}7${N} system info"
|
||||
cc "${O}4${N} setup btcpay server"
|
||||
echo ""
|
||||
echo " q) Quit"
|
||||
cc "${D}q quit${N}"
|
||||
echo ""
|
||||
read -p " Select option: " choice
|
||||
|
||||
|
||||
local pad=$(( (TW - 18) / 2 ))
|
||||
[ $pad -lt 0 ] && pad=0
|
||||
printf "%*s" "$pad" ""
|
||||
read -p "select option: " choice
|
||||
|
||||
case $choice in
|
||||
r|R)
|
||||
# Refresh - just loop again to show updated IP/status
|
||||
;;
|
||||
w|W)
|
||||
echo ""
|
||||
# Start the real backend on port 5678
|
||||
if command -v archipelago >/dev/null 2>&1; then
|
||||
if pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||
echo " ✅ Archipelago backend already running on port 5678"
|
||||
cc "${G}backend already running${N}"
|
||||
else
|
||||
echo " 🚀 Starting Archipelago backend on port 5678..."
|
||||
cc "${D}starting backend on port 5678...${N}"
|
||||
nohup archipelago >/tmp/archipelago.log 2>&1 &
|
||||
sleep 2
|
||||
if pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||
echo " ✅ Backend started!"
|
||||
cc "${G}backend started${N}"
|
||||
else
|
||||
echo " ⚠️ Failed to start backend. Check /tmp/archipelago.log"
|
||||
cc "${R}failed — see /tmp/archipelago.log${N}"
|
||||
fi
|
||||
fi
|
||||
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
echo ""
|
||||
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||
echo " │ 🌐 Open in browser: http://$IP:5678 │"
|
||||
echo " └─────────────────────────────────────────────────────────────┘"
|
||||
cc "open in browser: ${W}http://$IP${N}"
|
||||
else
|
||||
echo " ⚠️ Archipelago binary not found at /usr/local/bin/archipelago"
|
||||
echo ""
|
||||
echo " Try running:"
|
||||
echo " sudo cp /run/live/medium/archipelago/bin/archipelago /usr/local/bin/"
|
||||
cc "${R}binary not found at /usr/local/bin/archipelago${N}"
|
||||
fi
|
||||
echo ""
|
||||
read -p " Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
1)
|
||||
if [ -f "$SCRIPT_DIR/install-to-disk.sh" ]; then
|
||||
sudo bash "$SCRIPT_DIR/install-to-disk.sh"
|
||||
else
|
||||
echo "Installer not found. Running from: $SCRIPT_DIR"
|
||||
echo " installer not found at: $SCRIPT_DIR"
|
||||
fi
|
||||
read -p "Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
2)
|
||||
if [ -f "$SCRIPT_DIR/setup-bitcoin.sh" ]; then
|
||||
bash "$SCRIPT_DIR/setup-bitcoin.sh"
|
||||
else
|
||||
echo "Bitcoin setup script not found."
|
||||
echo " bitcoin setup script not found."
|
||||
fi
|
||||
read -p "Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
3)
|
||||
if [ -f "$SCRIPT_DIR/setup-lnd.sh" ]; then
|
||||
bash "$SCRIPT_DIR/setup-lnd.sh"
|
||||
else
|
||||
echo "LND setup script not found."
|
||||
echo " lnd setup script not found."
|
||||
fi
|
||||
read -p "Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
4)
|
||||
setup_btcpay
|
||||
read -p "Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
5)
|
||||
view_logs
|
||||
;;
|
||||
6)
|
||||
network_settings
|
||||
read -p "Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
7)
|
||||
system_info
|
||||
read -p "Press Enter to continue..."
|
||||
read -sp " press enter to continue..."
|
||||
;;
|
||||
q|Q)
|
||||
echo ""
|
||||
echo " Goodbye! 🏝️"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option"
|
||||
sleep 1
|
||||
sleep 0.5
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -222,62 +216,63 @@ main_menu() {
|
||||
|
||||
setup_btcpay() {
|
||||
show_banner
|
||||
echo " BTCPay Server Setup"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
cc "${W}btcpay server setup${N}"
|
||||
cc "${D}self-hosted bitcoin payment processor${N}"
|
||||
echo ""
|
||||
echo " BTCPay Server is a self-hosted Bitcoin payment processor."
|
||||
echo ""
|
||||
|
||||
|
||||
if ! podman ps | grep -q bitcoind; then
|
||||
echo " ⚠️ Bitcoin Core must be running first."
|
||||
cc "${R}bitcoin core must be running first${N}"
|
||||
return
|
||||
fi
|
||||
|
||||
read -p " Setup BTCPay Server? [y/N]: " SETUP
|
||||
|
||||
local pad=$(( (TW - 30) / 2 ))
|
||||
[ $pad -lt 0 ] && pad=0
|
||||
printf "%*s" "$pad" ""
|
||||
read -p "setup btcpay server? [y/N]: " SETUP
|
||||
if [[ ! "$SETUP" =~ ^[Yy]$ ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo " 🐳 Pulling BTCPay Server image..."
|
||||
cc "${D}pulling btcpay server image...${N}"
|
||||
podman pull "${BTCPAY_IMAGE}"
|
||||
|
||||
# Create data directory
|
||||
mkdir -p ~/.btcpay
|
||||
|
||||
|
||||
echo ""
|
||||
echo " BTCPay Server setup is more complex and typically uses docker-compose."
|
||||
echo " For a full setup, visit: https://docs.btcpayserver.org"
|
||||
cc "${D}full setup: https://docs.btcpayserver.org${N}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
view_logs() {
|
||||
show_banner
|
||||
echo " View Logs"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
cc "${W}view logs${N}"
|
||||
echo ""
|
||||
echo " 1) Bitcoin Core logs"
|
||||
echo " 2) LND logs"
|
||||
echo " 3) System logs"
|
||||
echo " b) Back"
|
||||
cc "${O}1${N} ${D}bitcoin core${N}"
|
||||
cc "${O}2${N} ${D}lnd${N}"
|
||||
cc "${O}3${N} ${D}system journal${N}"
|
||||
cc "${D}b back${N}"
|
||||
echo ""
|
||||
read -p " Select: " choice
|
||||
|
||||
|
||||
local pad=$(( (TW - 10) / 2 ))
|
||||
[ $pad -lt 0 ] && pad=0
|
||||
printf "%*s" "$pad" ""
|
||||
read -p "select: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
if podman ps -a | grep -q bitcoind; then
|
||||
podman logs -f --tail 50 bitcoind
|
||||
else
|
||||
echo "Bitcoin Core not running"
|
||||
read -p "Press Enter..."
|
||||
cc "${D}bitcoin core not running${N}"
|
||||
read -sp " press enter..."
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
if podman ps -a | grep -q lnd; then
|
||||
podman logs -f --tail 50 lnd
|
||||
else
|
||||
echo "LND not running"
|
||||
read -p "Press Enter..."
|
||||
cc "${D}lnd not running${N}"
|
||||
read -sp " press enter..."
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
@@ -288,57 +283,61 @@ view_logs() {
|
||||
|
||||
network_settings() {
|
||||
show_banner
|
||||
echo " Network Settings"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
cc "${W}network settings${N}"
|
||||
echo ""
|
||||
|
||||
# Show current IP
|
||||
|
||||
IP=$(hostname -I | awk '{print $1}')
|
||||
echo " Current IP: $IP"
|
||||
cc "${C}ip${N} ${W}$IP${N}"
|
||||
echo ""
|
||||
|
||||
# Show network interfaces
|
||||
echo " Network Interfaces:"
|
||||
|
||||
cc "${D}interfaces:${N}"
|
||||
ip -br addr | grep -v "^lo" | while read line; do
|
||||
echo " $line"
|
||||
cc " ${D}$line${N}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo " Ports in use:"
|
||||
echo " 8332 - Bitcoin RPC"
|
||||
echo " 8333 - Bitcoin P2P"
|
||||
echo " 9735 - Lightning P2P"
|
||||
echo " 10009 - Lightning gRPC"
|
||||
echo " 8080 - Lightning REST"
|
||||
|
||||
cc "${D}service ports:${N}"
|
||||
cc " ${D}8332 bitcoin rpc 9735 lightning p2p${N}"
|
||||
cc " ${D}8333 bitcoin p2p 10009 lightning grpc${N}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
system_info() {
|
||||
show_banner
|
||||
echo " System Information"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
cc "${W}system information${N}"
|
||||
echo ""
|
||||
echo " Hostname: $(hostname)"
|
||||
echo " Kernel: $(uname -r)"
|
||||
echo " Uptime: $(uptime -p)"
|
||||
|
||||
cc "${C}host${N} ${D}$(hostname)${N}"
|
||||
cc "${C}kernel${N} ${D}$(uname -r)${N}"
|
||||
cc "${C}uptime${N} ${D}$(uptime -p 2>/dev/null || echo 'unknown')${N}"
|
||||
echo ""
|
||||
echo " CPU: $(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
|
||||
echo " Memory: $(free -h | grep Mem | awk '{print $2}') total, $(free -h | grep Mem | awk '{print $3}') used"
|
||||
|
||||
local cpu=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | xargs)
|
||||
[ -n "$cpu" ] && cc "${C}cpu${N} ${D}${cpu}${N}"
|
||||
|
||||
local mem_total=$(free -h 2>/dev/null | grep Mem | awk '{print $2}')
|
||||
local mem_used=$(free -h 2>/dev/null | grep Mem | awk '{print $3}')
|
||||
[ -n "$mem_total" ] && cc "${C}memory${N} ${D}${mem_used} / ${mem_total}${N}"
|
||||
echo ""
|
||||
echo " Disk Usage:"
|
||||
df -h / | tail -1 | awk '{print " Root: " $3 " / " $2 " (" $5 " used)"}'
|
||||
|
||||
cc "${D}disk:${N}"
|
||||
df -h / | tail -1 | awk '{printf " root: %s / %s (%s used)\n", $3, $2, $5}' | while read line; do
|
||||
cc "${D}${line}${N}"
|
||||
done
|
||||
|
||||
if [ -d ~/.bitcoin ]; then
|
||||
echo " Bitcoin: $(du -sh ~/.bitcoin 2>/dev/null | cut -f1)"
|
||||
local btc_size=$(du -sh ~/.bitcoin 2>/dev/null | cut -f1)
|
||||
cc " ${D}bitcoin: $btc_size${N}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Container status
|
||||
echo " Containers:"
|
||||
|
||||
if command -v podman >/dev/null 2>&1; then
|
||||
podman ps --format " {{.Names}}: {{.Status}}" 2>/dev/null || echo " No containers running"
|
||||
cc "${D}containers:${N}"
|
||||
podman ps --format " {{.Names}}: {{.Status}}" 2>/dev/null | while read line; do
|
||||
cc "${D}${line}${N}"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main menu
|
||||
main_menu
|
||||
|
||||
Reference in New Issue
Block a user