Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
25 KiB
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:
# 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):
# 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
- NATS stream ATTACHMENTS не персистентний — після
docker restart dagi-nats-node1stream зникає. Parser спамитьServiceUnavailableErrorпоки не перезапустити. - parser-pipeline
SWAPPER_URL=http://swapper-service:8890— але контейнер називаєтьсяswapper-service-node1. DNS може не резолвитись. - ingest-service також має
SWAPPER_URL=http://swapper-service-node1:8890→socket.gaierror: Temporary failure in name resolution— сервіс намагається резолвити щось не те. - Шлях B результат незрозумілий — куди parser-pipeline відправляє результат обробки зображення після Vision?
- qwen3-vl:8b cold-start — перший запит до vision займе 30-60s (lazy load).
RECOMMENDED PATCHES
Patch 3: Виправити SWAPPER_URL в parser-pipeline compose:
# 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:
# 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
- render-pptx-worker не може резолвити
natsDNS — на іншій docker network або compose group. - index-doc-worker DNS fail (щось не резолвить) — перевірити network config.
- granite-docling у swapper
unloaded— завантажується lazily, займе час при першому запиті документа. GPU увімкнений для docling? (GPU_ENABLED=false зараз!) - Немає 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:
# 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, але ENVMAX_CONCURRENT_MODELS=1→ single-active режимGPU_ENABLED=false(наша зміна) — але config.yaml кажеgpu_enabled: trueWHISPER_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 є єдиним агрегатором для:
- Vision (
/vision) — qwen3-vl:8b для всіх агентів що аналізують фото - STT (
/stt) — Whisper для голосових повідомлень - OCR (
/ocr) — got-ocr2 для документів - Document (
/document) — granite-docling для PDF/DOCX - 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:
# 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:
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 (проблема!)
- NODA1:
- 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
- K3s worker NODA2 NotReady — pods Terminating/Pending. Не ясно чи це критично для поточного продакшну.
- NATS не кластеризований — немає leafnode/route між NODA1 і NODA2 NATS.
- Немає cross-node subjects для агентів.
- NODA2 підключення до NODA1: NODA2 має свій Docker Compose (окремі memory/qdrant), немає спільного bus.
RECOMMENDED PATCHES
Patch 7 (NATS federation між нодами):
# /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.txtGET /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:
# 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):
# 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
# 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