# NODA1 Full Audit — DAARION.city
**Дата:** 2026-02-27  
**Сервер:** node1-daarion | 144.76.224.179 | NVIDIA RTX 4000 SFF Ada (20GB VRAM)  
**Аудитор:** Sofiia — Chief AI Architect

---

## EXECUTIVE SUMMARY

| Напрям | Стан | Критичність |
|--------|------|-------------|
| Фото E2E (Telegram→Vision) | ✅ Працює, але є shortcut (не через NATS) | MEDIUM |
| PDF/Документи | ⚠️ render-pdf-worker idle, index-doc DNS fail | HIGH |
| Router/Profiles | ✅ OK — DeepSeek top-level, 27B crew, smollm2 CPU | LOW |
| STT/TTS | ✅ CPU-only (Whisper), TTS unloaded | LOW |
| Swapper | ⚠️ Потрібен — єдина точка Vision/STT/OCR/Document | KEEP |
| GPU policy | ✅ 27B GPU, smollm2 CPU, policy_ok=1 | OK |
| NODA1↔NODA2 | ⚠️ K3s cluster (flannel), NATS не з'єднані між нодами | HIGH |
| CTO Sofiia control plane | ⚠️ control-plane сервіс є, але тільки prompts+policy JWT | MEDIUM |

---

## 1. INVENTORY — Що реально запущено

### Контейнери (48 total, ключові):

```
swapper-service-node1       healthy  8890-8891
dagi-router-node1           healthy  9102→8000
dagi-nats-node1             up       4222
dagi-memory-service-node1   healthy  8000
dagi-qdrant-node1           healthy  6333
dagi-gateway-node1          healthy  9300
parser-pipeline             up       8101
ingest-service              up       8100
render-pdf-worker-node1     up       (no port)
render-pptx-worker-node1    up       (no port)
index-doc-worker-node1      up       (no port)
presentation-renderer-node1 healthy  9212
rag-service-node1           healthy  9500
dagi-vision-encoder-node1   healthy  8001
control-plane               up       9200
dagi-crawl4ai-node1         healthy  11235
oneok-gotenberg-node1       up       3010
plant-vision-node1          healthy  8085
crewai-nats-worker          up       9011
dagi-staging-crewai-service up       9010
artifact-registry-node1     healthy  9220
dagi-minio-node1            up       9000-9001
```

### Systemd:
- `ollama.service` — **active** (GPU, port 11434, qwen3.5:27b-q4_K_M, KEEP_ALIVE=10m)
- `ollama-cpu.service` — **active** (CPU, port 11435, smollm2:135m)
- `gpu-ollama-exporter.service` — **active** (port 9400)
- `ollama-warmup-27b.timer` — **active** (кожні 15хв)

---

## 2. ROUTER — Профілі, моделі, routing

### CURRENT STATE

**Env у контейнері dagi-router-node1:**
```
ENABLE_CREW_MODEL_ROUTING=1
CREW_SMALL_MODEL=smollm2:135m
CREWAI_WORKER_LLM_PROFILE=crew_local_27b
DEEPSEEK_API_KEY=sk-0db94...  (production key)
NATS_URL=nats://nats:4222
VISION_ENCODER_URL=http://vision-encoder:8001
```

**Профілі (router-config.yml):**
| Profile | Provider | Model | URL |
|---------|----------|-------|-----|
| `cloud_deepseek` | deepseek | deepseek-chat | api.deepseek.com |
| `cloud_mistral` | mistral | mistral-large-latest | api.mistral.ai |
| `crew_local_27b` | ollama | qwen3.5:27b-q4_K_M | 172.17.0.1:11434 (GPU) |
| `crew_vision_27b` | ollama | qwen3.5:27b-q4_K_M | 172.17.0.1:11434 (GPU) |
| `crew_local_small` | ollama | smollm2:135m | host.docker.internal:11435 (CPU) |
| `service_local_cpu` | ollama | smollm2:135m | host.docker.internal:11435 (CPU) |
| `vision_encoder` | — | — | vision-encoder:8001 (ViT-L-14) |
| `crewai` | — | — | localhost:9010 |

**Агенти з vision моделлю:** greenfood, druid, eonarch, helion → `qwen3-vl:8b` (через swapper)

**Метрики:** `llm_heavy_share_ratio=0.0` — важкі запити ще не логовані (лічильники нульові, нові після restart).

### GAPS

