Files
microdao-daarion/docs/runbook/matrix-bridge-dagi-ops.md
2026-03-05 01:12:49 -08:00

375 lines
13 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.
# Matrix Bridge DAGI — Ops Runbook (H4)
**Сервіс:** `matrix-bridge-dagi` | **Нода:** NODA1 | **Порт:** 7030 (localhost)
**Stack:** Matrix (Synapse) → bridge → Router `/v1/agents/{id}/infer` → Matrix reply
**Фаза:** M1 (1 room = Sofiia), H1/H2/H3 hardening активний
---
## 0. Purpose
Операційні перевірки та troubleshooting для `matrix-bridge-dagi` на NODA1:
- Matrix ↔ Bridge ↔ Router ↔ Agent ↔ Matrix
- Audit events в sofiia-console (`POST /api/audit/internal`)
- Rate limit (room/sender RPM)
- Backpressure queue (drops/queue wait)
---
## 1. Quick Status (30 секунд)
### 1.1 Health
```bash
curl -sS http://127.0.0.1:7030/health | python3 -m json.tool
```
Очікування:
```json
{
"ok": true,
"matrix_reachable": true,
"gateway_reachable": true,
"mappings_count": 1,
"queue": {"size": 0, "max": 100, "workers": 2},
"rate_limiter": {"room_rpm_limit": 20, "sender_rpm_limit": 10, "active_rooms": 0, "active_senders": 0}
}
```
| Поле | Норма | Тривога |
|------|-------|---------|
| `ok` | `true` | `false` → дивитись `error` |
| `matrix_reachable` | `true` | `false` → I4 |
| `gateway_reachable` | `true` | `false` → I1 |
| `queue.size` | ≈0 idle | >50 → I3 |
| `mappings_count` | ≥1 | 0 → перевірити `BRIDGE_ROOM_MAP` |
### 1.2 Mappings
```bash
curl -sS http://127.0.0.1:7030/bridge/mappings | python3 -m json.tool
```
### 1.3 Logs (останні 30 рядків)
```bash
docker logs matrix-bridge-dagi-node1 --tail 30
```
Шукати: `ERROR`, `Rate limited`, `Queue full`, `Invoke ok`, `Reply sent`
---
## 2. Smoke Test (23 хв)
### 2.1 E2E через Element (рекомендовано)
1. Element → "Change homeserver" → `matrix.daarion.space`
2. Логін: `test_user` / `TestUser_2026!`
3. Room: **DAGI — Sofiia**
4. Відправити: `ping`
5. Очікування: reply ≤ 5s (Mistral/DeepSeek залежний)
### 2.2 Smoke через curl (без Element)
```bash
# Логін як test_user
TOKEN=$(curl -sS -X POST http://localhost:8008/_matrix/client/v3/login \
-H 'Content-Type: application/json' \
-d '{"type":"m.login.password","identifier":{"type":"m.id.user","user":"test_user"},"password":"TestUser_2026!"}' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')
# Відправити повідомлення
ROOM_ID=$(grep 'SOFIIA_ROOM_ID' /opt/microdao-daarion/.env | cut -d= -f2)
TXN_ID="smoke-$(date +%s)"
curl -sS -X PUT "http://localhost:8008/_matrix/client/v3/rooms/$ROOM_ID/send/m.room.message/$TXN_ID" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"msgtype":"m.text","body":"smoke test ping"}' | python3 -c 'import sys,json; print("sent:", json.load(sys.stdin).get("event_id","ERROR"))'
# Чекати reply (~15-30s залежить від sync cycle)
sleep 30
# Перевірити health metrics
curl -sS http://127.0.0.1:7030/metrics | grep -E 'matrix_bridge_(messages_received|messages_replied)_total'
```
### 2.3 Перевірити .well-known (для Element auto-discovery)
```bash
curl -sS https://matrix.daarion.space/.well-known/matrix/client
# Очікування: {"m.homeserver":{"base_url":"https://matrix.daarion.space"}}
```
---
## 3. Операційні Метрики
### 3.1 Traffic counters
```bash
curl -sS http://127.0.0.1:7030/metrics | grep -E \
"matrix_bridge_messages_(received|replied)_total|matrix_bridge_rate_limited_total|matrix_bridge_queue_dropped_total"
```
| Метрика | Значення | Що означає |
|---------|----------|------------|
| `messages_received_total` | росте | повідомлення надходять |
| `messages_replied_total{status="ok"}` | росте | агент відповідає |
| `messages_replied_total{status="error"}` | >0 | проблема send_text → I1 |
| `rate_limited_total` | ≈0 | норма; >0 → I2 |
| `queue_dropped_total` | 0 | норма; >0 → I3 |
### 3.2 Latency histograms
```bash
curl -sS http://127.0.0.1:7030/metrics | grep -E \
"(invoke_duration|send_duration|queue_wait)_seconds_(bucket|count|sum)"
```
Орієнтири:
| Histogram | Норма (p95) | Тривога |
|-----------|-------------|---------|
| `invoke_duration_seconds` | 15s | >20s → LLM деградація |
| `send_duration_seconds` | <200ms | >2s → Synapse проблема |
| `queue_wait_seconds` | <50ms idle | >5s → I3 (workers перевантажені) |
### 3.3 Queue state
```bash
curl -sS http://127.0.0.1:7030/health | python3 -c \
'import sys,json; d=json.load(sys.stdin); print("queue:", d.get("queue"))'
```
---
## 4. Audit Events (sofiia-console)
### 4.1 Перевірити audit ingest (internal endpoint)
```bash
INT_TOKEN=$(grep 'SOFIIA_INTERNAL_TOKEN' /opt/microdao-daarion/.env | cut -d= -f2)
curl -sS -X POST http://127.0.0.1:8002/api/audit/internal \
-H 'Content-Type: application/json' \
-H "X-Internal-Service-Token: $INT_TOKEN" \
-d '{"event":"matrix.smoke_check","agent_id":"sofiia","node_id":"NODA1","status":"ok"}' | python3 -m json.tool
# Очікування: {"ok": true, "event": "matrix.smoke_check"}
```
### 4.2 Події для пошуку в audit log
| Event | Коли | Норма |
|-------|------|-------|
| `matrix.message.received` | при кожному вхідному | завжди |
| `matrix.agent.replied` | при успішній відповіді | завжди |
| `matrix.rate_limited` | при перевищенні RPM | рідко/ніколи |
| `matrix.queue_full` | при переповненні черги | ніколи |
| `matrix.error` | при помилці invoke/send | ніколи |
---
## 5. Common Incidents
### I1: "Element підключений, але Sofiia не відповідає"
```bash
# Step 1: health check
curl -sS http://127.0.0.1:7030/health | python3 -c 'import sys,json; d=json.load(sys.stdin); print("matrix:", d.get("matrix_reachable"), "gw:", d.get("gateway_reachable"), "maps:", d.get("mappings_count"))'
# Step 2: metrics — received vs replied
curl -sS http://127.0.0.1:7030/metrics | grep -E 'matrix_bridge_messages_(received|replied)_total'
# Step 3: logs
docker logs matrix-bridge-dagi-node1 --tail 50 | grep -E 'ERROR|invoke|reply|error'
# Step 4: router direct test
curl -sS -X POST http://127.0.0.1:9102/v1/agents/sofiia/infer \
-H 'Content-Type: application/json' \
-d '{"prompt":"ping","session_id":"smoke-check","user_id":"ops"}' | python3 -c 'import sys,json; d=json.load(sys.stdin); print("router:", d.get("response","ERROR")[:80])'
```
**Фікс:**
- `gateway_reachable: false` → перевірити `DAGI_GATEWAY_URL=http://dagi-router-node1:8000`
- `mappings_count: 0` → перевірити `BRIDGE_ROOM_MAP` в env
- received росте, replied ні → router або send_text проблема (Step 4)
---
### I2: "Bagato rate_limited в metrics"
```bash
curl -sS http://127.0.0.1:7030/metrics | grep rate_limited_total
curl -sS http://127.0.0.1:7030/health | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("rate_limiter"))'
```
**Причина:** RPM занадто низький або room flood (bot/скрипт).
**Фікс** (тимчасово, через env):
```bash
# На NODA1:
cd /opt/microdao-daarion
# Відредагувати .env:
# RATE_LIMIT_ROOM_RPM=40
# RATE_LIMIT_SENDER_RPM=20
docker compose -f docker-compose.matrix-bridge-node1.yml up -d
```
---
### I3: "Queue drops ростуть / queue.size велике"
```bash
curl -sS http://127.0.0.1:7030/metrics | grep queue_dropped_total
curl -sS http://127.0.0.1:7030/metrics | grep 'invoke_duration_seconds_bucket'
```
**Причина:** модель/Router повільні або `WORKER_CONCURRENCY` малий.
**Фікс:**
```bash
# Збільшити workers і queue (в .env):
# WORKER_CONCURRENCY=4
# QUEUE_MAX_EVENTS=300
docker compose -f docker-compose.matrix-bridge-node1.yml up -d
```
Паралельно перевірити latency histogram — якщо `invoke_duration` >20s, проблема на стороні LLM.
---
### I4: "matrix_reachable: false у /health"
```bash
# Synapse публічний ендпоінт
curl -sS https://matrix.daarion.space/_matrix/client/versions | python3 -c 'import sys,json; d=json.load(sys.stdin); print("ok, versions:", list(d.get("versions",[]))[:3])'
# Synapse внутрішній (з NODA1)
curl -sS http://localhost:8008/_matrix/client/versions
# Synapse контейнер
docker ps | grep synapse
docker logs dagi-synapse-node1 --tail 20
# Nginx
curl -sI https://matrix.daarion.space/_matrix/client/versions | head -3
```
**Фікс:**
- Synapse контейнер впав → `docker compose -f docker-compose.synapse-node1.yml up -d`
- TLS / .well-known → перевірити nginx (`nginx -t && nginx -s reload`)
- Токен невалідний → I5 (rotation)
---
### I5: "Замінити MATRIX_ACCESS_TOKEN"
```bash
# 1. Отримати новий токен (з NODA1)
NEW_TOKEN=$(curl -sS -X POST http://localhost:8008/_matrix/client/v3/login \
-H 'Content-Type: application/json' \
-d '{"type":"m.login.password","identifier":{"type":"m.id.user","user":"dagi_bridge"},"password":"DAGIbr1dge_M4tr1x_2026!"}' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')
echo "New token: ${#NEW_TOKEN} chars"
# 2. Оновити в .env
sed -i "s/^MATRIX_ACCESS_TOKEN=.*/MATRIX_ACCESS_TOKEN=$NEW_TOKEN/" /opt/microdao-daarion/.env
# 3. Restart bridge
cd /opt/microdao-daarion
docker compose -f docker-compose.matrix-bridge-node1.yml up -d
# 4. Verify
sleep 10
curl -sS http://127.0.0.1:7030/health | python3 -c 'import sys,json; d=json.load(sys.stdin); print("matrix:", d.get("matrix_reachable"))'
```
---
### I6: "Замінити SOFIIA_INTERNAL_TOKEN"
```bash
# 1. Генерувати новий токен
NEW_INT_TOKEN=$(openssl rand -base64 32 | tr -d '/+=')
# 2. Оновити в .env
sed -i "s/^SOFIIA_INTERNAL_TOKEN=.*/SOFIIA_INTERNAL_TOKEN=$NEW_INT_TOKEN/" /opt/microdao-daarion/.env
# 3. Restart обох сервісів
cd /opt/microdao-daarion
docker compose -f docker-compose.node1.yml up -d dagi-sofiia-console-node1
docker compose -f docker-compose.matrix-bridge-node1.yml up -d
sleep 15
# 4. Verify audit ingest
INT_TOKEN=$(grep 'SOFIIA_INTERNAL_TOKEN' .env | cut -d= -f2)
curl -sS -X POST http://127.0.0.1:8002/api/audit/internal \
-H 'Content-Type: application/json' \
-H "X-Internal-Service-Token: $INT_TOKEN" \
-d '{"event":"matrix.token_rotated","agent_id":"sofiia","node_id":"NODA1","status":"ok"}' | python3 -c 'import sys,json; print("audit ok:", json.load(sys.stdin).get("ok"))'
```
---
## 6. Restart / Rollback
### Restart bridge
```bash
cd /opt/microdao-daarion
docker compose -f docker-compose.matrix-bridge-node1.yml up -d --force-recreate
sleep 15
curl -sS http://127.0.0.1:7030/health | python3 -c 'import sys,json; d=json.load(sys.stdin); print("ok:", d["ok"])'
```
### Restart Synapse
```bash
docker compose -f docker-compose.synapse-node1.yml up -d
sleep 20
curl -sS http://localhost:8008/_matrix/client/versions | python3 -c 'import sys,json; print("synapse ok:", "versions" in json.load(sys.stdin))'
```
### Мінімальний smoke після restart
```bash
curl -sS http://127.0.0.1:7030/health | python3 -c \
'import sys,json; d=json.load(sys.stdin); print("bridge ok:", d["ok"], "| matrix:", d.get("matrix_reachable"), "| gw:", d.get("gateway_reachable"))'
# Потім: 1 повідомлення в Element → reply
```
---
## 7. Release Checklist (перед деплоєм bridge)
```
[ ] health ok (ok: true, matrix: true, gw: true)
[ ] queue drops == 0 (curl /metrics | grep queue_dropped_total → 0)
[ ] rate_limited == 0 (curl /metrics | grep rate_limited_total → 0)
[ ] smoke test: Element → ping → reply ≤ 5s
[ ] audit events з'являються (matrix.message.received, matrix.agent.replied)
[ ] .well-known → JSON з base_url
[ ] TLS cert valid (не <7 days до expiry)
```
### Перевірка TLS cert (термін дії)
```bash
echo | openssl s_client -connect matrix.daarion.space:443 -servername matrix.daarion.space 2>/dev/null \
| openssl x509 -noout -dates
# notAfter — запас не менше 7 днів (auto-renew certbot)
```
---
## 8. Що прикріпити до інциденту
```bash
echo "=== /health ===" && curl -sS http://127.0.0.1:7030/health | python3 -m json.tool
echo "=== /metrics traffic ===" && curl -sS http://127.0.0.1:7030/metrics | grep -E 'matrix_bridge_(messages|rate_limited|queue_dropped|gateway_errors)'
echo "=== /metrics latency ===" && curl -sS http://127.0.0.1:7030/metrics | grep -E '(invoke|send|queue_wait)_duration_seconds_(count|sum)'
echo "=== logs ===" && docker logs matrix-bridge-dagi-node1 --tail 50 2>&1 | grep -E 'ERROR|WARN|rate_limited|queue_full|Reply sent|invoke ok'
```