Files
microdao-daarion/docs/NODA1-TECHBORGS-PATCHES.md
Apple ef3473db21 snapshot: NODE1 production state 2026-02-09
Complete snapshot of /opt/microdao-daarion/ from NODE1 (144.76.224.179).
This represents the actual running production code that has diverged
significantly from the previous main branch.

Key changes from old main:
- Gateway (http_api.py): expanded from ~40KB to 164KB with full agent support
- Router: new /v1/agents/{id}/infer endpoint with vision + DeepSeek routing
- Behavior Policy: SOWA v2.2 (3-level: FULL/ACK/SILENT)
- Agent Registry: config/agent_registry.yml as single source of truth
- 13 agents configured (was 3)
- Memory service integration
- CrewAI teams and roles

Excluded from snapshot: venv/, .env, data/, backups, .tgz archives

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 08:46:46 -08:00

479 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NODA1 — Точні патчі для трьох техборгів
**Source of truth:** repo (node2) — `docs/NODA1-TECHBORGS-PATCHES.md`
**Copy on NODA1:** `/opt/microdao-daarion/docs/NODA1-TECHBORGS-PATCHES.md`
**Code commit:** d0188fd
**Docs commit:** fb6b92b
**Last sync:** 2026-01-28
---
Документ містить **зібрані з NODA1** YAML-фрагменти (read-only) і **мінімальні, безпечні патчі** для postgres-backup (pg16), render-pdf-worker (NATS hostname), Caddy vs nginx.
**Compose-файли на NODA1:**
- Основний стек: `/opt/microdao-daarion/docker-compose.node1.yml`
- Бекапи: `/opt/microdao-daarion/docker-compose.backups.yml`
- Effective config: `cd /opt/microdao-daarion && docker compose -f docker-compose.node1.yml config` → 766 рядків
---
## 0) Safety + rollback (~2 хв)
```bash
# 0.1 Backup compose файлів перед правкою
cd /opt/microdao-daarion
cp -a docker-compose.node1.yml docker-compose.node1.yml.bak.$(date +%F_%H%M%S)
cp -a docker-compose.backups.yml docker-compose.backups.yml.bak.$(date +%F_%H%M%S)
# 0.2 Зняти поточні images (для швидкого повернення)
docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}' | head -50
# 0.3 Rollback (якщо треба)
# cp docker-compose.node1.yml.bak.<ts> docker-compose.node1.yml
# cp docker-compose.backups.yml.bak.<ts> docker-compose.backups.yml
# docker compose -f docker-compose.node1.yml up -d
# docker compose -f docker-compose.backups.yml up -d
```
**Перед застосуванням — валідація YAML (ловить синтаксичні помилки до будь-якого `up -d`):**
```bash
docker compose -f /opt/microdao-daarion/docker-compose.node1.yml config >/dev/null && echo "node1 compose OK"
docker compose -f /opt/microdao-daarion/docker-compose.backups.yml config >/dev/null && echo "backups compose OK"
```
**Примітка про service name vs container_name (пастка №1):** `docker compose up -d <service>` працює тільки з **іменем сервісу** з compose, а `docker logs` / `docker exec` — по **імені контейнера** (container_name). Щоб не плутатись:
```bash
# Показати відповідність service → container
cd /opt/microdao-daarion
docker compose -f docker-compose.node1.yml ps
docker compose -f docker-compose.backups.yml ps
```
---
## 1) Зібрані YAML-блоки (read-only)
### 1.1 qdrant (з effective config)
```yaml
qdrant:
container_name: dagi-qdrant-node1
healthcheck:
test: ["CMD", "true"]
timeout: 10s
interval: 30s
retries: 3
image: qdrant/qdrant:v1.7.4
networks:
dagi-network: null
ports:
- "6333:6333"
- "6334:6334"
restart: unless-stopped
ulimits:
nofile: { soft: 65536, hard: 65536 }
volumes:
- qdrant-data-node1:/qdrant/storage
```
### 1.2 nats (з effective config)
```yaml
nats:
command: ["-js"]
container_name: dagi-nats-node1
image: nats:2.10-alpine
networks:
dagi-network: null
ports:
- "4222:4222"
restart: unless-stopped
volumes:
- nats-data-node1:/data
```
### 1.3 render-pdf-worker (з effective config)
```yaml
render-pdf-worker:
build:
context: /opt/microdao-daarion/services/render-pdf-worker
dockerfile: Dockerfile
container_name: render-pdf-worker-node1
depends_on:
artifact-registry: { condition: service_started, required: true }
minio: { condition: service_started, required: true }
nats: { condition: service_started, required: true }
environment:
ARTIFACT_REGISTRY_URL: http://artifact-registry:9220
MINIO_ACCESS_KEY: minioadmin
MINIO_BUCKET: artifacts
MINIO_ENDPOINT: minio:9000
MINIO_SECRET_KEY: minioadmin
MINIO_SECURE: "false"
NATS_URL: nats://nats:4222
networks:
dagi-network: null
restart: unless-stopped
```
### 1.4 postgres-backup (з docker-compose.backups.yml)
```yaml
services:
postgres-backup:
image: prodrigestivill/postgres-backup-local:15
container_name: postgres-backup-node1
restart: unless-stopped
environment:
POSTGRES_HOST: dagi-postgres
POSTGRES_DB: daarion_main,daarion_memory,rag
POSTGRES_USER: daarion
POSTGRES_PASSWORD: DaarionDB2026!
SCHEDULE: "@every 6h"
BACKUP_KEEP_DAYS: 7
BACKUP_KEEP_WEEKS: 4
BACKUP_KEEP_MONTHS: 6
POSTGRES_EXTRA_OPTS: "-Z9 --schema=public --blobs"
volumes:
- /opt/backups/postgres:/backups
networks:
- dagi-network
networks:
dagi-network:
external: true
```
### 1.5 Проксі (системні сервіси, не compose)
- **nginx:** active (running), тримає 80/443, конфіг: `/etc/nginx/nginx.conf` + `sites-enabled/*`, `conf.d/*`.
- **Caddy:** failed — "listen tcp :443: bind: address already in use" (nginx вже слухає 443). Версія Caddy: v2.10.2.
---
## 2) Патч #1: postgres-backup (pg_dump 16.x)
**Ціль:** pg_dump версії 16 відповідає Postgres 16.11 на сервері.
**Файл:** `/opt/microdao-daarion/docker-compose.backups.yml`
**Зміна (1 рядок):**
```diff
- image: prodrigestivill/postgres-backup-local:15
+ image: prodrigestivill/postgres-backup-local:16
```
Якщо тегу `:16` немає на Docker Hub для цього образу — варіанти:
- Використати інший образ на базі `postgres:16` (наприклад, з postgresql-client-16).
- Або зібрати власний Dockerfile на базі `postgres:16` і викликати `pg_dump` з нього.
**Після правки:**
```bash
cd /opt/microdao-daarion
docker compose -f docker-compose.backups.yml pull postgres-backup
docker compose -f docker-compose.backups.yml up -d postgres-backup
docker logs --tail 50 postgres-backup-node1
```
---
## 3) Патч #2: render-pdf-worker (NATS hostname)
**Проблема:** `NATS_URL=nats://nats:4222`, а сервіс у мережі називається `dagi-nats-node1` (container_name), тому DNS `nats` не резолвиться.
**Рекомендація: варіант A (менш інвазивний)** — додати alias `nats` для сервісу `nats` у `dagi-network`. Інші клієнти (якщо теж використовують `nats:4222`) продовжать працювати без змін.
**Нюанс (alias тільки на рівні сервісу):** зміна має бути саме у **сервісі** (не в секції top-level `networks:`). У docker-compose.node1.yml сервіс називається `nats` (container_name: dagi-nats-node1). Якщо у вашому файлі сервіс має інше ім'я (наприклад `dagi-nats-node1`), використовуйте його як ключ:
```yaml
services:
nats:
container_name: dagi-nats-node1
networks:
dagi-network:
aliases:
- nats
```
Або якщо сервіс названо `dagi-nats-node1`:
```yaml
services:
dagi-nats-node1:
networks:
dagi-network:
aliases:
- nats
```
Якщо зараз `networks: [dagi-network]` або `dagi-network: null` — замінити на мапу з `aliases` як вище.
**Файл:** `/opt/microdao-daarion/docker-compose.node1.yml`
**Знайти блок `nats:`** (приблизно такий):
```yaml
nats:
command:
- -js
container_name: dagi-nats-node1
image: nats:2.10-alpine
networks:
dagi-network: null
```
**Замінити на (додати `aliases`):**
```yaml
nats:
command:
- -js
container_name: dagi-nats-node1
image: nats:2.10-alpine
networks:
dagi-network:
aliases:
- nats
ports:
- "4222:4222"
restart: unless-stopped
volumes:
- nats-data-node1:/data
```
Тобто замість `dagi-network: null` використати:
```yaml
networks:
dagi-network:
aliases:
- nats
```
**Альтернатива B:** у сервісі `render-pdf-worker` змінити env:
```yaml
NATS_URL: nats://dagi-nats-node1:4222
```
**Після правки (варіант A):**
```bash
cd /opt/microdao-daarion
docker compose -f docker-compose.node1.yml up -d nats
docker compose -f docker-compose.node1.yml up -d render-pdf-worker
docker logs --tail 80 render-pdf-worker-node1
```
---
## 4) Render-PDF-Worker: діагностика та idle timeout
**Висновок з перевірок A/B:** Підключення воркера до NATS працює (alias `nats` резолвиться у `dagi-network`). Помилка `nats.errors.TimeoutError` у логах — це **таймаут очікування повідомлень** (`sub.next_msg()`), а не з’єднання. Subject підписки: `artifact.job.render_pdf.requested`. Якщо задач немає, воркер отримує timeout і без обробки винятку завершується (exit 1).
**A) Довести підключення до NATS (read-only):**
- TCP з воркера (коли контейнер Up): `docker exec render-pdf-worker-node1 sh -lc 'nc -vz -w 2 nats 4222 && echo NATS_TCP_OK'` (у контейнері memory-service немає `nc`, тому перевірку можна робити з іншого контейнера з `nc` або через `getent hosts nats`).
- Логи NATS: `docker logs --tail 200 dagi-nats-node1 | egrep -i 'client|connect|disconnect'`.
**B) Env і subject:** У воркера лише `NATS_URL=nats://nats:4222`; subject/queue задані в коді: `artifact.job.render_pdf.requested`.
**C) Варіант без зміни коду:** У compose вже є `restart: unless-stopped`; додано `stop_grace_period: 30s`. Воркер буде автоматично перезапускатися після exit при idle.
**D) Варіант з мінімальною зміною коду (рекомендовано):** У `main.py` обробити `nats.errors.TimeoutError` у циклі очікування повідомлень — не завершувати процес, а робити `continue`:
```python
import nats.errors
# ...
while True:
try:
msg = await sub.next_msg()
except nats.errors.TimeoutError:
continue
# обробка msg
```
Після патчу D воркер залишається активним при відсутності задач без рестарт-циклів.
**Спостережуваність NATS (без підвищеного логування):** стандартні логи NATS не показують connect/disconnect клієнтів. Корисно мати health/metrics endpoint або періодичну перевірку `connz` (якщо дозволено конфігом NATS monitoring port), щоб швидко довести, що клієнт реально підключився. Не обов’язково зараз, але зменшує час діагностики.
---
## 5) Патч #3: Caddy vs nginx (конфлікт 80/443)
**Поточний стан:** nginx active, тримає 80/443; Caddy failed через "address already in use".
**Рекомендація (найпростіший варіант):** **nginx єдиний reverse proxy** — вимкнути Caddy, щоб уникнути конфлікту і подвійної конфігурації.
**Мінімальний патч (без зміни nginx):**
```bash
sudo systemctl stop caddy
sudo systemctl disable caddy
```
Якщо потрібно саме Caddy як єдиний проксі — тоді вимкнути nginx, перенести конфіг з nginx у Caddyfile і налаштувати Caddy на 80/443 (окремий техборг).
---
## Порядок застосування патчів (рекомендований)
### 1.1 Патч #2 (NATS alias) — першим, бо розблоковує render-pdf-worker
Використовуйте **ім'я сервісу** з compose (`nats`, `render-pdf-worker`), не container_name (`dagi-nats-node1`, `render-pdf-worker-node1`) для `docker compose up -d`.
```bash
cd /opt/microdao-daarion
# Після внесення alias у docker-compose.node1.yml
docker compose -f docker-compose.node1.yml pull nats 2>/dev/null || true
docker compose -f docker-compose.node1.yml up -d nats
# Перевірка alias резолвиться з воркера
docker start render-pdf-worker-node1 2>/dev/null || true
docker exec render-pdf-worker-node1 sh -lc 'getent hosts nats && nc -vz -w 2 nats 4222' 2>/dev/null || true
# Перепідняти воркер (service name)
docker compose -f docker-compose.node1.yml up -d render-pdf-worker 2>/dev/null || true
docker logs --tail 120 render-pdf-worker-node1
```
Якщо `render-pdf-worker-node1` не керується compose (standalone container):
```bash
docker restart render-pdf-worker-node1
docker logs --tail 120 render-pdf-worker-node1
```
**Якщо alias додано, але worker все одно не бачить nats (пастка №2):** найчастіше воркер не в тій же мережі, або він standalone і не приєднаний до `dagi-network`.
```bash
# 1) Переконатися, що nats і worker в одній мережі
docker inspect dagi-nats-node1 --format '{{json .NetworkSettings.Networks}}' | jq .
docker inspect render-pdf-worker-node1 --format '{{json .NetworkSettings.Networks}}' | jq .
# 2) Якщо worker standalone і не в dagi-network — тимчасово приєднати (операційний workaround)
# УВАГА: це змінює стан хоста (але не compose). Застосовувати лише як швидкий фікс.
# docker network connect dagi-network render-pdf-worker-node1
# 3) Повторна перевірка
docker exec render-pdf-worker-node1 sh -lc 'getent hosts nats && nc -vz -w 2 nats 4222' 2>/dev/null || true
```
### 1.2 Патч #1 (postgres-backup образ pg16)
```bash
cd /opt/microdao-daarion
# Після заміни tag :15 -> :16 у docker-compose.backups.yml
docker compose -f docker-compose.backups.yml pull postgres-backup 2>/dev/null || true
docker compose -f docker-compose.backups.yml up -d postgres-backup
docker ps --filter name=postgres-backup-node1 --format 'table {{.Names}}\t{{.Status}}'
docker logs --tail 200 postgres-backup-node1
docker inspect postgres-backup-node1 --format '{{.State.Health.Status}}'
```
### 1.3 Патч #3 (nginx-only proxy) — вимкнути Caddy
```bash
systemctl stop caddy || true
systemctl disable caddy || true
systemctl status caddy --no-pager -l || true
# Переконатися що nginx тримає 80/443
systemctl status nginx --no-pager -l
ss -ltnp | egrep ':(80|443)\s'
```
---
## Sync from repo → NODA1
```bash
# локально (node2)
cd /Users/apple/node2
# (опційно) показати, що саме деплоїмо
git rev-parse --short HEAD
git show -s --format=%ci
# 1) Код воркера
scp -p services/render-pdf-worker/app/main.py \
root@144.76.224.179:/opt/microdao-daarion/services/render-pdf-worker/app/main.py
# 2) Документація
scp -p docs/NODA1-TECHBORGS-PATCHES.md \
root@144.76.224.179:/opt/microdao-daarion/docs/NODA1-TECHBORGS-PATCHES.md
scp -p docs/NODA1-MEMORY-RUNBOOK.md \
root@144.76.224.179:/opt/microdao-daarion/docs/NODA1-MEMORY-RUNBOOK.md
# 3) На NODA1: rebuild + restart + лог
ssh root@144.76.224.179 '
cd /opt/microdao-daarion &&
docker compose -f docker-compose.node1.yml ps &&
docker compose -f docker-compose.node1.yml build render-pdf-worker &&
docker compose -f docker-compose.node1.yml up -d render-pdf-worker &&
docker logs --tail 120 render-pdf-worker-node1
'
```
Якщо у compose сервіс називається інакше (наприклад `render-pdf-worker-node1` лише як container_name), замінити **лише** аргумент у `build` / `up -d`. Командою `docker compose ... ps` це одразу видно.
**Швидка валідація після синку (на NODA1):**
```bash
docker ps --filter name=render-pdf-worker --format 'table {{.Names}}\t{{.Status}}'
docker logs --tail 120 render-pdf-worker-node1 | egrep -i 'idle, no messages|subscribed|error|exception' || true
```
---
## Acceptance checks (ловлять регрес одразу)
```bash
# Memory/Qdrant (регрес на DOCKER-USER або мережу)
curl -s -o /dev/null -w "memory /health HTTP=%{http_code}\n" http://127.0.0.1:8000/health
docker exec dagi-memory-service-node1 sh -lc 'curl -s -o /dev/null -w "qdrant-from-memory HTTP=%{http_code}\n" --connect-timeout 5 http://dagi-qdrant-node1:6333/healthz' 2>/dev/null || true
iptables -L DOCKER-USER -n --line-numbers | head -25
# NATS: відрізнити "немає підключення" від "немає задач" (якщо воркер Exited — перевірка з іншого контейнера)
# Якщо в контейнері немає nc — альтернативи: getent hosts nats; curl -v --connect-timeout 2 telnet://nats:4222; python -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('nats',4222)); s.close(); print('TCP OK')"
docker exec render-pdf-worker-node1 sh -lc 'getent hosts nats && nc -vz -w 2 nats 4222' 2>/dev/null || echo "worker cannot reach nats"
# render-pdf-worker: якір — підтвердження що піднялась версія з idle-heartbeat
docker logs --tail 200 render-pdf-worker-node1 | egrep -i 'idle, no messages' && echo "worker heartbeat OK" || echo "heartbeat not seen (yet)"
# postgres-backup: mismatch зник (конкретний сигнал); якщо 1015 хв лишається "starting" — зібрати Health.Log і логи
docker inspect postgres-backup-node1 --format '{{json .State.Health}}' | jq .
docker inspect postgres-backup-node1 --format '{{range .State.Health.Log}}{{println .End " exit=" .ExitCode " " .Output}}{{end}}'
docker logs --tail 200 postgres-backup-node1
docker logs --tail 200 postgres-backup-node1 | egrep -i 'pg_dump.*version|mismatch' && echo "STILL MISMATCH" || echo "mismatch not seen in tail"
docker inspect postgres-backup-node1 --format '{{.State.Health.Status}}'
# Proxy ownership: nginx ок, caddy off
systemctl is-active nginx
systemctl is-enabled caddy 2>/dev/null || true
ss -ltnp | egrep ':(80|443)\s'
```
---
## 6) Контрольний список приймання після патчів
- [ ] `docker compose -f docker-compose.node1.yml config` без помилок.
- [ ] `docker compose -f docker-compose.backups.yml config` без помилок.
- [ ] `docker compose up -d` (відповідно до ваших файлів) без помилок.
- [ ] **postgres-backup:** health 200/healthy або лог без "version mismatch"; успішний pg_dump.
- [ ] **render-pdf-worker:** не падає з NATS timeout; або стабільно працює як long-running worker.
- [ ] **Проксі:** або nginx, або Caddy стабільно тримає 80/443; інший вимкнений (disable).
---
**Останнє оновлення:** 2026-01-28
**Джерело:** зібрані фрагменти з NODA1 (read-only), effective config з `docker-compose.node1.yml` та `docker-compose.backups.yml`.