Files
archy/scripts/dev-start.sh
T
archipelagoandClaude Opus 5 b2c7592840
Demo images / Build & push demo images (push) Failing after 2m16s
security: parameterize node addresses; drop dead APP_URLS config
Keeps the dev and test tooling an outside contributor would want, and takes
our node addresses out of it.

Scripts that silently defaulted to one of our nodes now require an explicit
host and exit 2 without one: smoke-test.sh, trust-archipelago-cert.sh,
dev-container-test.sh (which also derives its RPC and health URLs from the
SSH target instead of a second hardcoded copy), and image-recipe/dev-branding.sh.
A default that points at a machine the user does not own is worse than no
default: it fails confusingly, or reaches a stranger's device.

Usage examples, mock data and test fixtures move to the RFC 5737
documentation range (192.0.2.0/24). CGNAT test values stay inside
100.64.0.0/10 so the range-check semantics they exercise still hold, and
192.168.1.0/.1/.254 are left alone — those are gateway logic and UI
placeholders, not our addresses.

Playwright and the perf spec defaulted their baseURL to one of our nodes;
they now default to localhost:8100, the local dev server.

Removed neode-ui APP_URLS entirely. It is dead code — exported, never
imported — and it pinned fedimint's *prod* launch URL to 192.168.1.228:8175.
Had anything consumed it, every user's node would have tried to reach an
address that on their LAN is either nothing or someone else's machine.
Deleting beats sanitizing dead config.

Verified: frontend 868/868 vitest across 108 files; archipelago-container
75/75; mesh tests 9/9; audit-secrets 5/5. Zero node addresses and zero node
names remain in tracked files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:53:25 -04:00

410 lines
15 KiB
Bash
Executable File

