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>
479 lines
19 KiB
Markdown
479 lines
19 KiB
Markdown
# 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 зник (конкретний сигнал); якщо 10–15 хв лишається "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`.
|