- `local_qwen3_8b`, `qwen3_strategist_8b`, ... — **всі вказують на 27B замість 8B** (рядки в config не оновлені після зміни). Назви оманливі.
- `crew_local_27b` використовує `172.17.0.1:11434` — не `host.docker.internal`. Inconsistency: CPU профілі через host.docker.internal, GPU — через IP.

### RECOMMENDED PATCHES

**Patch 1:** Уніфікувати GPU профілі на `host.docker.internal:11434`:
```yaml
# services/router/router-config.yml
crew_local_27b:
  base_url: http://host.docker.internal:11434  # було 172.17.0.1
crew_vision_27b:
  base_url: http://host.docker.internal:11434
```

**Patch 2:** Перейменувати оманливі профілі (або залишити as-is якщо вони deprecated):
```yaml
# local_qwen3_8b → local_qwen3_27b (або видалити невикористані)
```

---

## 3. ФОТО E2E — Telegram → Vision → Агент

### CURRENT STATE (Два шляхи!)

#### Шлях A: Прямий (основний для більшості агентів)
```
Telegram photo → Gateway (http_api.py:~2085)
  ↓ download photo via Telegram Bot API → file_url (https://api.telegram.org/file/...)
  ↓ send_to_router({file_url, images: [file_url], prompt})
  ↓ Router (main.py:~2445) → SWAPPER_URL/vision
     payload: {model: "qwen3-vl-8b", prompt, images: [file_url]}
  ↓ Swapper /vision → завантажує qwen3-vl:8b (ollama pull) → відповідь
  ↓ Router повертає text → Gateway → Telegram
```

#### Шлях B: Через NATS ATTACHMENTS (для parser-pipeline)
```
Telegram photo → Gateway
  ↓ (окремий worker?) → NATS ATTACHMENTS stream
  ↓ parser-pipeline consumer
     process_image() → SWAPPER_URL/vision (base64 encode)
  ↓ result → ???  (не ясно куди іде результат)
```

**КРИТИЧНО:** `parser-pipeline` логи показують **тисячі** `ServiceUnavailableError` між рестартами — NATS stream `ATTACHMENTS` зникає після рестарту `dagi-nats-node1` (нема persistence). Після рестарту parser підключається знову (`Consumer created: parser-pipeline`).

### Vision model flow (Swapper):
- Gateway надсилає `file_url` (не base64 завантаження)
- Router передає `images: [file_url]` у Swapper
- Swapper `/vision` → `qwen3-vl:8b` через Ollama (6.1GB, lazy load)
- **qwen3-vl:8b зараз `unloaded`** — cold-start ~30-60s при першому виклику

### GAPS

1. **NATS stream ATTACHMENTS не персистентний** — після `docker restart dagi-nats-node1` stream зникає. Parser спамить `ServiceUnavailableError` поки не перезапустити.
2. **parser-pipeline `SWAPPER_URL=http://swapper-service:8890`** — але контейнер називається `swapper-service-node1`. DNS може не резолвитись.
3. **ingest-service** також має `SWAPPER_URL=http://swapper-service-node1:8890` → `socket.gaierror: Temporary failure in name resolution` — сервіс намагається резолвити щось не те.
4. **Шлях B результат незрозумілий** — куди parser-pipeline відправляє результат обробки зображення після Vision?
5. **qwen3-vl:8b cold-start** — перший запит до vision займе 30-60s (lazy load).

### RECOMMENDED PATCHES

**Patch 3:** Виправити `SWAPPER_URL` в parser-pipeline compose:
```yaml
# docker-compose.node1.yml, parser-pipeline service
environment:
  - SWAPPER_URL=http://swapper-service-node1:8890  # було: http://swapper-service:8890
```

**Patch 4:** NATS stream ATTACHMENTS — зробити файловий storage з retention:
```yaml
# nats-js-init service (вже є в compose) — перевірити що він запускається після рестарту NATS
```

---

## 4. PDF/ДОКУМЕНТИ — Обробка

### CURRENT STATE

