diff --git a/add_node.sh b/add_node.sh new file mode 100755 index 0000000..2e20bf0 --- /dev/null +++ b/add_node.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Script to provision an attached Agent Node to the Cortex Hub. + +set -e + +echo "==========================================" +echo "⚡ Cortex Hub - Add Agent Node" +echo "==========================================" +echo "" + +if [ -z "$1" ]; then + read -p "Enter Node ID (e.g. sandbox-node): " AGENT_NODE_ID +else + AGENT_NODE_ID=$1 +fi + +if [ -z "$2" ]; then + read -p "Enter Agent Auth Token (Generated from UI): " AGENT_AUTH_TOKEN +else + AGENT_AUTH_TOKEN=$2 +fi + +if [ -z "$3" ]; then + read -p "Enter gRPC Endpoint [Default for local docker: ai_hub_service:50051]: " GRPC_ENDPOINT + GRPC_ENDPOINT=${GRPC_ENDPOINT:-ai_hub_service:50051} +else + GRPC_ENDPOINT=$3 +fi + +export AGENT_NODE_ID +export AGENT_AUTH_TOKEN +export GRPC_ENDPOINT + +# Load the symmetric SECRET_KEY if present for encryption +if [ -f ".env" ]; then + export $(grep -v '^#' .env | xargs) +fi +export AGENT_SECRET_KEY=${SECRET_KEY:-default-insecure-key} + +echo "" +echo "Starting Node: ${AGENT_NODE_ID}..." + +if command -v docker-compose &> /dev/null; then + docker-compose -f docker-compose.node.yml -p "cortex-node-${AGENT_NODE_ID}" up -d --build +elif docker compose version &> /dev/null; then + docker compose -f docker-compose.node.yml -p "cortex-node-${AGENT_NODE_ID}" up -d --build +else + echo "❌ Error: Docker Compose is not installed." + exit 1 +fi + +# Attach it to the main cortex-hub_default network to ensure it can hit ai_hub_service directly. +if docker network inspect cortex-hub_default &> /dev/null; then + docker network connect cortex-hub_default "cortex_sandbox_node" || true +fi + +echo "" +echo "✅ Node '${AGENT_NODE_ID}' has been spun up in the background." +echo "Use the Web Dashboard to verify its connection status." diff --git a/ai-hub/app/core/services/user.py b/ai-hub/app/core/services/user.py index dd2e49b..e20e51d 100644 --- a/ai-hub/app/core/services/user.py +++ b/ai-hub/app/core/services/user.py @@ -72,7 +72,10 @@ ) db.add(new_admin) db.commit() + db.refresh(new_admin) + admin = new_admin print(f"[Day 1] Bootstrapped local admin account for {admin_email}") + except Exception as e: db.rollback() print(f"Failed to bootstrap local admin: {e}") diff --git a/docker-compose.node.yml b/docker-compose.node.yml new file mode 100644 index 0000000..ce00aef --- /dev/null +++ b/docker-compose.node.yml @@ -0,0 +1,30 @@ +version: '3.8' + +services: + agent-node: + build: ./agent-node + container_name: ${AGENT_NODE_ID:-cortex_sandbox_node} + restart: always + environment: + - AGENT_NODE_ID=${AGENT_NODE_ID:-sandbox-node} + - AGENT_NODE_DESC=${AGENT_NODE_DESC:-Attached Local Sandbox Node} + - GRPC_ENDPOINT=${GRPC_ENDPOINT:-ai-hub:50051} + - AGENT_AUTH_TOKEN=${AGENT_AUTH_TOKEN} + - AGENT_SECRET_KEY=${AGENT_SECRET_KEY:-default-insecure-key} + - AGENT_TLS_ENABLED=${AGENT_TLS_ENABLED:-false} + volumes: + - ./agent-node:/app/agent-node-source:ro + - ${AGENT_NODE_ID:-sandbox-node}_sync:/app/sync + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + # Allows it to route to localhost:50051 if running outside the main docker network + extra_hosts: + - "host.docker.internal:host-gateway" + +volumes: + # Dynamic volume generation per node + sandbox-node_sync: + driver: local diff --git a/docker-compose.yml b/docker-compose.yml index 0c6df07..838391d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,7 @@ - HUB_PUBLIC_URL=http://localhost:8002 - HUB_GRPC_ENDPOINT=localhost:50051 - SUPER_ADMINS=${SUPER_ADMINS:-admin@example.com} + - CORTEX_ADMIN_PASSWORD=${CORTEX_ADMIN_PASSWORD} - SECRET_KEY=${SECRET_KEY:-default-insecure-key} - DEBUG_GRPC=true volumes: @@ -45,28 +46,6 @@ cpus: '1.0' memory: 1G - # Default Local Sandbox Node (Day 0 Experience) - sandbox-node: - build: ./agent-node - container_name: cortex_sandbox_node - restart: always - environment: - - AGENT_NODE_ID=sandbox-node - - AGENT_NODE_DESC=Default Sandbox Node - - GRPC_ENDPOINT=ai-hub:50051 - - AGENT_AUTH_TOKEN=sandbox-token-1234 - - AGENT_SECRET_KEY=${SECRET_KEY:-default-insecure-key} - - AGENT_TLS_ENABLED=false - volumes: - - ./agent-node:/app/agent-node-source:ro - - sandbox_sync:/app/sync - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - depends_on: - - ai-hub # Dedicated Browser Service (M6 Refactor) browser-service: @@ -92,8 +71,7 @@ volumes: ai_hub_data: driver: local - sandbox_sync: - driver: local + browser_shm: driver: local driver_opts: diff --git a/docs/auth_tls_todo.md b/docs/auth_tls_todo.md index e6bdfb2..fc10662 100644 --- a/docs/auth_tls_todo.md +++ b/docs/auth_tls_todo.md @@ -6,7 +6,7 @@ These tasks ensure the out-of-the-box local developer experience functions fully. - [x] **Infrastructure**: Fix the `.env` override trap in `docker-compose.yml`. - [x] **Setup Script**: Update `setup.sh` to accept optional `config.yaml` to cure the "Brain Dead" state. -- [x] **Infrastructure**: Include a bundled `sandbox-node` container in `docker-compose.yml` so the Hub isn't an "Empty Shell" on startup. +- [x] **Infrastructure**: Create a standalone `add_node.sh` script to explicitly provision a `sandbox-node` instead of bundling it in `docker-compose.yml` to prevent node respawns. ## Phase 2: Day 1 Local Auth Fallback Enable local authentication using the `CORTEX_ADMIN_PASSWORD` generated by the setup script. diff --git a/setup.sh b/setup.sh index d5a5c20..7edc476 100644 --- a/setup.sh +++ b/setup.sh @@ -67,3 +67,8 @@ echo "" echo "Please save your password safely! You can change it later in the UI." echo "==========================================" +echo "" +echo "💡 Looking to attach a Node? " +echo " 1. Login to the Hub UI and generate an Invite Token." +echo " 2. Run: ./add_node.sh" +echo "=========================================="