diff --git a/Makefile b/Makefile index 5da02d94..ad9c7a3e 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,12 @@ stop: @echo "Stopping services..." @-lsof -ti:$(PORT) | xargs kill -9 2>/dev/null @-lsof -ti:$(FRONTEND_PORT) | xargs kill -9 2>/dev/null - @echo "✓ App processes stopped. Shared PostgreSQL is still running on localhost:5432." + @case "$(DATABASE_URL)" in \ + ""|*@localhost:*|*@localhost/*|*@127.0.0.1:*|*@127.0.0.1/*|*@\[::1\]:*|*@\[::1\]/*) \ + echo "✓ App processes stopped. Shared PostgreSQL is still running on localhost:$(POSTGRES_PORT)." ;; \ + *) \ + echo "✓ App processes stopped. Remote PostgreSQL was not affected." ;; \ + esac # Full verification: typecheck + unit tests + Go tests + E2E check: diff --git a/SELF_HOSTING.md b/SELF_HOSTING.md index 8e671b53..f6884441 100644 --- a/SELF_HOSTING.md +++ b/SELF_HOSTING.md @@ -257,8 +257,14 @@ Each team member who wants to run AI agents locally needs to: ```bash # Point CLI to your server + # + # For production deployments with TLS: export MULTICA_APP_URL=https://app.example.com export MULTICA_SERVER_URL=wss://api.example.com/ws + # + # For local deployments without TLS: + # export MULTICA_APP_URL=http://localhost:3000 + # export MULTICA_SERVER_URL=ws://localhost:8080/ws # Login (opens browser) multica login @@ -267,6 +273,8 @@ Each team member who wants to run AI agents locally needs to: multica daemon start ``` + > **Note:** Use `https://` and `wss://` for production deployments behind a TLS-terminating reverse proxy. For local or development deployments without TLS, use `http://` and `ws://` instead. + The daemon auto-detects installed agent CLIs and registers itself with the server. When an agent is assigned a task in Multica, the daemon picks it up, creates an isolated workspace, runs the agent, and reports results back. ## Upgrading diff --git a/scripts/ensure-postgres.sh b/scripts/ensure-postgres.sh index 78d7345b..17525cf3 100644 --- a/scripts/ensure-postgres.sh +++ b/scripts/ensure-postgres.sh @@ -17,26 +17,88 @@ set +a POSTGRES_DB="${POSTGRES_DB:-multica}" POSTGRES_USER="${POSTGRES_USER:-multica}" POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-multica}" +DATABASE_URL="${DATABASE_URL:-}" export PGPASSWORD="$POSTGRES_PASSWORD" -echo "==> Ensuring shared PostgreSQL container is running on localhost:5432..." -docker compose up -d postgres +db_host="" +db_port="${POSTGRES_PORT:-5432}" +db_name="$POSTGRES_DB" -echo "==> Waiting for PostgreSQL to be ready..." -until docker compose exec -T postgres pg_isready -U "$POSTGRES_USER" -d postgres > /dev/null 2>&1; do - sleep 1 -done +parse_database_url() { + local rest authority hostport path port_part -echo "==> Ensuring database '$POSTGRES_DB' exists..." -db_exists="$(docker compose exec -T postgres \ - psql -U "$POSTGRES_USER" -d postgres -Atqc "SELECT 1 FROM pg_database WHERE datname = '$POSTGRES_DB'")" + rest="${DATABASE_URL#*://}" + rest="${rest%%\?*}" + authority="${rest%%/*}" + path="${rest#*/}" -if [ "$db_exists" != "1" ]; then - docker compose exec -T postgres \ - psql -U "$POSTGRES_USER" -d postgres -v ON_ERROR_STOP=1 \ - -c "CREATE DATABASE \"$POSTGRES_DB\"" \ - > /dev/null + if [ "$authority" = "$rest" ]; then + path="" + fi + + hostport="${authority##*@}" + + if [[ "$hostport" == \[* ]]; then + db_host="${hostport#\[}" + db_host="${db_host%%]*}" + port_part="${hostport#*\]}" + if [[ "$port_part" == :* ]] && [ -n "${port_part#:}" ]; then + db_port="${port_part#:}" + fi + else + db_host="${hostport%%:*}" + if [[ "$hostport" == *:* ]] && [ -n "${hostport##*:}" ]; then + db_port="${hostport##*:}" + fi + fi + + if [ -n "$path" ]; then + db_name="${path%%/*}" + fi +} + +if [ -n "$DATABASE_URL" ]; then + parse_database_url fi -echo "✓ PostgreSQL ready. Application database: $POSTGRES_DB" +is_local() { + [ -z "$DATABASE_URL" ] || [ "$db_host" = "localhost" ] || [ "$db_host" = "127.0.0.1" ] || [ "$db_host" = "::1" ] +} + +if is_local; then + # ---------- Local: use Docker ---------- + echo "==> Ensuring shared PostgreSQL container is running on localhost:5432..." + docker compose up -d postgres + + echo "==> Waiting for PostgreSQL to be ready..." + until docker compose exec -T postgres pg_isready -U "$POSTGRES_USER" -d postgres > /dev/null 2>&1; do + sleep 1 + done + + echo "==> Ensuring database '$POSTGRES_DB' exists..." + db_exists="$(docker compose exec -T postgres \ + psql -U "$POSTGRES_USER" -d postgres -Atqc "SELECT 1 FROM pg_database WHERE datname = '$POSTGRES_DB'")" + + if [ "$db_exists" != "1" ]; then + docker compose exec -T postgres \ + psql -U "$POSTGRES_USER" -d postgres -v ON_ERROR_STOP=1 \ + -c "CREATE DATABASE \"$POSTGRES_DB\"" \ + > /dev/null + fi + + echo "✓ PostgreSQL ready (local Docker). Database: $POSTGRES_DB" +else + # ---------- Remote: skip Docker, verify connectivity ---------- + echo "==> Remote database detected (host: $db_host). Skipping Docker." + if command -v pg_isready > /dev/null 2>&1; then + echo "==> Waiting for PostgreSQL at $db_host:$db_port to be ready..." + until pg_isready -d "$DATABASE_URL" > /dev/null 2>&1; do + sleep 1 + done + echo "✓ PostgreSQL ready (remote: $db_host:$db_port). Database: $db_name" + else + echo "==> pg_isready not found. Skipping remote connectivity preflight." + echo "✓ PostgreSQL configured (remote: $db_host:$db_port). Database: $db_name" + fi +fi