# NODA2 Audit Findings # Date: 2026-02-27 # Auditor: Sofiia (Cursor session) # ── PASS (що працює) ────────────────────────────────────────────────────── pass: - id: PASS-01 title: "NATS leafnode NODA2→NODA1 connected" detail: "spoke=true, rtt=58ms, cross-node pub/sub tested and working" - id: PASS-02 title: "Docker stack healthy" detail: "All 12 containers running. 10 have healthcheck PASS. NATS no healthcheck docker-level (compensated by leafnode check)." - id: PASS-03 title: "Router NODA2 operational" detail: "nats_connected=true, NODE_ID=NODA2, 14 agents registered, mounts local router-config.yml" - id: PASS-04 title: "Ollama 11 models available" detail: "qwen3.5:35b-a3b (primary), qwen3:14b, llava:13b (vision), deepseek-r1:70b, deepseek-coder:33b, glm-4.7-flash, gemma3, mistral-nemo, starcoder2, phi3, gpt-oss" - id: PASS-05 title: "Swapper healthy, active_model=qwen3-14b" detail: "/health=healthy, /models=200, /vision/models=200, /stt/models=200, /tts/models=200" - id: PASS-06 title: "Sofiia agent registered in gateway (14 agents)" detail: "sofiia present in agent_registry.yml, 1579-line prompt, class=top_level, telegram enabled" - id: PASS-07 title: "Memory + Qdrant operational" detail: "6 collections present (sofiia_messages:2 points, others empty). sofiia_docs ready for indexing." - id: PASS-08 title: "Sofiia Console UI running" detail: "http://localhost:8002, Python process, connects to NODA1 router (http://144.76.224.179:9102) and NODA2 router" - id: PASS-09 title: "Gitea (self-hosted Git) running" detail: "http://localhost:3000, version 1.25.3. Internal code repo." - id: PASS-10 title: "Spacebot + OpenCode running" detail: "Spacebot uses sofiia-console BFF. OpenCode.app active. Both integrated with NODA2 stack." # ── PARTIAL (частково) ──────────────────────────────────────────────────── partial: - id: PART-01 title: "Vision capability: llava:13b available but outdated" detail: "llava:13b in Ollama (vision_capable=true via CLIP). But swapper /vision/models returns empty - llava not added to swapper_config_node2.yaml. qwen3-vl:8b not installed." action: "Install qwen3-vl:8b OR add llava:13b to swapper vision config" - id: PART-02 title: "Router LLM profiles use 172.17.0.1 (Docker bridge IP, Linux-style)" detail: "router-config.yml has base_url: http://172.17.0.1:11434 for all profiles. On macOS Docker, this is host.docker.internal. Works because Ollama binds 127.0.0.1 and Docker on Mac uses host.docker.internal correctly — BUT 172.17.0.1 may not work in all network configurations." action: "Replace 172.17.0.1:11434 with host.docker.internal:11434 in router-config.yml for NODA2" - id: PART-03 title: "llama-server:11435 duplicates Ollama (same Qwen3.5-35B model)" detail: "Wastes memory. Two instances of same model. No routing config points to 11435." action: "Either remove llama-server or add it as dedicated profile in router-config.yml with specific purpose" - id: PART-04 title: "Qdrant memory collections mostly empty" detail: "sofiia_messages:2, all others 0. Memory not being used/indexed actively." action: "Enable memory indexing in memory-service config or start ingesting docs" - id: PART-05 title: "Sofiia Console not fully integrated" detail: "UI serves HTML on :8002. Has NODES_NODA1_ROUTER_URL configured. But no node-ops-worker for NATS-based control. SSH password in env." action: "Implement node-ops-worker (see FAIL-02)" # ── FAIL (зламано) ──────────────────────────────────────────────────────── fail: - id: FAIL-01 title: "Vision pipeline broken end-to-end" detail: "Swapper /vision/models=[] (empty). No vision model configured in swapper_config_node2.yaml. Router has no vision profile pointing to NODA2. Photo requests would fail silently or go to NODA1." priority: P0 action: | 1. Add llava:13b to swapper_config_node2.yaml vision section 2. OR install: ollama pull qwen3-vl:8b and add to swapper config 3. Add NODA2 vision profile to router-config.yml - id: FAIL-02 title: "Node-ops-worker not implemented" detail: "Sofiia has no NATS-based node control. Only mechanism is SSH root (password in env = SECURITY RISK). No subjects: node.noda1.ops.request, node.noda2.ops.request" priority: P1 action: | 1. Implement services/node_ops_worker/ (Python NATS subscriber) 2. Allowlist: docker ps/logs/restart, health curl, tail logs 3. Deploy to both NODA1 and NODA2 4. Remove NODES_NODA1_SSH_PASSWORD from env - id: FAIL-03 title: "NODA2 router-config.yml profiles use 172.17.0.1 (Linux Docker bridge)" detail: "On macOS, Docker containers use host.docker.internal for host access. 172.17.0.1 may or may not resolve depending on Docker version." priority: P1 action: "Replace 172.17.0.1:11434 → host.docker.internal:11434 in router-config.yml" # ── SECURITY risks ──────────────────────────────────────────────────────── security: - id: SEC-01 title: "SSH root password in sofiia-console container env" detail: "NODES_NODA1_SSH_PASSWORD=[secret present] in sofiia-console Docker env. Any process inside the container can read it." severity: HIGH priority: P1 action: | 1. Remove NODES_NODA1_SSH_PASSWORD from docker env 2. Use SSH key-based auth instead (mount private key as secret) 3. Better: implement NATS node-ops-worker (FAIL-02) to eliminate SSH dependency - id: SEC-02 title: "DATABASE_URL with plaintext password in router env" detail: "DATABASE_URL=postgresql://daarion:XXXXX@... in router container. Acceptable for local dev, risk if container is compromised." severity: MEDIUM priority: P2 action: "Use Docker secrets or env file with restricted permissions" - id: SEC-03 title: "NODA2 external IP (145.224.111.147) — ports exposed" detail: "All Docker ports bound to 0.0.0.0. If NODA2 has external IP, ports 9300 (gateway), 9102 (router), 8890 (swapper), 8000 (memory) are publicly accessible." severity: HIGH priority: P1 action: "Add firewall rules (macOS pfctl or router-level) to restrict inbound to trusted IPs only" # ── Summary ─────────────────────────────────────────────────────────────── summary: current_state: - "NODA2 = MacBook Pro M4 Max, 64GB RAM, macOS 26.3" - "12 Docker containers running, 10 healthy" - "NATS leafnode connected to NODA1 (rtt=58ms, cross-node pub/sub PASS)" - "Ollama: 11 models, primary=qwen3.5:35b-a3b + qwen3:14b. Vision=llava:13b (legacy)" - "llama-server:11435 running Qwen3.5-35B-A3B via llama.cpp (duplicates Ollama)" - "Swapper active_model=qwen3-14b, vision/models=EMPTY (gap)" - "Sofiia: agent_registry entry, 1579-line prompt, gateway registered" - "Sofiia Console UI on :8002, connects to NODA1+NODA2 routers" - "Spacebot (Telegram) → sofiia-console BFF → NODA2 stack" - "Memory: 6 Qdrant collections (mostly empty), Neo4j running" gaps: - "P0: Vision pipeline broken — swapper /vision/models=empty, qwen3-vl:8b not installed" - "P1: node-ops-worker not implemented — Sofiia controls NODA1 via SSH root password (SECURITY)" - "P1: router-config.yml on NODA2 uses 172.17.0.1 (should be host.docker.internal)" - "P1: NODA2 ports exposed on 0.0.0.0 without firewall — security risk" - "P1: SSH root password in sofiia-console env" - "P2: llama-server:11435 duplicates Ollama — memory waste" - "P2: Qdrant memory collections empty — memory/RAG not being used" - "P2: No cross-node vision routing (NODA1 can't send vision tasks to NODA2 via NATS yet)" - "P2: Swapper config missing llava:13b in vision section" - "P2: swapper_config_node2.yaml references gemma2:27b and qwen2.5-coder:32b — NOT installed"