Files
microdao-daarion/docs/tasks/TASK_PHASE_NODE_SELF_HEALING_v1.md
Apple bca81dc719 feat: Node Self-Healing, DAGI Audit, Agent Prompts, Infra Invariants
### Backend (city-service)
- Node Registry + Self-Healing API (migration 039)
- Improved get_all_nodes() with robust fallback for node_registry/node_cache
- Agent Prompts Runtime API for DAGI Router integration
- DAGI Router Audit endpoints (phantom/stale detection)
- Node Agents API (Guardian/Steward)
- Node metrics extended (CPU/GPU/RAM/Disk)

### Frontend (apps/web)
- Node Directory with improved error handling
- Node Cabinet with metrics cards
- DAGI Router Card component
- Node Metrics Card component
- useDAGIAudit hook

### Scripts
- check-invariants.py - deploy verification
- node-bootstrap.sh - node self-registration
- node-guardian-loop.py - continuous self-healing
- dagi_agent_audit.py - DAGI audit utility

### Migrations
- 034: Agent prompts seed
- 035: Agent DAGI audit
- 036: Node metrics extended
- 037: Node agents complete
- 038: Agent prompts full coverage
- 039: Node registry self-healing

### Tests
- test_infra_smoke.py
- test_agent_prompts_runtime.py
- test_dagi_router_api.py

### Documentation
- DEPLOY_CHECKLIST_2024_11_30.md
- Multiple TASK_PHASE docs
2025-11-30 13:52:01 -08:00

7.8 KiB
Raw Permalink Blame History

TASK_PHASE_NODE_SELF_HEALING_v1

Проєкт

DAARION.city — Nodes / Node Cabinet / DAGI Router

Фаза

Self-healing нод (автоматична реєстрація, відновлення та синхронізація)

Статус

COMPLETED


Мета

Зробити так, щоб:

  1. Ноди ніколи не "зникали" з Node Directory, якщо фізично існують і шлють heartbeat
  2. Реєстрація/оновлення нод виконувалась агентами ноди, а не ручними діями
  3. Node Directory → Node Cabinet → Node Metrics → DAGI Router були повністю узгоджені

Problem Statement

Симптом

  • /nodes (Node Directory) показує:
    • «Знайдено нод: 0»
    • «Помилка завантаження нод»
  • Хоча:
    • насправді NODE1/NODE2 є в node_cache
    • метрики, DAGI Router, агенти ноди працюють

Причини

  • Node Directory фронт дивився на іншу структуру даних
  • Реєстрація ноди не відпрацьовувала після деплою
  • Немає самовідновлюваної логіки на рівні нод

Рішення

1. Node Registry — єдине джерело істини

Таблиця: node_registry

CREATE TABLE node_registry (
    id text PRIMARY KEY,                    -- node_id
    name text NOT NULL,                     -- Людська назва
    hostname text,                          -- Hostname
    environment text NOT NULL,              -- production/development/staging
    roles text[] NOT NULL DEFAULT '{}',     -- ['gpu', 'ai_runtime', ...]
    description text,
    is_active boolean NOT NULL DEFAULT true,
    registered_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now(),
    last_self_registration timestamptz,     -- Остання самореєстрація
    self_registration_count integer DEFAULT 0
);

View для Node Directory:

CREATE VIEW v_nodes_directory AS
SELECT 
    r.*,
    c.cpu_model, c.gpu_model, c.ram_total, ...
    c.last_heartbeat,
    c.agent_count_router,
    c.agent_count_system,
    CASE 
        WHEN c.last_heartbeat < NOW() - INTERVAL '10 minutes' THEN 'stale'
        ELSE 'online'
    END AS connection_status
FROM node_registry r
LEFT JOIN node_cache c ON c.node_id = r.id
WHERE r.is_active = true;

2. Self-Registration API

Endpoint Метод Опис
/internal/nodes/register-or-update POST Самореєстрація ноди
/internal/node/{node_id}/heartbeat POST Heartbeat з метриками
/internal/node/{node_id}/directory-check GET Перевірка видимості
/internal/node/{node_id}/self-healing/status GET Статус self-healing
/internal/node/{node_id}/self-healing/trigger POST Тригер self-healing
/internal/nodes/needing-healing GET Список нод для healing

3. Node Bootstrap Script

