#!/usr/bin/env bash # ============================================================ # Trifti Sovereign — Quick Setup Bootstrap # ============================================================ # This script bootstraps the full setup on customer hardware. # curl -sSL https://setup.sovereign.trifti.ch | bash # ============================================================ set -euo pipefail echo "" echo " ╔══════════════════════════════════════════╗" echo " ║ Trifti Sovereign — Setup v1.0.0 ║" echo " ║ Local-First AI for Swiss Business ║" echo " ╚══════════════════════════════════════════╝" echo "" INSTALL_DIR="/opt/trifti-sovereign" LOG_FILE="/var/log/sovereign-setup.log" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' log() { printf "${BLUE}[Sovereign]${NC} %s\n" "$*" | tee -a "$LOG_FILE"; } ok() { printf "${GREEN}[✓]${NC} %s\n" "$*" | tee -a "$LOG_FILE"; } warn() { printf "${YELLOW}[!]${NC} %s\n" "$*" | tee -a "$LOG_FILE"; } err() { printf "${RED}[✗]${NC} %s\n" "$*" | tee -a "$LOG_FILE"; exit 1; } # ── Hardware Detection ──────────────────────────────────────── detect_hardware() { log "Detecting hardware..." local arch mem_kb tier model_name mem_limit arch="$(uname -m)" if [[ -f /proc/meminfo ]]; then mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') elif command -v sysctl &>/dev/null; then mem_kb=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1024 )) else mem_kb=0 fi local mem_gb=$(( mem_kb / 1024 / 1024 )) if [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then if (( mem_gb <= 8 )); then tier="lite"; model_name="tinyllama"; mem_limit="6G" warn "Raspberry Pi detected (${mem_gb}GB RAM). Lite tier." else tier="pro"; model_name="qwen3:8b"; mem_limit="12G" ok "Apple Silicon detected (${mem_gb}GB RAM). Pro tier." fi elif [[ "$arch" == "x86_64" ]]; then if (( mem_gb >= 16 )); then tier="pro"; model_name="qwen3:8b"; mem_limit="12G" else tier="lite"; model_name="tinyllama"; mem_limit="6G" fi ok "x86_64 detected (${mem_gb}GB RAM). ${tier^} tier." else err "Unsupported architecture: $arch" fi SOVEREIGN_TIER="$tier" OLLAMA_DEFAULT_MODEL="$model_name" OLLAMA_MEMORY_LIMIT="$mem_limit" ok "Tier: $SOVEREIGN_TIER | Model: $OLLAMA_DEFAULT_MODEL | RAM: $OLLAMA_MEMORY_LIMIT" } # ── Prerequisites ───────────────────────────────────────────── check_prerequisites() { log "Checking prerequisites..." if ! command -v docker &>/dev/null; then warn "Docker not found. Installing..." if [[ "$(uname)" == "Darwin" ]]; then err "Please install Docker Desktop from https://docker.com/products/docker-desktop" else curl -fsSL https://get.docker.com | sh sudo systemctl enable --now docker sudo usermod -aG docker "$USER" 2>/dev/null || true ok "Docker installed." fi else ok "Docker: $(docker --version)" fi docker compose version &>/dev/null || err "Docker Compose V2 not found." ok "Docker Compose found." command -v curl &>/dev/null || err "curl required." command -v openssl &>/dev/null || err "openssl required." } # ── Setup Directory ─────────────────────────────────────────── setup_directory() { log "Setting up $INSTALL_DIR..." sudo mkdir -p "$INSTALL_DIR"/{data,backups,branding,tools} sudo mkdir -p /etc/sovereign/tls /var/log # Download deployment files from update server local base_url="https://update.sovereign.trifti.ch/releases/1.0.0" for f in docker-compose.yml Dockerfile.sovereign sovereign-updater.sh sovereign.nginx; do log "Downloading $f..." curl -sSf "${base_url}/${f}" -o "${INSTALL_DIR}/${f}" 2>/dev/null || warn "Could not download $f" done chmod +x "${INSTALL_DIR}/sovereign-updater.sh" 2>/dev/null || true ok "Files deployed to $INSTALL_DIR" } # ── Generate .env ───────────────────────────────────────────── generate_env() { local env_file="${INSTALL_DIR}/.env" if [[ -f "$env_file" ]]; then warn ".env already exists. Skipping." return fi log "Generating .env..." local secret_key install_id secret_key="$(openssl rand -hex 32)" install_id="sov-$(openssl rand -hex 8)" cat > "$env_file" </dev/null chmod 600 "${cert_dir}/key.pem" ok "TLS certificate generated." } # ── Start Containers ────────────────────────────────────────── start_containers() { log "Starting containers..." cd "$INSTALL_DIR" docker compose pull ollama 2>/dev/null || warn "Could not pull ollama image" docker compose build jarvis 2>/dev/null || warn "Could not build jarvis" docker compose up -d log "Waiting for Ollama..." local waited=0 while (( waited < 90 )); do curl -sf http://127.0.0.1:11434/api/tags > /dev/null 2>&1 && { ok "Ollama healthy."; break; } sleep 5; waited=$((waited + 5)) done (( waited >= 90 )) && warn "Ollama timeout. Check: docker compose logs ollama" log "Pulling model: $OLLAMA_DEFAULT_MODEL..." docker exec sovereign-ollama ollama pull "$OLLAMA_DEFAULT_MODEL" 2>/dev/null || warn "Model pull failed." ok "Containers started." } # ── Updater Timer ───────────────────────────────────────────── install_updater() { log "Installing updater timer..." tee /etc/systemd/system/sovereign-updater.service > /dev/null < /dev/null < "${INSTALL_DIR}/data/.sovereign-version" mkdir -p /etc/sovereign printf '{"version":"1.0.0","installed":"%s","install_id":"%s","tier":"%s"}\n' \ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$SOVEREIGN_INSTALL_ID" "$SOVEREIGN_TIER" \ > /etc/sovereign/version.json ok "Version 1.0.0 written." } # ── Summary ─────────────────────────────────────────────────── print_summary() { echo "" echo "============================================================" echo " Trifti Sovereign — Installation Complete" echo "============================================================" echo " Tier: $SOVEREIGN_TIER" echo " Model: $OLLAMA_DEFAULT_MODEL" echo " Install ID: $SOVEREIGN_INSTALL_ID" echo " Directory: $INSTALL_DIR" echo "" echo " Access: http://localhost:7870" echo " Logs: docker compose -f $INSTALL_DIR/docker-compose.yml logs -f" echo " Update: sudo $INSTALL_DIR/sovereign-updater.sh" echo "============================================================" } # ── Main ────────────────────────────────────────────────────── main() { detect_hardware check_prerequisites setup_directory generate_env source <(grep '^SOVEREIGN_INSTALL_ID=' "${INSTALL_DIR}/.env") generate_tls start_containers install_updater write_version print_summary } main "$@"