#!/bin/bash # # Deploy the AIUI (Chat mode iframe) build to an Archipelago server. # # Usage: # ./scripts/setup-aiui-server.sh # ./scripts/setup-aiui-server.sh archipelago@192.168.1.198 # ./scripts/setup-aiui-server.sh archipelago@192.168.1.228 # # What it does: # Rsyncs (or tar+scp, if rsync is unavailable on the target) a locally # built AIUI dist/ into /opt/archipelago/web-ui/aiui/ on the target node. # # What it no longer does (13-02-PLAN.md — closing a live production # exposure): it used to also patch nginx to route /aiui/api/claude/ to a # standalone Python proxy holding its own ANTHROPIC_API_KEY, with no session # gate — anyone who could reach the node's web port could spend the owner's # API budget. That proxy, its systemd unit, and this script's nginx-patch # step are all deleted (see scripts/deploy-to-target.sh's "Removing legacy # Claude API proxy sidecar" step). AIUI's Claude/Ollama calls now route # through the Rust daemon (127.0.0.1:5678), which enforces the session # cookie itself and reads the node's single key ledger. Set the key via # `system.settings.set claude_api_key` (Settings > AIUI in neode-ui) — this # script has nothing to do with the key anymore. # # Prerequisites: # - SSH key access to target server # - AIUI is built automatically (via scripts/build-aiui.sh) when its dist # is missing or stale — D-19 (2026-08-03): AIUI lives in-repo at aiui/ # now, so there is no second checkout to build separately first. set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" SSH_KEY="${ARCHIPELAGO_SSH_KEY:-$HOME/.ssh/archipelago-deploy}" SSH_OPTS="-o StrictHostKeyChecking=no -i $SSH_KEY" TARGET_HOST="$1" if [ -z "$TARGET_HOST" ]; then echo "Usage: $0 " echo " e.g. $0 archipelago@192.168.1.198" exit 1 fi AIUI_DIST="$PROJECT_DIR/aiui/packages/app/dist" AIUI_SRC="$PROJECT_DIR/aiui/packages/app/src" timestamp() { echo "[$(date +%H:%M:%S)]"; } # D-19 (2026-08-03): AIUI lives in-repo at aiui/ — no second checkout to # build separately first. Build it automatically when the dist is missing # or stale, via the one supported build path (scripts/build-aiui.sh # enforces VITE_BASE_PATH, installs from the committed lockfile, and # attributes the build to this repo's own commit). D-15's "enforced, not # remembered" applies here too — a script that only prints instructions is # the remembered form. if [ ! -f "$AIUI_DIST/index.html" ] || [ "$(find "$AIUI_SRC" -newer "$AIUI_DIST/index.html" -print -quit 2>/dev/null)" != "" ]; then echo "$(timestamp) AIUI dist missing or stale — building via scripts/build-aiui.sh..." bash "$PROJECT_DIR/scripts/build-aiui.sh" fi if [ ! -f "$AIUI_DIST/index.html" ]; then echo "ERROR: AIUI build not found at $AIUI_DIST after running scripts/build-aiui.sh" exit 1 fi echo "╔════════════════════════════════════════════════════════════╗" echo "║ Archipelago AIUI deploy ║" echo "║ Target: $TARGET_HOST" echo "╚════════════════════════════════════════════════════════════╝" # --- Deploy AIUI files --- echo "" echo "$(timestamp) 📦 Deploying AIUI files..." if ssh $SSH_OPTS "$TARGET_HOST" "which rsync" &>/dev/null; then rsync -avz --delete -e "ssh $SSH_OPTS" "$AIUI_DIST/" "$TARGET_HOST:/opt/archipelago/web-ui/aiui/" 2>&1 | tail -3 else echo " rsync not available, using tar+scp..." TMPTAR=$(mktemp /tmp/aiui-dist-XXXXX.tar.gz) (cd "$AIUI_DIST" && tar czf "$TMPTAR" .) scp $SSH_OPTS "$TMPTAR" "$TARGET_HOST:/tmp/aiui-dist.tar.gz" ssh $SSH_OPTS "$TARGET_HOST" "sudo mkdir -p /opt/archipelago/web-ui/aiui && cd /opt/archipelago/web-ui/aiui && sudo tar xzf /tmp/aiui-dist.tar.gz --overwrite" rm -f "$TMPTAR" fi echo " AIUI deployed." # --- Verify --- echo "" echo "$(timestamp) ✅ Verification..." ssh $SSH_OPTS "$TARGET_HOST" " echo \" AIUI index: \$(ls -la /opt/archipelago/web-ui/aiui/index.html 2>/dev/null | awk '{print \$6,\$7,\$8}')\" echo \" Nginx: \$(systemctl is-active nginx)\" echo \" Backend: \$(systemctl is-active archipelago)\" " echo "" echo "$(timestamp) Done! AIUI deployed." echo " Set the Claude API key (if not already set) via Settings > AIUI in" echo " neode-ui — it now lives only at /secrets/claude-api-key." echo " Access: http://$(echo $TARGET_HOST | cut -d@ -f2)"