#!/bin/bash
# Archipelago Development Server Starter
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
FRONTEND_DIR="$PROJECT_ROOT/neode-ui"
BACKEND_DIR="$PROJECT_ROOT/core"
# Quietly kill a port — avoids EAGAIN by not piping through xargs
kill_port() {
local pids
pids=$(lsof -ti:"$1" 2>/dev/null) || true
if [ -n "$pids" ]; then
echo "$pids" | while read -r pid; do
kill -9 "$pid" 2>/dev/null || true
done
sleep 1
fi
}
cleanup_ports() {
kill_port 5959
kill_port 8100
}
ensure_deps() {
cd "$FRONTEND_DIR"
if [ ! -d "node_modules" ]; then
echo " Installing dependencies..."
npm install
fi
}
if [ ! -d "$FRONTEND_DIR" ]; then
echo "Frontend directory not found: $FRONTEND_DIR"
exit 1
fi
echo ""
echo "Archipelago Dev Server"
echo ""
# Detect if running on a Linux dev machine (production-like mode available)
IS_LINUX=false
if [[ "$OSTYPE" == "linux"* ]]; then
IS_LINUX=true
fi
echo " 0) Boot branding dev (GRUB theme, Plymouth, installer — patch + QEMU)"
echo " 1) Mock backend (UI dev — fastest, no Docker/Podman needed)"
echo " 2) Full stack (Rust backend + frontend)"
echo " 3) Setup mode (first-time password setup — mock)"
echo " 4) Onboarding mode (onboarding flow — mock)"
echo " 5) Existing user (login screen — mock)"
echo " 6) Boot mode (simulated 25s startup — mock)"
echo " 7) Testnet stack (signet Bitcoin + LND + ThunderHub via Podman)"
echo " 8) Manual instructions"
echo " 9) Container orchestration dev (live testing on .228)"
if [ "$IS_LINUX" = true ]; then
echo " 10) Production build (Linux only — build, install, restart all services)"
echo " Mirrors ISO exactly: backend + frontend + Tor + WG + NostrVPN + nginx"
fi
echo ""
read -p "Enter choice [0-10]: " choice
case $choice in
0)
echo ""
echo "Boot Branding Dev"
echo ""
# Find an ISO to patch
ISO=$(ls -t ~/Desktop/archipelago-dev-*.iso 2>/dev/null | head -1)
if [ -z "$ISO" ]; then
ISO=$(ls -t "$PROJECT_ROOT/image-recipe/results/archipelago-"*.iso 2>/dev/null | head -1)
fi
DEV_BRANDING="$PROJECT_ROOT/image-recipe/dev-branding.sh"
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
echo " No ISO found to patch. Options:"
echo ""
echo " a) Preview GRUB background only (instant):"
echo " python3 image-recipe/branding/generate-grub-background.py /tmp/grub-bg.png && open /tmp/grub-bg.png"
echo ""
echo " b) Download an ISO from FileBrowser (http://192.0.2.10:8083)"
echo " then drop it on your Desktop and re-run this option."
echo ""
echo " Files you can edit:"
echo " image-recipe/branding/grub-theme/background.png — GRUB boot background"
echo " image-recipe/branding/grub-theme/theme.txt — GRUB menu colors/layout"
echo " image-recipe/branding/plymouth-theme/logo.png — Plymouth boot logo"
echo " image-recipe/branding/plymouth-theme/*.script — Plymouth animation"
echo ""
exit 0
fi
echo " ISO: $ISO"
echo " Edit these files, then this script patches and boots in QEMU:"
echo " branding/grub-theme/background.png — GRUB background"
echo " branding/grub-theme/theme.txt — GRUB menu theme"
echo " branding/plymouth-theme/logo.png — Plymouth logo"
echo ""
if [ -f "$DEV_BRANDING" ]; then
exec bash "$DEV_BRANDING" "$ISO"
else
echo " dev-branding.sh not found at: $DEV_BRANDING"
exit 1
fi
;;
1)
echo ""
echo "Starting frontend with mock backend..."
cleanup_ports
ensure_deps
exec npm run dev:mock
;;
2)
echo ""
echo "Starting full stack (Rust backend + frontend)..."
cleanup_ports
if [ ! -d "$BACKEND_DIR" ]; then
echo "Backend directory not found: $BACKEND_DIR"
exit 1
fi
cd "$BACKEND_DIR"
if ! cargo check --bin archipelago > /tmp/archipelago-backend-check.log 2>&1; then
echo "Backend build check failed. See /tmp/archipelago-backend-check.log"
echo "Falling back to mock backend."
ensure_deps
exec npm run dev:mock
fi
echo " Starting Rust backend..."
export ARCHIPELAGO_DATA_DIR=/tmp/archipelago-dev
export ARCHIPELAGO_DEV_DATA_DIR=/tmp/archipelago-dev
export ARCHIPELAGO_DEV_MODE=true
export ARCHIPELAGO_BIND=127.0.0.1:5959
export ARCHIPELAGO_LOG_LEVEL=debug
export ARCHIPELAGO_BITCOIN_SIMULATION=mock
cargo run --bin archipelago > /tmp/archipelago-backend.log 2>&1 &
BACKEND_PID=$!
echo " Backend PID: $BACKEND_PID (logs: /tmp/archipelago-backend.log)"
echo " Waiting for backend on port 5959..."
for i in $(seq 1 60); do
if lsof -ti:5959 >/dev/null 2>&1; then break; fi
sleep 1
done
if ! lsof -ti:5959 >/dev/null 2>&1; then
echo "Backend did not start. Falling back to mock."
kill "$BACKEND_PID" 2>/dev/null || true
ensure_deps
exec npm run dev:mock
fi
echo " Backend ready."
trap "kill $BACKEND_PID 2>/dev/null" EXIT
ensure_deps
exec npm run dev
;;
3)
echo ""
echo "Starting setup mode..."
cleanup_ports
ensure_deps
VITE_DEV_MODE=setup exec npm run dev:mock
;;
4)
echo ""
echo "Starting onboarding mode..."
cleanup_ports
ensure_deps
VITE_DEV_MODE=onboarding exec npm run dev:mock
;;
5)
echo ""
echo "Starting existing user mode..."
cleanup_ports
ensure_deps
VITE_DEV_MODE=existing exec npm run dev:mock
;;
6)
echo ""
echo "Starting boot mode (25s simulated startup)..."
cleanup_ports
ensure_deps
VITE_DEV_MODE=boot exec npm run dev:mock
;;
7)
echo ""
echo "Starting testnet stack (signet) via Podman/Docker..."
# Check for a working container runtime (binary exists AND daemon responds)
RUNTIME=""
COMPOSE=""
if command -v docker &>/dev/null && docker ps &>/dev/null; then
RUNTIME="docker"
COMPOSE="docker compose"
elif command -v podman &>/dev/null && podman ps &>/dev/null; then
if command -v podman-compose &>/dev/null; then
RUNTIME="podman"
COMPOSE="podman-compose"
else
RUNTIME="podman"
COMPOSE="podman compose"
fi
fi
if [ -z "$RUNTIME" ]; then
if command -v podman &>/dev/null; then
echo " Podman machine not running — starting it..."
if ! podman machine ls --format '{{.Name}}' 2>/dev/null | grep -q .; then
echo " No Podman machine found — initializing..."
podman machine init
fi
podman machine start
if podman ps &>/dev/null; then
if command -v podman-compose &>/dev/null; then
RUNTIME="podman"
COMPOSE="podman-compose"
else
RUNTIME="podman"
COMPOSE="podman compose"
fi
else
echo " Failed to start Podman machine."
exit 1
fi
elif command -v docker &>/dev/null; then
echo ""
echo "Docker is installed but the daemon isn't running."
echo "Start Docker Desktop and try again."
exit 1
else
echo ""
echo "No container runtime found. Install one:"
echo " brew install podman podman-compose"
echo " # or"
echo " brew install --cask docker"
exit 1
fi
fi
echo " Using: $RUNTIME"
cd "$PROJECT_ROOT"
echo " Starting signet Bitcoin + LND + ThunderHub + Fedimint..."
$COMPOSE -f docker-compose.testnet.yml up -d
echo ""
echo " Testnet stack starting. Services:"
echo " ThunderHub: http://localhost:3010 (password: thunderhub)"
echo " Fedimint Guardian: http://localhost:18175"
echo " LND REST: http://localhost:8080"
echo " Bitcoin RPC: localhost:38332"
echo ""
echo " Get signet coins: https://signetfaucet.com"
echo ""
echo " Also starting mock frontend..."
cleanup_ports
ensure_deps
exec npm run dev:mock
;;
8)
echo ""
echo "Manual Instructions"
echo ""
echo "UI development (mock backend, no Docker):"
echo " cd $FRONTEND_DIR"
echo " npm install && npm run dev:mock"
echo ""
echo "Dev modes (prepend to command):"
echo " VITE_DEV_MODE=setup First-time setup flow"
echo " VITE_DEV_MODE=onboarding Onboarding flow"
echo " VITE_DEV_MODE=existing Login screen"
echo " VITE_DEV_MODE=boot Boot sequence"
echo ""
echo "Testnet stack (requires Podman or Docker):"
echo " podman compose -f docker-compose.testnet.yml up -d"
echo ""
echo "Full stack (requires Rust toolchain):"
echo " Terminal 1: cd $BACKEND_DIR && cargo run --bin archipelago"
echo " Terminal 2: cd $FRONTEND_DIR && npm run dev"
echo ""
echo "Access: http://localhost:8100 (password: password123)"
;;
9)
echo ""
echo "Container Orchestration Dev (live testing on .228)"
echo "Syncs code, builds on server, runs orchestration smoke tests."
echo ""
exec "$SCRIPT_DIR/dev-container-test.sh"
;;
10)
if [ "$IS_LINUX" != true ]; then
echo "Production build is only available on Linux dev machines."
exit 1
fi
echo ""
echo "Production Build — mirrors ISO install exactly"
echo ""
FAILED=0
# Step 1: Build backend
echo "[1/5] Building Rust backend (release)..."
cd "$BACKEND_DIR/archipelago"
if cargo build --release 2>&1 | tail -3; then
RELEASE_BIN="$BACKEND_DIR/target/release/archipelago"
sudo cp "$RELEASE_BIN" /usr/local/bin/archipelago
sudo chmod +x /usr/local/bin/archipelago
echo " Backend installed: $(ls -lh /usr/local/bin/archipelago | awk '{print $5}')"
else
echo " FAILED: cargo build --release"
FAILED=1
fi
# Step 2: Type-check + build frontend
echo "[2/5] Building frontend..."
cd "$FRONTEND_DIR"
if [ ! -d "node_modules" ]; then
npm install
fi
if npx vue-tsc -b --noEmit 2>&1 | tail -3; then
npm run build 2>&1 | tail -3
sudo cp -r "$PROJECT_ROOT/web/dist/neode-ui/"* /opt/archipelago/web-ui/
# Deploy AIUI (pre-built demo or source build)
if [ -d "$PROJECT_ROOT/../AIUI/packages/app/dist" ]; then
sudo cp -r "$PROJECT_ROOT/../AIUI/packages/app/dist/"* /opt/archipelago/web-ui/aiui/
echo " AIUI deployed from source build"
elif [ -d "$PROJECT_ROOT/demo/aiui" ]; then
sudo mkdir -p /opt/archipelago/web-ui/aiui/
sudo cp -r "$PROJECT_ROOT/demo/aiui/"* /opt/archipelago/web-ui/aiui/
echo " AIUI deployed from demo/"
fi
echo " Frontend deployed to /opt/archipelago/web-ui/"
else
echo " FAILED: vue-tsc type check"
FAILED=1
fi
# Step 3: Sync configs from repo
echo "[3/5] Syncing configs..."
sudo cp "$PROJECT_ROOT/image-recipe/configs/archipelago.service" /etc/systemd/system/archipelago.service
sudo cp "$PROJECT_ROOT/image-recipe/configs/nginx-archipelago.conf" /etc/nginx/sites-available/archipelago
sudo cp "$PROJECT_ROOT/image-recipe/configs/snippets/"*.conf /etc/nginx/snippets/ 2>/dev/null
for unit in archipelago-tor-helper.service archipelago-tor-helper.path archipelago-wg.service archipelago-wg-address.service nostr-relay.service nostr-vpn.service; do
sudo cp "$PROJECT_ROOT/image-recipe/configs/$unit" "/etc/systemd/system/$unit"
done
sudo cp "$PROJECT_ROOT/scripts/tor-helper.sh" /opt/archipelago/scripts/tor-helper.sh
sudo chmod +x /opt/archipelago/scripts/tor-helper.sh
sudo cp "$PROJECT_ROOT/scripts/archipelago-wg" /usr/local/bin/archipelago-wg
sudo chmod +x /usr/local/bin/archipelago-wg
echo " Configs synced"
# Step 4: Sync Tor hostnames
echo "[4/5] Syncing Tor hostnames..."
for svc in archipelago bitcoin electrumx lnd btcpay mempool fedimint; do
dir="/var/lib/archipelago/tor/hidden_service_$svc"
if [ -f "$dir/hostname" ]; then
sudo cp "$dir/hostname" "/var/lib/archipelago/tor-hostnames/$svc"
fi
done
sudo chown -R "$(whoami)":"$(whoami)" /var/lib/archipelago/tor-hostnames 2>/dev/null
# Step 5: Reload and restart all services
echo "[5/5] Restarting services..."
sudo systemctl daemon-reload
sudo nginx -t 2>&1 && sudo systemctl reload nginx
sudo systemctl restart archipelago
# Verify
echo ""
echo "Service Status:"
for svc in tor@default archipelago-wg archipelago-wg-address nostr-relay nostr-vpn archipelago-tor-helper.path archipelago nginx; do
STATUS=$(systemctl is-active "$svc" 2>/dev/null)
if [ "$STATUS" = "active" ]; then
printf " %-30s active\n" "$svc"
else
printf " %-30s FAILED\n" "$svc"
FAILED=1
fi
done
echo ""
if [ "$FAILED" -eq 0 ]; then
HOST_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
ONION=$(cat /var/lib/archipelago/tor-hostnames/archipelago 2>/dev/null || echo "generating...")
echo "All services running. Access:"
echo " LAN: http://$HOST_IP"
echo " Tor: http://$ONION"
echo " WG: 10.44.0.1"
echo " RPC: http://127.0.0.1:5678/rpc/v1"
else
echo "Some services failed. Check: journalctl -u <service> --no-pager -n 20"
fi
;;
*)
echo "Invalid choice"
exit 1
;;
esac