# NEXA Deployment Guide ## πŸš€ Architektur-Überblick ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ MEMOS (Port 5230) β”‚ β”‚ Interface, Voice-Input, Webhooks β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” ↓ ↓ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ n8n β”‚ β”‚ SAIA β”‚ β”‚ Nextcloudβ”‚ β”‚Logic β”‚ β”‚LiteLLM β”‚ β”‚Tasks/Mailβ”‚ β”‚7879 β”‚ β”‚4000 β”‚ β”‚80/443 β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β” ↓ ↓ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ QDRANT β”‚ β”‚ GraphDB β”‚ β”‚ Obsidian β”‚ β”‚Vector β”‚ β”‚ (Ontotext) β”‚ β”‚(local sync) β”‚ β”‚6333 β”‚ β”‚7200 β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ“‹ Voraussetzungen ### Hardware - **CPU**: Min. 4 Cores (empfohlen 8) - **RAM**: Min. 16 GB (empfohlen 32 GB) - **Storage**: Min. 100 GB SSD (fΓΌr Datenbanken & Backups) - **Network**: Stabile Verbindung, HTTPS-ready ### Software - Docker & Docker Compose (Version 20.10+) - Git - Bash - curl/wget - `.env` Datei aus `.env.example` (mit Secrets gefΓΌllt) ### Accounts & Credentials - Nextcloud Instanz mit Admin-Zugang - OpenAI API-Key (fΓΌr SAIA/LiteLLM) - Obsidian Vault Zugang lokalem Filesystem --- ## 🐳 Schritt 1: Docker Compose Setup ### 1.1 Repository klonen ```bash git clone https://github.com/noheton/nexa.git cd nexa/nexa-core ``` ### 1.2 .env konfigurieren ```bash cp .env.example .env # WICHTIG: Alle Secrets eintragen! # - MEMOS_API_KEY # - SAIA_API_KEY # - NC_APP_PASSWORD # - QDRANT_API_KEY # - GRAPHDB_PASSWORD nano .env ``` ### 1.3 docker-compose.yml erstellen ```bash cat > docker-compose.yml << 'EOF' version: '3.8' services: # ===== MEMOS (Interface) ===== memos: image: neosmemo/memos:latest container_name: nexa-memos ports: - "5230:5230" volumes: - ./data/memos:/root/.memos environment: MEMOS_PORT: "5230" MEMOS_DRIVER: sqlite MEMOS_DSN: /root/.memos/memos.db networks: - nexa restart: unless-stopped # ===== n8n (Workflow Engine) ===== n8n: image: n8nio/n8n:latest container_name: nexa-n8n ports: - "5678:5678" volumes: - ./data/n8n:/home/node/.n8n - ./n8n-workflows:/workflows environment: N8N_BASIC_AUTH_ACTIVE: "true" N8N_BASIC_AUTH_USER: "${N8N_USER:-admin}" N8N_BASIC_AUTH_PASSWORD: "${N8N_PASSWORD:-nexa_secure_pass}" N8N_HOST: "${N8N_DOMAIN:-localhost}" N8N_PORT: "5678" N8N_PROTOCOL: https N8N_WEBHOOK_URL: "https://${N8N_DOMAIN:-localhost}/webhook" GENERIC_TIMEZONE: Europe/Berlin DB_TYPE: sqlite DB_SQLITE_PATH: /home/node/.n8n/nexa.db networks: - nexa restart: unless-stopped depends_on: - memos # ===== QDRANT (Vector DB) ===== qdrant: image: qdrant/qdrant:latest container_name: nexa-qdrant ports: - "6333:6333" volumes: - ./data/qdrant:/qdrant/storage environment: QDRANT_API_KEY: "${QDRANT_API_KEY}" networks: - nexa restart: unless-stopped # ===== GraphDB (Ontotext SPARQL) ===== graphdb: image: ontotext/graphdb:10-ee container_name: nexa-graphdb ports: - "7200:7200" volumes: - ./data/graphdb:/opt/graphdb/data - ./config/graphdb-init.ttl:/graphdb-import/import.ttl environment: GDB_JAVA_OPTS: "-Xmx8g -Xms4g" GDB_PASSWORD: "${GRAPHDB_PASSWORD}" networks: - nexa restart: unless-stopped # ===== SAIA / LiteLLM Proxy (optional - wenn lokal) ===== # (Falls SAIA extern gehostet: nicht nΓΆtig) networks: nexa: driver: bridge volumes: nexa_memos: nexa_n8n: nexa_qdrant: nexa_graphdb: EOF ``` --- ## πŸ”§ Schritt 2: Individuelle Service-Konfiguration ### 2.1 MEMOS Initialisierung ```bash # Nach memos startup (ca. 30s) curl -X POST http://localhost:5230/api/v1/auth/signup \ -H "Content-Type: application/json" \ -d '{ "username": "nexa", "password": "secure_password" }' # API-Token generieren TOKEN=$(curl -X POST http://localhost:5230/api/v1/auth/signin \ -H "Content-Type: application/json" \ -d '{"username":"nexa","password":"secure_password"}' \ | jq -r '.data.token') echo "MEMOS_API_KEY=$TOKEN" >> .env ``` ### 2.2 Qdrant Konfiguration ```bash # Warte auf Qdrant startup sleep 10 # Erstelle Collection fΓΌr Nexa curl -X PUT "http://localhost:6333/collections/nexa_knowledge" \ -H "Content-Type: application/json" \ -H "api-key: ${QDRANT_API_KEY}" \ -d '{ "vectors": { "size": 1536, "distance": "Cosine" }, "optimizers_config": { "deleted_threshold": 0.2, "vacuum_threshold": 0.5, "default_segment_number": 5, "max_segment_number": 30, "memmap_threshold": 268435456, "indexing_threshold": 20000, "flush_interval_sec": 10, "max_optimization_threads": 4 } }' echo "βœ… Qdrant Collection erstellt" ``` ### 2.3 GraphDB Initialisierung ```bash # Warte auf GraphDB startup sleep 15 # Erstelle Repository GRAPHDB_URL="http://localhost:7200" curl -X POST "$GRAPHDB_URL/rest/repositories" \ -H "Content-Type: application/json" \ -u "admin:${GRAPHDB_PASSWORD}" \ -d '{ "id": "nexa_knowledge", "title": "Nexa Knowledge Graph", "type": "free", "params": { "baseURL": { "label": "Base URL", "value": "http://nexa.local/" }, "defaultLanguage": { "label": "Default language", "value": "en" } } }' echo "βœ… GraphDB Repository erstellt" ``` ### 2.4 n8n Workflows Import ```bash # Warte auf n8n startup (ca. 60s) sleep 60 # Importiere Discovery Workflow curl -X POST http://localhost:5678/api/v1/workflows \ -H "Content-Type: application/json" \ -u "admin:${N8N_PASSWORD}" \ -d @n8n-workflows/phase-1/1_1_discovery_workflow.json # Importiere Memos Bridge curl -X POST http://localhost:5678/api/v1/workflows \ -H "Content-Type: application/json" \ -u "admin:${N8N_PASSWORD}" \ -d @n8n-workflows/phase-1/1_2_memos_bridge_v2.json # Importiere Email Butler curl -X POST http://localhost:5678/api/v1/workflows \ -H "Content-Type: application/json" \ -u "admin:${N8N_PASSWORD}" \ -d @n8n-workflows/phase-2/2_1_email_butler.json echo "βœ… n8n Workflows importiert" ``` --- ## πŸš€ Schritt 3: Deployment starten ### 3.1 Docker Compose Up ```bash docker-compose up -d # Logs ΓΌberwachen docker-compose logs -f ``` ### 3.2 Health Checks ```bash # Health Check Script check_service() { local name=$1 local url=$2 echo "Checking $name..." if curl -s "$url" > /dev/null; then echo "βœ… $name: OK" else echo "❌ $name: FAILED" fi } sleep 30 check_service "Memos" "http://localhost:5230" check_service "n8n" "http://localhost:5678" check_service "Qdrant" "http://localhost:6333/health" check_service "GraphDB" "http://localhost:7200/health" echo "" echo "🎯 Service URLs:" echo " - Memos: http://localhost:5230" echo " - n8n: http://localhost:5678" echo " - Qdrant: http://localhost:6333" echo " - GraphDB: http://localhost:7200" ``` --- ## βš™οΈ Schritt 4: Konfiguration via Command System ### 4.1 Discovery-Workflow triggern ```bash # In Memos Memo erstellen: # #nexa:config # Oder via API: curl -X POST http://localhost:5678/api/v1/workflows/run \ -H "Content-Type: application/json" \ -u "admin:${N8N_PASSWORD}" \ -d '{ "workflowId": "discovery-workflow-id" }' ``` ### 4.2 Automatische Konfiguration laden Nach erfolgreichem Discovery werden Nextcloud-Listen-IDs: ```bash # Es wird automatisch aktualisiert in: # - Qdrant: als Metadata # - n8n: als Umgebungsvariablen # - Memos: als Confirmation-Memo ``` --- ## πŸ” Schritt 5: Reverse Proxy Setup (Production) ### 5.1 Nginx Configuration ```bash cat > /etc/nginx/sites-available/nexa.conf << 'EOF' upstream memos_backend { server localhost:5230; } upstream n8n_backend { server localhost:5678; } upstream qdrant_backend { server localhost:6333; } upstream graphdb_backend { server localhost:7200; } server { listen 443 ssl http2; server_name nexa.yourdomain.com; ssl_certificate /etc/letsencrypt/live/nexa.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/nexa.yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # MEMOS location / { proxy_pass http://memos_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # n8n location /n8n/ { proxy_pass http://n8n_backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # Qdrant (nur intern, wenn nΓΆtig) location /qdrant/ { allow 10.0.0.0/8; deny all; proxy_pass http://qdrant_backend/; } # GraphDB (nur intern) location /graphdb/ { allow 10.0.0.0/8; deny all; proxy_pass http://graphdb_backend/; } # Webhook endpoint fΓΌr externe Services location /webhook/ { proxy_pass http://n8n_backend/webhook/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } server { listen 80; server_name nexa.yourdomain.com; return 301 https://$server_name$request_uri; } EOF # Aktivieren sudo ln -s /etc/nginx/sites-available/nexa.conf /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` --- ## πŸ“Š Schritt 6: Monitoring & Backups ### 6.1 Backup Script ```bash cat > scripts/backup_nexa.sh << 'EOF' #!/bin/bash BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)" mkdir -p "$BACKUP_DIR" echo "πŸ”„ Backing up Nexa..." # Backup Datenbanken docker-compose exec -T qdrant tar czf - /qdrant/storage > "$BACKUP_DIR/qdrant.tar.gz" docker-compose exec -T graphdb tar czf - /opt/graphdb/data > "$BACKUP_DIR/graphdb.tar.gz" docker-compose exec -T memos tar czf - /root/.memos > "$BACKUP_DIR/memos.tar.gz" docker-compose exec -T n8n tar czf - /home/node/.n8n > "$BACKUP_DIR/n8n.tar.gz" # Backup Configs cp .env "$BACKUP_DIR/.env.backup" cp docker-compose.yml "$BACKUP_DIR/docker-compose.yml.backup" echo "βœ… Backup completed: $BACKUP_DIR" # Optional: zu S3 hochladen # aws s3 sync "$BACKUP_DIR" s3://nexa-backups/$(date +%Y%m%d)/ EOF chmod +x scripts/backup_nexa.sh # TΓ€gliches Backup (cron) echo "0 2 * * * /path/to/nexa/scripts/backup_nexa.sh" | crontab - ``` ### 6.2 Health Check Monitoring ```bash cat > scripts/monitor_nexa.sh << 'EOF' #!/bin/bash # Monitore Service Status SERVICES=("memos" "n8n" "qdrant" "graphdb") for service in "${SERVICES[@]}"; do if docker-compose ps $service | grep -q "Up"; then echo "βœ… $service: Running" else echo "❌ $service: DOWN" # Restart bei Fehler docker-compose restart $service fi done EOF chmod +x scripts/monitor_nexa.sh # Alle 5 Minuten echo "*/5 * * * * /path/to/nexa/scripts/monitor_nexa.sh" | crontab - ``` --- ## πŸ§ͺ Schritt 7: Validation & Testing ### 7.1 End-to-End Test ```bash cat > scripts/test_nexa.sh << 'EOF' #!/bin/bash echo "πŸ§ͺ Testing Nexa Installation..." # Test 1: Memos API echo "Test 1: Memos API" if curl -s http://localhost:5230/api/v1/auth/status | jq . > /dev/null 2>&1; then echo "βœ… Memos: OK" else echo "❌ Memos: FAILED" fi # Test 2: n8n API echo "Test 2: n8n API" if curl -s -u "admin:${N8N_PASSWORD}" http://localhost:5678/api/v1/workflows | jq . > /dev/null 2>&1; then echo "βœ… n8n: OK" else echo "❌ n8n: FAILED" fi # Test 3: Qdrant Collection echo "Test 3: Qdrant Collection" if curl -s -H "api-key: ${QDRANT_API_KEY}" http://localhost:6333/collections/nexa_knowledge | jq . > /dev/null 2>&1; then echo "βœ… Qdrant: OK" else echo "❌ Qdrant: FAILED" fi # Test 4: GraphDB Repository echo "Test 4: GraphDB Repository" if curl -s -u "admin:${GRAPHDB_PASSWORD}" http://localhost:7200/repositories | jq . > /dev/null 2>&1; then echo "βœ… GraphDB: OK" else echo "❌ GraphDB: FAILED" fi echo "" echo "🎯 Create Test Memo:" MEMO=$(curl -s -X POST http://localhost:5230/api/v1/memos \ -H "Authorization: Bearer ${MEMOS_API_KEY}" \ -H "Content-Type: application/json" \ -d '{"content": "Test memo - #nexa:config"}' | jq -r '.id') echo "βœ… Test Memo created: $MEMO" echo "Check Memos UI to verify webhook triggers Discovery" EOF chmod +x scripts/test_nexa.sh ./scripts/test_nexa.sh ``` --- ## πŸ†˜ Troubleshooting ### Problem: Memos kann sich nicht mit n8n verbinden ```bash # ÜberprΓΌfe n8n Webhook URL in Memos config curl http://localhost:5678/webhook/memos # Falls 404: Workflow ist nicht aktiviert/deployed docker-compose logs n8n | grep webhook ``` ### Problem: GraphDB startet nicht ```bash # Memory-Limit erhΓΆhen docker-compose exec graphdb -e "GDB_JAVA_OPTS=-Xmx16g -Xms8g" restart # Logs checken docker-compose logs graphdb ``` ### Problem: Qdrant Collection leer ```bash # ÜberprΓΌfe ob Memos Bridge Workflow lΓ€uft docker-compose logs n8n | grep "memos_bridge" # Manuell test curl -X POST http://localhost:5230/api/v1/memos \ -H "Authorization: Bearer ${MEMOS_API_KEY}" \ -d '{"content": "- [ ] Test task #nexa"}' ``` --- ## πŸ“ˆ Next Steps 1. βœ… **Phase 1 deployt**: Memos Bridge & Discovery laufen 2. ⏳ **Phase 2 aktivieren**: Email Butler (wenn Nextcloud Mail konfiguriert) 3. ⏳ **Phase 3 setup**: Qdrant Embedding Pipeline 4. ⏳ **Phase 4 integration**: GraphRAG Queries testen 5. ⏳ **Phase 5**: Home Assistant Voice Integration --- ## πŸ“ž Support & Debugging ```bash # Alle Logs in eine Datei sammeln docker-compose logs > deployment_logs.txt # Spezifischer Service docker-compose logs n8n --tail 100 # Wiederherstellung aus Backup ./scripts/restore_nexa.sh backup/2024-01-15_120000/ ``` --- **Deployment abgeschlossen! Nexa ist nun betriebsbereit.** πŸš€