- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
20 KiB
🌐 Децентралізована мережа нод - Архітектура автоматичного підключення
Дата: 2025-11-23
Версія: 2.0.0
Статус: 🎯 Production Architecture
🎯 Мета
Створити повністю автоматизовану систему для додавання нових нод до децентралізованої мережі штучного інтелекту без ручного втручання.
🏗️ Архітектура мережі
Поточна структура
┌─────────────────────────────────────────────────────────┐
│ DAARION AI Network │
├─────────────────────────────────────────────────────────┤
│ │
│ НОДА1 (Hetzner) ◄──────────────────┐ │
│ 144.76.224.179 │ │
│ ├─ Node Registry (9205) │ │
│ ├─ NATS JetStream (4222) │ │
│ ├─ DAGI Router (9102) │ Discovery │
│ └─ Gateway (9300) │ & Heartbeat │
│ │ │
│ НОДА2 (MacBook M4 Max) ─────────────┘ │
│ 192.168.1.33 (local) │
│ ├─ Development Node │
│ ├─ 50 DAARION Agents │
│ └─ 8 AI Models │
│ │
│ НОДА3-N (Future) ───────────────────┐ │
│ Any location │ │
│ └─ Auto-registration ────────────────┘ │
└─────────────────────────────────────────────────────────┘
🔧 Компоненти системи
1. Node Registry Service (Вже існує!)
Місце: НОДА1 (144.76.224.179:9205)
Статус: ✅ Готовий (потрібна імплементація API)
Призначення:
- Центральний реєстр всіх нод
- Автоматична реєстрація нових нод
- Heartbeat tracking (keep-alive)
- Node discovery
База даних:
-- nodes table
node_id VARCHAR(255) -- Унікальний ID ноди
node_name VARCHAR(255) -- Людське ім'я
ip_address INET -- Публічна IP
local_ip INET -- Локальна IP
status VARCHAR(50) -- online, offline, degraded
last_heartbeat TIMESTAMP -- Останній heartbeat
capabilities JSONB -- GPU, CPU, RAM, моделі
API Endpoints:
POST /api/v1/nodes/register # Реєстрація нової ноди
POST /api/v1/nodes/heartbeat # Heartbeat (keep-alive)
GET /api/v1/nodes # Список всіх нод
GET /api/v1/nodes/{node_id} # Інфо про конкретну ноду
GET /api/v1/nodes/discover # Знайти ноди з певними capabilities
2. Bootstrap Agent (Авто-реєстрація)
Файл: tools/dagi_node_agent/bootstrap.py
Статус: ✅ Існує, потрібне покращення
Як працює:
# Запуск на новій ноді
python3 -m tools.dagi_node_agent.bootstrap \
--role production-node \
--labels gpu,cpu,ollama \
--registry-url http://144.76.224.179:9205
Що робить:
- ✅ Автоматично визначає hostname, IP
- ✅ Генерує унікальний node_id
- ✅ Збирає інформацію про систему (CPU, RAM, GPU)
- ✅ Реєструється в Node Registry
- ✅ Зберігає node_id локально
- ✅ Запускає heartbeat loop
3. Heartbeat Service (Keep-Alive)
Призначення: Підтримувати з'єднання з Registry
Механізм:
# Кожні 30 секунд відправляє heartbeat
while True:
await send_heartbeat(node_id, metrics)
await asyncio.sleep(30)
Метрики що відправляються:
- CPU usage %
- RAM usage %
- GPU usage % (якщо є)
- Disk usage %
- Активні сервіси
- Кількість агентів
- Статус моделей
4. Discovery Service (Пошук нод)
Призначення: Знайти ноду з потрібними capabilities
API Query:
# Знайти ноду з GPU
GET /api/v1/nodes/discover?capability=gpu&status=online
# Знайти ноду з конкретною моделлю
GET /api/v1/nodes/discover?model=deepseek-r1:70b
# Знайти найближчу ноду
GET /api/v1/nodes/discover?latency=min®ion=europe
Response:
{
"nodes": [
{
"node_id": "node-1-hetzner",
"ip_address": "144.76.224.179",
"latency_ms": 45,
"capabilities": {
"gpu": "NVIDIA RTX 4000",
"models": ["qwen3:8b", "mistral:7b"]
}
}
]
}
🚀 Автоматичне підключення нової ноди
Сценарій: Додавання НОДА3
Крок 1: Встановлення на новій ноді
# На НОДА3
git clone git@github.com:IvanTytar/microdao-daarion.git
cd microdao-daarion
pip install -r requirements.txt
Крок 2: Конфігурація (один файл)
# Створити .env файл
cat > .env << EOF
# Node Registry URL
NODE_REGISTRY_URL=http://144.76.224.179:9205
# NATS для комунікації
NATS_URL=nats://144.76.224.179:4222
# Роль ноди
NODE_ROLE=worker-node
NODE_LABELS=gpu,inference,home
# Ollama (якщо є)
OLLAMA_BASE_URL=http://localhost:11434
EOF
Крок 3: Автоматична реєстрація
# Запустити bootstrap
python3 -m tools.dagi_node_agent.bootstrap --auto
Що відбудеться автоматично:
- ✅ Визначить hostname та IP
- ✅ Згенерує унікальний node_id
- ✅ Зсканує систему (CPU, RAM, GPU, диск)
- ✅ Перевірить наявність Ollama та моделей
- ✅ Зареєструється в Node Registry
- ✅ Запустить heartbeat service
- ✅ Стане доступною в мережі через 30 секунд
Крок 4: Перевірка
# На будь-якій ноді
curl http://144.76.224.179:9205/api/v1/nodes
# Побачите НОДА3 в списку!
🔐 Безпека та автентифікація
Рівень 1: API Key (Базовий)
# При реєстрації генерується API key
NODE_API_KEY=sk-node-abc123xyz
# Всі запити до Registry потребують ключ
curl -H "Authorization: Bearer $NODE_API_KEY" \
http://144.76.224.179:9205/api/v1/nodes
Рівень 2: mTLS (Mutual TLS)
# Кожна нода має свій сертифікат
NODE_CERT=/etc/dagi/certs/node.crt
NODE_KEY=/etc/dagi/certs/node.key
CA_CERT=/etc/dagi/certs/ca.crt
# Всі з'єднання через TLS
Рівень 3: Whitelist (IP або node_id)
# В Node Registry
ALLOWED_NODES = [
"node-1-hetzner",
"node-2-macbook",
# Автоматично додаються після верифікації
]
🌍 Топології мережі
Топологія 1: Централізована (поточна)
НОДА1 (Registry)
/ | \
/ | \
НОДА2 НОДА3 НОДА4
Плюси:
- ✅ Простота
- ✅ Один point of truth
- ✅ Легке управління
Мінуси:
- ⚠️ Single point of failure
- ⚠️ Bottleneck при великій кількості нод
Топологія 2: Федеративна (майбутнє)
НОДА1 (Registry-EU) ◄────► НОДА5 (Registry-US)
/ | \ / | \
НОДА2 3 4 НОДА6 7 8
Плюси:
- ✅ Відмовостійкість
- ✅ Географічна близькість
- ✅ Масштабованість
Мінуси:
- ⚠️ Складніша синхронізація
- ⚠️ Eventual consistency
Топологія 3: P2P (повна децентралізація)
НОДА1 ◄──► НОДА2
▲ \ / ▲
│ \ / │
│ ◄ │
└─► НОДА3 ◄─┘
Плюси:
- ✅ Повна децентралізація
- ✅ Максимальна відмовостійкість
- ✅ Немає central authority
Мінуси:
- ⚠️ Складна імплементація
- ⚠️ Consensus протоколи потрібні
📡 Протоколи комунікації
1. HTTP/REST (Реєстрація та discovery)
Використання: Реєстрація, heartbeat, queries
Протокол: HTTP/HTTPS
Порт: 9205
2. NATS JetStream (Messaging)
Використання: Асинхронні повідомлення, events
Протокол: NATS
Порт: 4222
Topics:
- node.{node_id}.events
- node.{node_id}.tasks
- system.broadcast
3. gRPC (High-performance calls)
Використання: Швидкі запити між нодами
Протокол: gRPC/HTTP2
Порт: 9106 (майбутнє)
4. WebSocket (Real-time)
Використання: Real-time dashboard, monitoring
Протокол: WebSocket
Порт: 9107 (майбутнє)
🔄 Життєвий цикл ноди
1. Registration (Реєстрація)
НОДА3 → POST /api/v1/nodes/register → Node Registry
Response: {node_id, api_key, config}
2. Active (Працює)
НОДА3 → POST /api/v1/nodes/heartbeat (every 30s) → Registry
Registry → Marks node as "online"
3. Degraded (Проблеми)
Heartbeat timeout > 90s
Registry → Marks node as "degraded"
Alert sent to admin
4. Offline (Відключена)
Heartbeat timeout > 5min
Registry → Marks node as "offline"
Node removed from active routing
5. Deregistration (Видалення)
НОДА3 → DELETE /api/v1/nodes/{node_id} → Registry
Registry → Removes node from database
🛠️ Практична імплементація
Файл 1: bootstrap.py (Покращений)
#!/usr/bin/env python3
"""
Автоматична реєстрація ноди в DAARION Network
"""
import socket
import platform
import psutil
import requests
import uuid
from pathlib import Path
class NodeBootstrap:
def __init__(self, registry_url):
self.registry_url = registry_url
self.node_id = self.generate_node_id()
def generate_node_id(self):
"""Генерує унікальний node_id"""
hostname = socket.gethostname()
# Якщо є збережений ID - використати його
node_id_file = Path.home() / ".dagi" / "node_id"
if node_id_file.exists():
return node_id_file.read_text().strip()
# Інакше створити новий
short_uuid = str(uuid.uuid4())[:8]
node_id = f"node-{hostname}-{short_uuid}"
# Зберегти для майбутнього використання
node_id_file.parent.mkdir(parents=True, exist_ok=True)
node_id_file.write_text(node_id)
return node_id
def collect_capabilities(self):
"""Збирає інформацію про систему"""
capabilities = {
"hostname": socket.gethostname(),
"ip_address": self.get_public_ip(),
"local_ip": self.get_local_ip(),
"os": platform.system(),
"os_version": platform.version(),
"cpu_cores": psutil.cpu_count(),
"ram_gb": round(psutil.virtual_memory().total / (1024**3), 2),
"disk_gb": round(psutil.disk_usage('/').total / (1024**3), 2),
"gpu": self.detect_gpu(),
"ollama": self.check_ollama(),
}
return capabilities
def register(self):
"""Реєстрація в Node Registry"""
payload = {
"node_id": self.node_id,
"capabilities": self.collect_capabilities(),
"status": "online"
}
response = requests.post(
f"{self.registry_url}/api/v1/nodes/register",
json=payload,
timeout=10
)
response.raise_for_status()
return response.json()
def start_heartbeat(self):
"""Запускає heartbeat loop"""
import asyncio
asyncio.run(self.heartbeat_loop())
async def heartbeat_loop(self):
"""Безкінечний loop heartbeat"""
while True:
try:
metrics = self.collect_metrics()
requests.post(
f"{self.registry_url}/api/v1/nodes/heartbeat",
json={"node_id": self.node_id, "metrics": metrics},
timeout=5
)
except Exception as e:
print(f"Heartbeat failed: {e}")
await asyncio.sleep(30)
# ... (інші методи)
if __name__ == "__main__":
bootstrap = NodeBootstrap("http://144.76.224.179:9205")
print(f"🔧 Registering node: {bootstrap.node_id}")
result = bootstrap.register()
print(f"✅ Registered successfully!")
print(f" Node ID: {result['node_id']}")
print(f" API Key: {result['api_key']}")
print("💓 Starting heartbeat...")
bootstrap.start_heartbeat()
Файл 2: node_discovery.py (Пошук нод)
"""
Пошук доступних нод в мережі
"""
import requests
from typing import List, Dict, Optional
class NodeDiscovery:
def __init__(self, registry_url):
self.registry_url = registry_url
def find_nodes(
self,
role: Optional[str] = None,
capability: Optional[str] = None,
status: str = "online"
) -> List[Dict]:
"""Знайти ноди за критеріями"""
params = {"status": status}
if role:
params["role"] = role
if capability:
params["capability"] = capability
response = requests.get(
f"{self.registry_url}/api/v1/nodes/discover",
params=params
)
response.raise_for_status()
return response.json()["nodes"]
def find_best_node_for_model(self, model_name: str) -> Optional[Dict]:
"""Знайти найкращу ноду для конкретної моделі"""
nodes = self.find_nodes(capability="ollama")
for node in nodes:
if model_name in node["capabilities"].get("models", []):
return node
return None
def get_nearest_node(self, location: str = "auto") -> Dict:
"""Знайти найближчу ноду"""
response = requests.get(
f"{self.registry_url}/api/v1/nodes/discover",
params={"nearest": location}
)
response.raise_for_status()
return response.json()["nodes"][0]
# Використання
discovery = NodeDiscovery("http://144.76.224.179:9205")
# Знайти всі ноди з GPU
gpu_nodes = discovery.find_nodes(capability="gpu")
print(f"Found {len(gpu_nodes)} nodes with GPU")
# Знайти ноду з deepseek-r1:70b
node = discovery.find_best_node_for_model("deepseek-r1:70b")
if node:
print(f"Model available on: {node['node_name']}")
🎯 Переваги цієї архітектури
Для розробників
- ✅ Plug and Play - додали ноду за 5 хвилин
- ✅ Нульова конфігурація - все автоматично
- ✅ Discovery API - легко знайти потрібну ноду
Для системи
- ✅ Fault tolerance - автоматичне виявлення збоїв
- ✅ Load balancing - розподіл навантаження
- ✅ Horizontal scaling - додавай скільки потрібно нод
Для бізнесу
- ✅ Швидке масштабування - від 2 до 1000 нод
- ✅ Географічне розподілення - ноди по всьому світу
- ✅ Cost optimization - використання ресурсів по потребі
📈 Roadmap
Phase 1: Базова функціональність (ЗАРАЗ)
- Node Registry Service infrastructure
- Імплементація Node Registry API
- Bootstrap agent покращений
- Heartbeat service
- Discovery API
Phase 2: Безпека та моніторинг
- API key authentication
- mTLS certificates
- Prometheus metrics
- Grafana dashboards
- Alert system
Phase 3: Федерація
- Multi-region registries
- Registry replication
- Geographic routing
- Latency optimization
Phase 4: Повна децентралізація
- P2P discovery (DHT)
- Consensus protocol
- Blockchain registry (optional)
- Zero-knowledge proofs
✅ Що потрібно зробити ЗАРАЗ
1. Завершити Node Registry API
# Файл: services/node-registry/app/main.py
# Імплементувати endpoints:
POST /api/v1/nodes/register
POST /api/v1/nodes/heartbeat
GET /api/v1/nodes
GET /api/v1/nodes/{node_id}
GET /api/v1/nodes/discover
2. Покращити Bootstrap Agent
# Файл: tools/dagi_node_agent/bootstrap.py
# Додати:
- Auto-detection capabilities
- Ollama models scanning
- GPU detection (NVIDIA, Apple Silicon)
- Persistent node_id storage
3. Створити Frontend для моніторингу
# Додати сторінку в React:
/nodes/registry
- Список всіх нод
- Статус (online/offline)
- Capabilities
- Real-time heartbeat
🎯 Відповідь на ваше питання
Чи можливо автоматизувати додавання нод?
✅ ТАК! Це повністю можливо і вже частково реалізовано!
Що вже є:
- ✅ Node Registry Service (infraструктура готова)
- ✅ Bootstrap Agent (базова версія)
- ✅ Database schema для нод
- ✅ Docker конфігурація
Що треба:
- 🔧 Завершити Node Registry API (1-2 дні)
- 🔧 Покращити Bootstrap Agent (1 день)
- 🔧 Додати heartbeat service (1 день)
- 🔧 Створити Discovery API (1 день)
Результат:
# На будь-якій новій ноді просто запускаєте:
./scripts/join-network.sh
# І нода автоматично:
# 1. Реєструється
# 2. Починає heartbeat
# 3. Стає доступною в мережі
# 4. З'являється в dashboard
Статус: ✅ Архітектура готова, залишилась імплементація!
Часова оцінка: 4-5 днів розробки
Складність: Середня (Node Registry API + Bootstrap покращення)
Коли буде готово - у вас буде plug-and-play мережа штучного інтелекту! 🚀