Archipelago — open-source initial import

This commit is contained in:
Archipelago
2026-08-12 10:55:49 +00:00
commit 8c78a7d4c2
1560 changed files with 331970 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
#!/bin/bash
# Shared utility functions for Archipelago scripts
#
# Source this from any script:
# source "$(dirname "$0")/lib/common.sh"
#
# Provides: logging, SSH helpers, health checks, disk checks, memory limits
# Guard against double-sourcing
[ -n "${_ARCHY_COMMON_LOADED:-}" ] && return 0
_ARCHY_COMMON_LOADED=1
# ── Colored logging ─────────────────────────────────────────────────────
log_info() { echo -e "\033[0;32m[INFO]\033[0m $(date '+%H:%M:%S') $*"; }
log_warn() { echo -e "\033[0;33m[WARN]\033[0m $(date '+%H:%M:%S') $*"; }
log_error() { echo -e "\033[0;31m[ERROR]\033[0m $(date '+%H:%M:%S') $*"; }
# ── SSH wrapper with deploy key ─────────────────────────────────────────
# Usage: ssh_cmd <host> <command...>
# Uses the standard deploy key and safe defaults.
ssh_cmd() {
local host="$1"; shift
local key="${ARCHIPELAGO_SSH_KEY:-$HOME/.ssh/archipelago-deploy}"
ssh -i "$key" \
-o StrictHostKeyChecking=no \
-o ConnectTimeout=10 \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=4 \
"archipelago@${host}" "$@"
}
# Usage: scp_cmd <src> <dest>
# Wraps scp with the same deploy key and options.
scp_cmd() {
local key="${ARCHIPELAGO_SSH_KEY:-$HOME/.ssh/archipelago-deploy}"
scp -i "$key" \
-o StrictHostKeyChecking=no \
-o ConnectTimeout=10 \
"$@"
}
# ── Health check ────────────────────────────────────────────────────────
# Wait for an HTTP health endpoint to respond successfully.
# Usage: wait_for_health <host> [max_wait_seconds] [path]
wait_for_health() {
local host="$1" max_wait="${2:-60}" path="${3:-/health}"
local waited=0
while [ $waited -lt $max_wait ]; do
if curl -sf "http://${host}${path}" >/dev/null 2>&1; then
log_info "Health check passed for ${host}"
return 0
fi
sleep 2
waited=$((waited + 2))
done
log_error "Health check failed for ${host} after ${max_wait}s"
return 1
}
# ── Disk space check ───────────────────────────────────────────────────
# Check that disk usage on a remote host is below a threshold.
# Usage: check_disk_space <host> [max_percent]
check_disk_space() {
local host="$1" max_pct="${2:-85}"
local pct
pct=$(ssh_cmd "$host" "df / | tail -1 | awk '{print \$(NF-1)}' | tr -d '%'" 2>/dev/null)
if [ -n "$pct" ] && [ "$pct" -gt "$max_pct" ] 2>/dev/null; then
log_error "Disk at ${pct}% on ${host} (max ${max_pct}%)"
return 1
fi
return 0
}
# ── Memory limit calculator ────────────────────────────────────────────
# Returns the memory limit for a container by name.
# Checks /etc/archipelago/memory-limits.conf first (override), then falls
# back to built-in defaults. Mirrors the pattern in first-boot-containers.sh.
#
# Low-memory mode: set LOW_MEM=true before calling to get reduced limits
# on certain heavy containers.
#
# Usage: mem_limit <container-name>
mem_limit() {
local name="$1"
# Allow per-host overrides via config file
local limit
limit=$(grep "^${name}=" /etc/archipelago/memory-limits.conf 2>/dev/null | cut -d= -f2)
if [ -n "$limit" ]; then
echo "$limit"
return
fi
# Built-in defaults (keep in sync with first-boot-containers.sh and Rust package config)
local low="${LOW_MEM:-false}"
case "$name" in
bitcoin|bitcoin-core|bitcoin-knots) $low && echo "4g" || echo "8g" ;;
ollama) $low && echo "1g" || echo "4g" ;;
lnd) echo "512m" ;;
electrumx|mempool-electrs|electrs) echo "4g" ;;
nextcloud) echo "1g" ;;
immich_server) echo "1g" ;;
btcpay-server|btcpayserver) echo "1g" ;;
homeassistant) echo "512m" ;;
fedimint) echo "512m" ;;
fedimint-gateway) echo "512m" ;;
photoprism) $low && echo "512m" || echo "1g" ;;
mempool-api) echo "512m" ;;
jellyfin) echo "1g" ;;
searxng) echo "512m" ;;
archy-btcpay-db) echo "512m" ;;
archy-nbxplorer) echo "512m" ;;
archy-mempool-db) echo "512m" ;;
archy-mempool-web) echo "256m" ;;
grafana) echo "256m" ;;
vaultwarden) echo "256m" ;;
uptime-kuma) echo "256m" ;;
filebrowser) echo "256m" ;;
portainer) echo "256m" ;;
nginx-proxy-manager) echo "256m" ;;
immich_postgres) echo "256m" ;;
immich_redis) echo "128m" ;;
tailscale) echo "256m" ;;
penpot-postgres) echo "256m" ;;
penpot-valkey) echo "128m" ;;
penpot-backend) echo "512m" ;;
penpot-exporter) echo "256m" ;;
penpot-frontend) echo "256m" ;;
nostr-rs-relay) echo "256m" ;;
strfry) echo "256m" ;;
indeedhub|archy-bitcoin-ui|archy-lnd-ui|archy-electrs-ui) echo "128m" ;;
*) echo "512m" ;;
esac
}
# ── Wait for container readiness ───────────────────────────────────────
# Wait for a container health check command to succeed.
# Usage: wait_for_container <name> <check_cmd> [max_wait_seconds]
wait_for_container() {
local name="$1" check_cmd="$2" max_wait="${3:-30}"
local waited=0
while [ $waited -lt $max_wait ]; do
if eval "$check_cmd" 2>/dev/null; then
log_info "$name is ready (${waited}s)"
return 0
fi
sleep 2
waited=$((waited + 2))
done
log_warn "$name not ready after ${max_wait}s"
return 1
}
# ── Section timing ─────────────────────────────────────────────────────
# Track elapsed time for deploy sections.
# Usage:
# section_start "Building frontend"
# ... do work ...
# section_end
_SECTION_START=0
_SECTION_NAME=""
section_start() {
_SECTION_NAME="${1:-}"
_SECTION_START=$(date +%s)
[ -n "$_SECTION_NAME" ] && log_info "$_SECTION_NAME"
}
section_end() {
local elapsed=$(( $(date +%s) - _SECTION_START ))
if [ -n "$_SECTION_NAME" ]; then
log_info "$_SECTION_NAME done (${elapsed}s)"
else
echo " (${elapsed}s)"
fi
}
+292
View File
@@ -0,0 +1,292 @@
#!/bin/bash
# ─────────────────────────────────────────────────────────────────
# Archipelago Install TUI Library
# Sourced by auto-install.sh to add animations to the installer.
# If not sourced, installer falls back to plain text output.
# ─────────────────────────────────────────────────────────────────
# Revert: remove the "source" line in auto-install.sh and
# this file. Installer reverts to plain step/ok/fail output.
# ─────────────────────────────────────────────────────────────────
[ -n "${_INSTALL_TUI_LOADED:-}" ] && return 0
_INSTALL_TUI_LOADED=1
# ── Extra colors (plain installer only has basic set) ──────────
ORANGE_GLOW=$'\033[38;5;220m'
GREEN_DIM=$'\033[38;5;22m'
GREEN_BRIGHT=$'\033[38;5;46m'
DARK=$'\033[38;5;235m'
# ── Terminal setup ─────────────────────────────────────────────
TW=$(tput cols 2>/dev/null || echo 80)
TH=$(tput lines 2>/dev/null || echo 24)
[[ $TW -gt 100 ]] && TW=100
LOGO_W=43
LOGO_PAD=$(( (TW - LOGO_W) / 2 ))
[[ $LOGO_PAD -lt 0 ]] && LOGO_PAD=0
LOGO_PADS=$(printf "%*s" "$LOGO_PAD" "")
# ── Primitives ─────────────────────────────────────────────────
hide_cursor() { tput civis 2>/dev/null || true; }
show_cursor() { tput cnorm 2>/dev/null || true; }
goto() { printf "\033[%d;%dH" "$1" "$2"; }
clear_line() { printf "\033[K"; }
# ── Hacker glyphs ─────────────────────────────────────────────
HEXCHARS='0123456789abcdef'
SPIN_FRAMES='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
tui_rand_hex() {
local len="${1:-8}" out=""
for _ in $(seq 1 "$len"); do
out="${out}${HEXCHARS:RANDOM % 16:1}"
done
echo -n "$out"
}
# ── ASCII Logo ─────────────────────────────────────────────────
LOGO_FRONT=(
'▄▀█ █▀▄ █▀▀ █ █ █ █▀█ █▀▀ █ ▄▀█ █▀▀ █▀█'
'█▀█ █▀▄ █ █▀█ █ █▀▀ ██▀ █ █▀█ █ █ █ █'
'▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀'
)
# ── Boot scan effect ──────────────────────────────────────────
tui_boot_scan() {
clear
hide_cursor
local messages=(
"POST: memory check ............ OK"
"BIOS: AES-NI .................. detected"
"UEFI: secure boot ............. disabled"
"USB: boot media .............. verified"
"NET: interface ............... link up"
"INIT: loading archipelago ....."
)
for i in $(seq 1 6); do
local addr data
addr=$(tui_rand_hex 8)
data=$(tui_rand_hex 32)
goto $i 1
printf "%s%b0x%s %s%b" "$PADS" "$DARK" "$addr" "$data" "$NC"
sleep 0.02
done
local row=3
for msg in "${messages[@]}"; do
goto $row 1; clear_line
printf "%s%b" "$PADS" "$ORANGE_DIM"
local i=0
while [[ $i -lt ${#msg} ]]; do
printf "%s" "${msg:$i:1}"
i=$((i + 1))
sleep 0.01
done
printf "%b\n" "$NC"
row=$((row + 1))
sleep 0.04
done
sleep 0.3
for r in $(seq 1 $((row + 2))); do goto $r 1; clear_line; done
sleep 0.2
}
# ── Logo decrypt reveal ───────────────────────────────────────
tui_logo_decrypt_reveal() {
local row="${1:-3}"
local iterations=6
local scramble_chars='█▓▒░╳◆▀▄▌▐┃━╋╬╪'
local front_col=$((LOGO_PAD + 1))
local shadow_col=$((LOGO_PAD + 3))
# Draw shadow layer (static, dark)
for li in 0 1 2; do
goto $((row + li + 1)) "$shadow_col"
printf "%b%s%b" "$DARK" "${LOGO_FRONT[$li]}" "$NC"
done
# Decrypt front layer
for iter in $(seq 1 "$iterations"); do
local color
case $iter in
1|2) color="$ORANGE_DIM" ;;
3|4) color="$ORANGE" ;;
*) color="$ORANGE_BRIGHT" ;;
esac
for li in 0 1 2; do
local real="${LOGO_FRONT[$li]}"
local out=""
local len=${#real}
local resolve=$(( iter * len / iterations ))
local ci=0
while [[ $ci -lt $len ]]; do
local ch="${real:$ci:1}"
if [[ $ci -lt $resolve ]]; then
out="${out}${ch}"
elif [[ "$ch" == " " ]]; then
out="${out} "
else
out="${out}${scramble_chars:RANDOM % ${#scramble_chars}:1}"
fi
ci=$((ci + 1))
done
goto $((row + li)) "$front_col"
printf "%b%s%b" "$color" "$out" "$NC"
done
sleep 0.07
done
# Glow pulse
for color in "$ORANGE_BRIGHT" "$ORANGE_GLOW" "$ORANGE_BRIGHT" "$ORANGE"; do
for li in 0 1 2; do
goto $((row + li)) "$front_col"
printf "\033[K%b%s%b" "$color" "${LOGO_FRONT[$li]}" "$NC"
done
sleep 0.05
done
}
# ── Logo celebration strobe ───────────────────────────────────
tui_logo_celebrate() {
local row="${1:-3}"
local col=$((LOGO_PAD + 1))
local party_colors=("$ORANGE" "$ORANGE_GLOW" "$WHITE" "$ORANGE_BRIGHT" "$GREEN_BRIGHT" "$ORANGE_GLOW" "$ORANGE")
for color in "${party_colors[@]}"; do
for li in 0 1 2; do
goto $((row + li)) "$col"
printf "\033[K%b%s%b" "$color" "${LOGO_FRONT[$li]}" "$NC"
done
sleep 0.06
done
}
# ── CRT power-on scan line ────────────────────────────────────
tui_crt_on() {
hide_cursor; clear
for r in $(seq 1 "$TH"); do
goto "$r" 1
printf "%b%*s%b" "$ORANGE_DIM" "$TW" "" "$NC"
if [[ $r -gt 1 ]]; then goto $((r - 1)) 1; clear_line; fi
sleep 0.008
done
goto "$TH" 1; clear_line
sleep 0.1; clear
}
# ── Progress bar with bouncing Bitcoin symbol ─────────────────
# Usage: tui_progress_bar <pid> <message>
# Runs until process $pid exits. Shows progress bar + bouncing ₿
tui_progress_bar() {
local pid=$1 msg=$2
local frames='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
local bar_width=36
local fi=0 tick=0
local est_ticks=200 # rough estimate for progress
# Bouncing ₿ setup
local term_h=$TH
local b_row=12 b_col=10
local b_dr=1 b_dc=1
local b_min_row=10 b_max_row=$((term_h - 2))
[[ $b_max_row -lt 14 ]] && b_max_row=14
local b_min_col=4 b_max_col=$((TW - 4))
local b_colors=("$ORANGE" "$ORANGE_BRIGHT" "$ORANGE_GLOW" "$ORANGE_DIM" "$WHITE")
local b_ci=0
local b_prev_row=$b_row b_prev_col=$b_col
while kill -0 "$pid" 2>/dev/null; do
# Spinner
printf "\r%s %b%s %s%b" "$PADS" "$ORANGE" "${frames:fi%10:1}" "$msg" "$NC"
fi=$((fi + 1))
tick=$((tick + 1))
# Progress bar (estimate-based since we don't know real progress)
local pct=$(( tick * 95 / est_ticks ))
[[ $pct -gt 95 ]] && pct=95
local filled=$(( pct * bar_width / 100 ))
local empty=$(( bar_width - filled ))
local bar_f="" bar_e=""
for _ in $(seq 1 "$filled" 2>/dev/null); do bar_f="${bar_f}"; done
for _ in $(seq 1 "$empty" 2>/dev/null); do bar_e="${bar_e}"; done
# Draw bar below spinner
goto 22 1; clear_line
printf "%s %b%s%b%s%b %b%d%%%b" "$PADS" "$ORANGE" "$bar_f" "$DARK" "$bar_e" "$NC" "$ORANGE_DIM" "$pct" "$NC"
# Bouncing ₿
goto "$b_prev_row" "$b_prev_col"; printf " "
b_row=$((b_row + b_dr)); b_col=$((b_col + b_dc))
if [[ $b_row -ge $b_max_row ]] || [[ $b_row -le $b_min_row ]]; then
b_dr=$(( -b_dr )); b_row=$((b_row + b_dr))
b_ci=$(( (b_ci + 1) % ${#b_colors[@]} ))
fi
if [[ $b_col -ge $b_max_col ]] || [[ $b_col -le $b_min_col ]]; then
b_dc=$(( -b_dc )); b_col=$((b_col + b_dc))
b_ci=$(( (b_ci + 1) % ${#b_colors[@]} ))
fi
goto "$b_row" "$b_col"
printf "%b₿%b" "${b_colors[$b_ci]}" "$NC"
b_prev_row=$b_row; b_prev_col=$b_col
sleep 0.1
done
# Clean up
goto "$b_prev_row" "$b_prev_col"; printf " "
goto 22 1; clear_line
# Clear bouncing area
for r in $(seq "$b_min_row" "$b_max_row"); do goto "$r" 1; clear_line; done
printf "\r%s %b✓ %s%b\n" "$PADS" "$ORANGE_BRIGHT" "$msg" "$NC"
}
# ── Flashing completion message ───────────────────────────────
tui_flash_remove_usb() {
local row="${1:-20}"
for _ in $(seq 1 5); do
goto "$row" 1; clear_line
printf "%s%b%b >>> REMOVE THE USB DRIVE NOW <<<%b\n" "$PADS" "$ORANGE" "$BOLD" "$NC"
sleep 0.5
goto "$row" 1; clear_line
printf "%s%b >>> REMOVE THE USB DRIVE NOW <<<%b\n" "$PADS" "$ORANGE_DIM" "$NC"
sleep 0.5
done
goto "$row" 1; clear_line
printf "%s%b%b >>> REMOVE THE USB DRIVE NOW <<<%b\n" "$PADS" "$ORANGE" "$BOLD" "$NC"
}
# ── Override: replace plain spinner with progress bar ─────────
# Call this to replace the default spinner() with the animated version.
# Only for long operations (base system install, bootloader).
tui_enable_progress_spinner() {
spinner() {
tui_progress_bar "$1" "$2"
}
}
# ── Welcome screen (replaces plain logo) ─────────────────────
tui_welcome() {
tui_crt_on
tui_boot_scan
clear
hide_cursor
tui_logo_decrypt_reveal 3
# Subtitle
local sub_pad=$(printf "%*s" $((LOGO_PAD + LOGO_W - 15)) "")
goto 7 1
printf "%s%b%s%b\n" "$sub_pad" "$ORANGE_DIM" "bitcoin node os" "$NC"
echo ""
}
# ── Completion screen (replaces plain message) ────────────────
tui_complete() {
tui_logo_celebrate 3
show_cursor
}
# Mark as loaded — installer checks this
TUI_AVAILABLE=1