#!/bin/bash # docker/scripts/install.sh # One-click Auxx.ai installer. # # Usage: # bash <(curl -sL https://raw.githubusercontent.com/Auxx-Ai/auxx-ai/main/docker/scripts/install.sh) # ─── Check dependencies ────────────────────────────────── echo "Checking dependencies..." if ! command -v docker &>/dev/null; then echo -e "\t❌ Docker is not installed or not in PATH.\n\t\tSee https://docs.docker.com/get-docker/" exit 1 fi if ! docker compose version &>/dev/null; then echo -e "\t❌ Docker Compose v2 is not installed (n.b. docker-compose is deprecated)\n\t\tUpdate Docker or install docker-compose-plugin\n\t\tOn Linux: sudo apt-get install docker-compose-plugin\n\t\tSee https://docs.docker.com/compose/install/" exit 1 fi if ! docker info &>/dev/null; then echo -e "\t❌ Docker is not running.\n\t\tStart Docker Desktop or check https://docs.docker.com/config/daemon/start/" exit 1 fi if ! command -v curl &>/dev/null; then echo -e "\t❌ curl is not installed.\n\t\tOn macOS: brew install curl\n\t\tOn Linux: sudo apt install curl" exit 1 fi if ! command -v openssl &>/dev/null; then echo -e "\t❌ openssl is not installed.\n\t\tOn macOS: brew install openssl\n\t\tOn Linux: sudo apt install openssl" exit 1 fi # Check Docker Compose version >= 2 if [ "$(docker compose version --short | cut -d'.' -f1)" -lt 2 ]; then echo -e "\t❌ Docker Compose is outdated. Please update to version 2+.\n\t\tSee https://docs.docker.com/compose/install/linux/" exit 1 fi # Warn about legacy docker-compose if command -v docker-compose &>/dev/null; then if [ "$(docker-compose version --short | cut -d'.' -f1)" -lt 2 ]; then echo -e "\n\t⚠️ 'docker-compose' (legacy) is installed but outdated. Use 'docker compose' (plugin).\n\t\tSee https://docs.docker.com/compose/install/standalone/\n" fi fi # ─── Error handling ─────────────────────────────────────── set -e function on_exit { local exit_status=$? if [ $exit_status -ne 0 ]; then echo "❌ Something went wrong, exiting: $exit_status" fi } trap on_exit EXIT # ─── Version resolution ────────────────────────────────── # Use VERSION env var, or fetch latest from GitHub Releases API # Pin a specific version: VERSION=0.1.28 bash <(curl -sL ...) version=${VERSION:-$(curl -s "https://api.github.com/repos/Auxx-Ai/auxx-ai/releases/latest" \ | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4 | sed 's/^auxx-v//' || echo "latest")} if [ -z "$version" ]; then version="latest" fi branch=${BRANCH:-main} echo "🚀 Using image version: $version (branch: $branch)" # ─── Directory setup ───────────────────────────────────── dir_name="auxx" function ask_directory { read -p "📁 Enter the directory name to setup the project (default: $dir_name): " answer if [ -n "$answer" ]; then dir_name=$answer fi } ask_directory while [ -d "$dir_name" ]; do read -p "🚫 Directory '$dir_name' already exists. Do you want to overwrite it? (y/N) " answer if [ "$answer" = "y" ]; then echo "🗑️ Removing existing directory '$dir_name'" rm -rf "$dir_name" break else ask_directory fi done echo "📁 Creating directory '$dir_name'" mkdir -p "$dir_name" && cd "$dir_name" || { echo "❌ Failed to create/access directory '$dir_name'"; exit 1; } # ─── Download config files ─────────────────────────────── echo -e "\t• Downloading docker-compose.yml" curl -sLo docker-compose.yml "https://raw.githubusercontent.com/Auxx-Ai/auxx-ai/$branch/docker/docker-compose.yml" echo -e "\t• Downloading .env.example" curl -sLo .env "https://raw.githubusercontent.com/Auxx-Ai/auxx-ai/$branch/.env.example" # ─── Platform-aware sed ────────────────────────────────── do_sed() { if [[ $(uname) == "Darwin" ]]; then sed -i '' "$@" else sed -i'' "$@" fi } # ─── Set version tag ───────────────────────────────────── do_sed "s|^VERSION=.*|VERSION=$version|" .env # ─── Set deployment mode ───────────────────────────────── do_sed "s|^DEPLOYMENT_MODE=.*|DEPLOYMENT_MODE=self-hosted|" .env # ─── Generate secrets ──────────────────────────────────── echo -e "\t• Generating secrets" generate_secret() { openssl rand -hex "$1" } DATABASE_PASSWORD=$(generate_secret 16) REDIS_PASSWORD=$(generate_secret 16) BETTER_AUTH_SECRET=$(generate_secret 32) API_KEY_SALT=$(generate_secret 16) LAMBDA_INVOKE_SECRET=$(generate_secret 32) WORKFLOW_CREDENTIAL_ENCRYPTION_KEY=$(generate_secret 16) PUBLIC_WORKFLOW_JWT_SECRET=$(generate_secret 32) SDK_CLIENT_SECRET=$(generate_secret 32) # Generate Ed25519 keypair for cross-app login token signing LOGIN_TOKEN_KEYS=$(node -e " const { generateKeyPairSync } = require('crypto'); const { publicKey, privateKey } = generateKeyPairSync('ed25519'); const priv = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString().trim().replace(/\n/g, '\\\\n'); const pub = publicKey.export({ type: 'spki', format: 'pem' }).toString().trim().replace(/\n/g, '\\\\n'); console.log(priv + '|||' + pub); " 2>/dev/null || true) LOGIN_TOKEN_PRIVATE_KEY="${LOGIN_TOKEN_KEYS%%|||*}" LOGIN_TOKEN_PUBLIC_KEY="${LOGIN_TOKEN_KEYS##*|||}" BUILD_SESSION_SECRET=$(generate_secret 32) do_sed "s|^DATABASE_PASSWORD=.*|DATABASE_PASSWORD=$DATABASE_PASSWORD|" .env do_sed "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$REDIS_PASSWORD|" .env do_sed "s|^BETTER_AUTH_SECRET=.*|BETTER_AUTH_SECRET=$BETTER_AUTH_SECRET|" .env do_sed "s|^API_KEY_SALT=.*|API_KEY_SALT=$API_KEY_SALT|" .env do_sed "s|^LAMBDA_INVOKE_SECRET=.*|LAMBDA_INVOKE_SECRET=$LAMBDA_INVOKE_SECRET|" .env do_sed "s|^WORKFLOW_CREDENTIAL_ENCRYPTION_KEY=.*|WORKFLOW_CREDENTIAL_ENCRYPTION_KEY=$WORKFLOW_CREDENTIAL_ENCRYPTION_KEY|" .env do_sed "s|^PUBLIC_WORKFLOW_JWT_SECRET=.*|PUBLIC_WORKFLOW_JWT_SECRET=$PUBLIC_WORKFLOW_JWT_SECRET|" .env do_sed "s|^SDK_CLIENT_SECRET=.*|SDK_CLIENT_SECRET=$SDK_CLIENT_SECRET|" .env # Keys contain literal \n which sed would interpret as newlines — double-escape for sed ESCAPED_PRIVATE_KEY=$(printf '%s' "$LOGIN_TOKEN_PRIVATE_KEY" | sed 's/\\/\\\\/g') ESCAPED_PUBLIC_KEY=$(printf '%s' "$LOGIN_TOKEN_PUBLIC_KEY" | sed 's/\\/\\\\/g') do_sed "s|^LOGIN_TOKEN_PRIVATE_KEY=.*|LOGIN_TOKEN_PRIVATE_KEY=$ESCAPED_PRIVATE_KEY|" .env do_sed "s|^LOGIN_TOKEN_PUBLIC_KEY=.*|LOGIN_TOKEN_PUBLIC_KEY=$ESCAPED_PUBLIC_KEY|" .env do_sed "s|^BUILD_SESSION_SECRET=.*|BUILD_SESSION_SECRET=$BUILD_SESSION_SECRET|" .env # ─── Set DATABASE_URL for Docker networking ─────────────── do_sed "s|^DATABASE_URL=.*|DATABASE_URL=postgresql://postgres:${DATABASE_PASSWORD}@postgres:5432/auxx-ai|" .env # ─── Set Redis host for Docker networking ───────────────── do_sed "s|^REDIS_HOST=.*|REDIS_HOST=redis|" .env # ─── Set S3 to use MinIO ───────────────────────────────── do_sed "s|^S3_ENDPOINT=.*|S3_ENDPOINT=http://minio:9000|" .env do_sed "s|^S3_ACCESS_KEY_ID=.*|S3_ACCESS_KEY_ID=minioadmin|" .env do_sed "s|^S3_SECRET_ACCESS_KEY=.*|S3_SECRET_ACCESS_KEY=minioadmin|" .env do_sed "s|^CDN_URL=.*|CDN_URL=http://localhost:9000/auxx-public-local|" .env echo -e "\t• .env configuration completed" # ─── Port conflict detection ───────────────────────────── # Only updates .env — docker-compose.yml uses ${VAR:-default} interpolation from .env, # so no compose file modifications are needed. # Track the web port globally for browser opening later web_port=3000 # check_port