Файл: scripts/node-bootstrap.sh

# Використання при старті ноди
NODE_ID=node-2-macbook-m4max \
NODE_NAME="MacBook Pro M4 Max" \
NODE_ENVIRONMENT=development \
NODE_ROLES=gpu,ai_runtime,development \
./scripts/node-bootstrap.sh

Що робить:

  1. Відправляє POST на /internal/nodes/register-or-update
  2. При успіху — відправляє початковий heartbeat
  3. При помилці — retry до 5 разів

4. Node Guardian Self-Healing Loop

Файл: scripts/node-guardian-loop.py

# Запуск як фоновий процес
NODE_ID=node-2-macbook-m4max \
NODE_NAME="NODE2" \
python scripts/node-guardian-loop.py --interval 60

# Одноразова перевірка
python scripts/node-guardian-loop.py --node-id node-2-macbook-m4max --once

Що перевіряє:

  1. Чи нода видима в Node Directory
  2. Чи є heartbeat
  3. Чи є Guardian/Steward агенти
  4. Чи є агенти в router

Self-healing дії:

  1. Якщо не видима — виконує self-registration
  2. Якщо heartbeat старий — відправляє новий
  3. Якщо статус error — тригерить healing через API

Файли

Файл Опис
migrations/039_node_registry_self_healing.sql Міграція для node_registry
services/city-service/repo_city.py Функції для self-healing
services/city-service/routes_city.py API endpoints
scripts/node-bootstrap.sh Bootstrap скрипт
scripts/node-guardian-loop.py Self-healing loop

Інваріанти Self-Healing

Умова Дія
Нода не в node_registry → self_register()
heartbeat > 10 хв → send_heartbeat()
agent_count_router = 0 → alert + try reinstall
guardian_agent_id = NULL → alert
self_healing_status = error → trigger_healing()

Використання

При першому деплої ноди

# 1. Запустити міграцію
psql -d daarion < migrations/039_node_registry_self_healing.sql

# 2. Запустити bootstrap
NODE_ID=node-2-macbook-m4max \
NODE_NAME="MacBook Pro M4 Max" \
NODE_ENVIRONMENT=development \
./scripts/node-bootstrap.sh

Запуск Guardian Loop

# Через systemd
[Unit]
Description=DAARION Node Guardian
After=network.target

[Service]
Environment=NODE_ID=node-2-macbook-m4max
Environment=NODE_NAME=NODE2
Environment=CITY_SERVICE_URL=http://localhost:7001
ExecStart=/usr/bin/python3 /path/to/scripts/node-guardian-loop.py
Restart=always

[Install]
WantedBy=multi-user.target

Через Docker Compose

services:
  node-guardian:
    image: python:3.11-slim
    environment:
      - NODE_ID=node-2-macbook-m4max
      - NODE_NAME=NODE2
      - CITY_SERVICE_URL=http://city-service:7001
    command: python /app/scripts/node-guardian-loop.py
    volumes:
      - ./scripts:/app/scripts
    depends_on:
      - city-service

Self-Healing сценарії

Сценарій 1: Нода зникла з Directory після деплою

1. Node Guardian запускається
2. check_visibility() → false
3. self_register() → успіх
4. check_visibility() → true
5. ✅ Нода знову в Directory

Сценарій 2: Heartbeat застарів

1. Node Guardian перевіряє статус
2. self_healing_status = "stale_heartbeat"
3. send_heartbeat() → успіх
4. ✅ Heartbeat оновлено

Сценарій 3: Agent count = 0

1. Node Guardian бачить agent_count_router = 0
2. Логує попередження
3. (Опційно) trigger_healing() для перевірки DAGI Router
4. ⚠️ Потребує уваги адміністратора

Acceptance Criteria

Критерій Статус
node_registry таблиця створена
API self-registration працює
node-bootstrap.sh виконує реєстрацію
node-guardian-loop.py запускається
Ноди видимі в /nodes після реєстрації
Self-healing при зникненні
Heartbeat оновлює статус

Наступні кроки

  1. Автоматичний DAGI Router reinstall при agent_count_router = 0
  2. NATS events для node healing (node.selfhealing.*)
  3. Prometheus metrics для self-healing
  4. Alert rules для критичних станів
  5. Node Federation — з'єднання нод між собою