**Сервіси обробки документів:**
| Сервіс | Статус | Роль |
|--------|--------|------|
| `render-pdf-worker-node1` | ✅ up, **idle** | PDF → PNG/зображення (NATS: artifact.job.render_pdf.requested) |
| `render-pptx-worker-node1` | ⚠️ DNS fail (`nats`) | PPTX → PNG (NATS: нема з'єднання) |
| `index-doc-worker-node1` | ⚠️ DNS fail (RAG service?) | RAG indexing (NATS: artifact.job.*) |
| `presentation-renderer-node1` | ✅ healthy (9212) | API сервіс рендерингу |
| `oneok-gotenberg-node1` | ✅ up (3010) | HTML/PDF generation (Gotenberg) |
| `rag-service-node1` | ✅ healthy (9500) | RAG retrieval |
| `artifact-registry-node1` | ✅ healthy (9220) | Артефакт реєстр |
| `dagi-minio-node1` | ✅ up (9000-9001) | S3 storage |
| `parser-pipeline` | ✅ up (8101) | NATS consumer → Swapper doc+image |

**Docling:** НЕ ВСТАНОВЛЕНИЙ як окремий контейнер. Є як модель у Swapper (`granite-docling`, тип `document`, 2.5GB, `unloaded`).

**Шлях обробки документа (PDF):**
```
Telegram doc → Gateway → ?
  → або send_to_router з doc_url
  → або через NATS → parser-pipeline → Swapper /document
     Swapper /document → granite-docling (lazy load, 2.5GB) → текст

  Паралельно:
  → artifact.job.render_pdf.requested → render-pdf-worker → PNG → artifact-registry → MinIO
  → artifact.job.index_doc.requested → index-doc-worker → rag-service (RAG indexing)
```

### GAPS

1. **render-pptx-worker** не може резолвити `nats` DNS — на іншій docker network або compose group.
2. **index-doc-worker** DNS fail (щось не резолвить) — перевірити network config.
3. **granite-docling** у swapper `unloaded` — завантажується lazily, займе час при першому запиті документа. GPU увімкнений для docling? (GPU_ENABLED=false зараз!)
4. **Немає Docling окремим сервісом** — вся обробка документів через Swapper, який зараз CPU-only через наші зміни.

### GAPS — КРИТИЧНО

> **Swapper GPU_ENABLED=false** — означає, що granite-docling, got-ocr2, qwen3-vl-8b і whisper будуть завантажуватись в CPU/RAM. При 20GB VRAM це субоптимально для Vision і OCR моделей.

### RECOMMENDED PATCHES

**Patch 5:** Виправити network для render-pptx-worker та index-doc-worker:
```yaml
# docker-compose.node1.yml — додати network dagi-network до цих сервісів
render-pptx-worker:
  networks:
    - dagi-network  # щоб резолвити 'nats'
index-doc-worker:
  networks:
    - dagi-network
```

---

## 5. STT/TTS/SWAPPER — Детальний аналіз

### CURRENT STATE

**Swapper /health:** `{"status":"healthy","active_model":"qwen3-8b","mode":"single-active"}`

**Swapper конфіг (фактичний):**
- `mode: multi-active` в yaml, але ENV `MAX_CONCURRENT_MODELS=1` → single-active режим
- `GPU_ENABLED=false` (наша зміна) — але config.yaml каже `gpu_enabled: true`
- `WHISPER_DEVICE=cpu, WHISPER_COMPUTE_TYPE=int8`

**Моделі в Swapper:**
| Модель | Тип | Розмір | Статус |
|--------|-----|--------|--------|
| qwen3-8b | llm | 5.2GB | **loaded** (Ollama) |
| qwen3-vl-8b | vision | 6.1GB | unloaded |
| got-ocr2 | ocr | 7.0GB | unloaded |
| donut-base | ocr | 3.0GB | unloaded |
| donut-cord | ocr | 3.0GB | unloaded |
| granite-docling | document | 2.5GB | unloaded |
| faster-whisper-large | stt | 3.0GB | unloaded |
| whisper-small | stt | 0.5GB | unloaded |
| xtts-v2 | tts | 2.0GB | unloaded |
| flux-klein-4b | image_gen | 15.4GB | unloaded |

**STT:**
- STT startup: `[STT-POLICY] WHISPER_DEVICE env='cpu' | actual_device='cpu'` ✅
- Swapper `/stt` ← parser-pipeline (audio processing)
- Swapper `/stt` ← router (STT_URL)
- Swapper `/stt` ← gateway (STT_SERVICE_URL)
- **Whisper завантажується lazily при першому аудіо-запиті** на CPU (int8)

**TTS:** xtts-v2 (2GB) — `unloaded`. Не використовується активно.

**Висновок по Swapper: ЗАЛИШИТИ (він критичний)**

Swapper є єдиним агрегатором для:
1. **Vision** (`/vision`) — qwen3-vl:8b для всіх агентів що аналізують фото
2. **STT** (`/stt`) — Whisper для голосових повідомлень
3. **OCR** (`/ocr`) — got-ocr2 для документів
4. **Document** (`/document`) — granite-docling для PDF/DOCX
5. **TTS** (`/tts`) — xtts-v2 (поки не активований)

**Проблема:** `active_model=qwen3-8b` через Ollama — це **дублювання** з основним Ollama GPU. Swapper завантажує qwen3:8b через свій ollama, поки є окремий Ollama на 11434 з 27B. При виклику vision, swapper **swap'ає** qwen3:8b і завантажує qwen3-vl:8b — займає VRAM GPU.

> **Але GPU_ENABLED=false!** — Значить qwen3-vl:8b завантажиться в RAM/CPU, що дуже повільно (>30s).

### RECOMMENDED PATCHES

**Patch 6 (ВАЖЛИВИЙ):** Вирішити GPU конфлікт Swapper vs Ollama:

Варіанти:
- **A (рекомендований):** Swapper Vision через Ollama GPU (11434), STT на CPU:
  ```yaml
  # docker-compose.node1.yml, swapper-service
  environment:
    - GPU_ENABLED=true   # дозволити GPU для vision/OCR
    - WHISPER_DEVICE=cpu  # але STT лишається CPU
    - WHISPER_COMPUTE_TYPE=int8
    # Прибрати CUDA_VISIBLE_DEVICES= (empty block GPU)
  ```
  Потрібно додати GPU device back:
  ```yaml
  deploy:
    resources:
      reservations:
        devices:
          - driver: nvidia
            count: 1
            capabilities: [gpu]
  ```
  Тоді Swapper поверне GPU для vision і OCR.

- **B (поточний стан):** GPU_ENABLED=false → all CPU → Vision дуже повільно

---

## 6. GPU POLICY

### CURRENT STATE ✅

```
VRAM: 18783 MiB / 20475 MiB  (qwen3.5:27b-q4_K_M завантажений — warmup timer)
GPU Ollama (11434): 1 model — qwen3.5:27b-q4_K_M (17434 MiB)
CPU Ollama (11435): 0 models (smollm2:135m unloaded, lazy)
gpu_single_model_policy_ok = 1  ✅
ollama_cpu_instance_up = 1      ✅
```

**Проблема:** Swapper показує `active_model=qwen3-8b` — це qwen3:8b через ollama **всередині swapper**, але Swapper зараз CPU-only. Значить qwen3:8b у свопері не займає GPU VRAM поки GPU_ENABLED=false. Але якщо повернути GPU Swapper — треба перевірити що 27B + qwen3-vl-8b не одночасно в VRAM (20GB максимум).

**Потенційний конфлікт:** 27B (17.4GB) + qwen3-vl-8b (6.1GB) = **23.5GB > 20GB VRAM** → OOM!

Необхідна координація: коли Swapper завантажує vision модель, Ollama GPU має вивантажити 27B або навпаки.

---

## 7. NODA1 ↔ NODA2 — З'єднання

### CURRENT STATE

**Інфраструктура:**
- NODA1 і NODA2 (`llm80-che-1-1`, IP 192.168.1.240) — це **K3s cluster** (flannel CNI)!
  - NODA1: `node1-daarion` — **control-plane, master** (Ready)
  - NODA2 (`llm80-che-1-1`): `worker node` — **NotReady** (проблема!)
- **Flannel:** `10.42.0.0/24` (NODA1), `10.42.1.0/24` (NODA2) — pod overlay network
- **WireGuard:** НЕ встановлений
- **NATS:** cluster config є (`my_cluster`, port 6222), але `routes = []` — **NATS не з'єднаний між нодами**

**K3s pods на NODA2 (llm80-che-1-1):** більшість `Terminating` або `Pending` — NODA2 NotReady!

**Що це означає:**
- Фізично NODA1 і NODA2 з'єднані через K3s/flannel (LAN, 192.168.x.x)
- Але Docker Compose сервіси на NODA2 (memory service, qdrant, neo4j) — **окремі**, не в K3s
- NATS між нодами не federatederated — жоден cross-node message bus не налаштований

### GAPS

1. **K3s worker NODA2 NotReady** — pods Terminating/Pending. Не ясно чи це критично для поточного продакшну.
2. **NATS не кластеризований** — немає leafnode/route між NODA1 і NODA2 NATS.
3. **Немає cross-node subjects** для агентів.
4. **NODA2 підключення до NODA1:** NODA2 має свій Docker Compose (окремі memory/qdrant), немає спільного bus.

### RECOMMENDED PATCHES

**Patch 7 (NATS federation між нодами):**
```conf
# /opt/microdao-daarion/nats/nats-server.conf (NODA1)
leafnodes {
  port: 7422
}

# NATS на NODA2 підключається як leafnode:
leafnodes {
  remotes = [{ url: "nats://144.76.224.179:7422" }]
}
```

Це дозволить NODA2 публікувати/підписуватись на `node.control.noda2.*` через NODA1.

---

## 8. CTO SOFIIA — Control Plane

### CURRENT STATE

**`control-plane` контейнер (порт 9200):**
- FastAPI сервіс з JWT auth (`SERVICE_ROLE=controlplane`)
- Endpoints:
  - `GET /prompts/{agent_id}` — версіоновані system prompts з файлів `*_prompt.txt`
  - `GET /policy/{agent_id}` — RBAC/entitlements (DefaultPolicies)
  - `GET /prompts/{agent_id}/hash` — hash промпту для drift detection
- **401 Unauthorized** при зверненні без JWT — це правильно

**Що є:**
- ✅ Промпти централізовані та версіоновані
- ✅ JWT auth для сервіс-до-сервіс
- ✅ Policy/RBAC per agent
- ✅ `dagi-vision-encoder-node1` — ViT-L-14 на CPU (embeddings)

**Що НЕ реалізовано:**
- ❌ Node operations (restart/deploy/health через control-plane)
- ❌ Sofiia не має NATS-control topic для публікації команд
- ❌ Немає `node-ops-worker` на кожній ноді
- ❌ Sofiia добавляє нову ноду тільки через SSH root (bRhfV7uNY9m6er — hardcoded!)
- ❌ Немає механізму "додати нову ноду без root"

**Поточний механізм керування нодами:** SSH з паролем root. Небезпечно.

### RECOMMENDED PATCHES

**Patch 8 (мінімальний control plane extension):**

Додати в control-plane endpoints для node ops:
```python
# services/control-plane/app/main.py (або новий node_ops.py)

# Sofiia публікує на NATS:
# node.control.noda1.restart_service → {service_name, reason}
# node.control.noda1.health_check → {}
# node.control.noda1.get_logs → {service_name, lines}

# node-ops-worker (новий мікросервіс) підписується на ці subjects
# виконує whitelist commands (docker restart, docker logs tail, health curl)
# відповідає на node.control.noda1.reply.*
```

**Мінімальна реалізація (50 рядків Python):**
```python
# services/node-ops-worker/main.py
ALLOWED_COMMANDS = {
  "restart_service": lambda s: f"docker restart {s}",
  "health_check": lambda s: f"curl -sf http://localhost:{PORT_MAP[s]}/health",
  "logs_tail": lambda s, n: f"docker logs --tail {n} {s}",
}
# Subscribe to node.control.noda1.> via NATS
# Execute only ALLOWED_COMMANDS
# Reply to reply subject
```

---

## VALIDATION CHECKLIST

```bash
# 1. Router CPU profiles (host.docker.internal)
docker exec dagi-router-node1 curl -s http://host.docker.internal:11435/api/tags | python3 -c 'import sys,json; print("CPU Ollama OK:", len(json.load(sys.stdin).get("models",[])))'

# 2. GPU policy
curl -s http://localhost:9400/metrics | grep gpu_single_model_policy_ok

# 3. Swapper Vision (cold start test — без кешу)
# УВАГА: займе 30-60s якщо GPU_ENABLED=false!
# curl -s -X POST http://localhost:8890/vision -H 'Content-Type: application/json' \
#   -d '{"model":"qwen3-vl-8b","prompt":"що на фото?","images":["<url>"]}' | jq .

# 4. Parser pipeline connected
docker logs --tail 5 parser-pipeline 2>&1 | grep -E 'Connected|Consumer created'

# 5. NATS stream ATTACHMENTS exists
curl -s 'http://localhost:8222/jsz?streams=true' | python3 -m json.tool | grep -A3 'ATTACHMENTS'

# 6. render-pptx-worker DNS fix check
docker logs --tail 5 render-pptx-worker-node1 2>&1 | grep -v 'getaddrinfo'

# 7. index-doc-worker DNS fix check  
docker logs --tail 5 index-doc-worker-node1 2>&1 | grep -v 'getaddrinfo'

# 8. Control plane health
curl -s http://localhost:9200/health

# 9. Swapper STT device
docker logs swapper-service-node1 2>&1 | grep STT-POLICY

# 10. K3s NODA2 status
kubectl get nodes
```

---

## PRIORITIZED ACTION PLAN

### P0 — Негайно (production impact):

| # | Патч | Файл | Вплив |
|---|------|------|-------|
| 3 | SWAPPER_URL fix в parser-pipeline | docker-compose.node1.yml | Vision через parser |
| 5 | Network fix render-pptx + index-doc | docker-compose.node1.yml | Документи |
| 6 | GPU повернути Swapper (Vision повільний!) | docker-compose.node1.yml | Vision latency |

### P1 — Цього тижня:

| # | Патч | Файл | Вплив |
|---|------|------|-------|
| 1 | host.docker.internal для GPU профілів | router-config.yml | Stability |
| 4 | NATS ATTACHMENTS persistence | nats config | Parser stability |
| 7 | NATS leafnode NODA1↔NODA2 | nats-server.conf | Cross-node |

### P2 — Наступний спринт:

| # | Патч | Файл | Вплив |
|---|------|------|-------|
| 8 | node-ops-worker для Sofiia control | нові файли | Security |
| 2 | Profile rename в router-config | router-config.yml | Clarity |

---

## ВІДПОВІДІ НА 7 КЛЮЧОВИХ ПИТАНЬ

### 1. Фото E2E
**Telegram photo → Gateway** (скачує файл → file_url) → **`send_to_router({images:[file_url]})`** → **Router** перевіряє агента → якщо vision-агент → **`SWAPPER_URL/vision`** → Swapper → Ollama `qwen3-vl:8b` → text опис → Router → Gateway → Telegram. Parser-pipeline — паралельний worker для асинхронної обробки (не основний шлях). Payload: `{model, prompt, images:[url], max_tokens}`.

### 2. Документи/PDF
**Немає Docling як сервісу.** Docling вбудований в Swapper як `granite-docling` (lazy, unloaded). Шлях: Gateway → Router → `SWAPPER_URL/document` → Swapper → granite-docling. Паралельно через NATS: `artifact.job.render_pdf.requested` → render-pdf-worker → PNG → MinIO/artifact-registry. `index-doc-worker` індексує в RAG але має DNS fail.

### 3. Router
Top-level агенти → **DeepSeek API** (cloud_deepseek). Crew tasks → **qwen3.5:27b-q4_K_M** (crew_local_27b, GPU). Monitoring/small → **smollm2:135m** (crew_local_small, CPU Ollama 11435). `ENABLE_CREW_MODEL_ROUTING=1` активний. Vision агенти отримують `qwen3-vl-8b` через Swapper.

### 4. TTS/STT
STT: **Whisper (CPU, int8)** через Swapper `/stt`. `WHISPER_DEVICE=cpu` підтверджено логами. Lazy load при першому аудіо. Підтримується: faster-whisper-large (3GB), whisper-small (0.5GB). TTS: xtts-v2 (2GB) — **not deployed активно** (unloaded). Немає VRAM конкуренції для STT.

### 5. Swapper
**Залишити.** Є єдиним агрегатором для Vision (qwen3-vl:8b), STT (Whisper), OCR (got-ocr2), Document (granite-docling), TTS (xtts-v2). Без Swapper треба окремі сервіси для кожного. Але: `active_model=qwen3-8b` — потенційно невикористана ролі (є окремий Ollama). **Слід розглянути видалення qwen3-8b зі Swapper** — він дублює GPU Ollama, залишити тільки Vision/OCR/STT/Document функції.

### 6. NODA1↔NODA2
З'єднані через **K3s cluster** (flannel, 10.42.0.0/24). NODA2 (`llm80-che-1-1`, 192.168.1.240) — K3s worker, зараз **NotReady**. NATS між нодами **не з'єднаний** (routes=[]), немає leafnode. Docker Compose сервіси незалежні. Для cross-node messaging потрібен NATS leafnode або Flannel pod networking.

### 7. CTO Sofiia Control Plane
Поточний стан: `control-plane` (9200) — JWT-захищений сервіс з prompts + policy. **Немає node-ops механізму**. Sofiia керує нодами через SSH root (небезпечно). Правильний шлях: NATS-control plane + `node-ops-worker` на кожній ноді з whitelist команд. control-plane вже є основою — треба додати NATS subscription для node operations.

---

*Звіт згенеровано автоматично аудитом NODA1 | Sofiia v2.7 | 2026-02-27*
