ligbox-ops-platform/specs/035-ligbox-watchman/spec.md
Ligbox Spec Hub c1881f58e6 chore: sync Console SSO, DNS viewer, specs e infra docs pendentes
Inclui console handoff Desk↔Console (Spec 019), melhorias DNS Viewer (037),
OpenPanel/Nextcloud/VM116 deploy notes, contracts stack e sidebar actualizado.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 20:10:17 +00:00

275 lines
8.9 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.

# Spec 035 — Ligbox Watchman (vigilância contínua + alertas)
**Criado:** 2026-06-22
**Solicitado por:** Roger
**Status:** 📋 Draft — aguarda implementação Fase 1
**Prioridade:** **P0** (incidente Desk 502 sem alerta — 2026-06-22)
**Sistema:** VM122 (processo standalone) + monorepo `ligbox-ops-platform`
**Relacionado:** Spec **033** (stack health UI) · Spec **002** (Wazuh) · Spec **007** (push) · VM115 ntfy
---
## Resumo
**Ligbox Watchman** é um processo **leve, contínuo e independente** do Desk API que monitoriza **todos** os serviços do stack Ligbox (VMs 112, 114, 122, 123, 130 e extensões), regista estado, e **alerta** quando um serviço deixa de responder — **antes** de um utilizador reportar “a página não abre”.
**Motivação (incidente real):** `desk.ligbox.com.br` devolveu **HTTP 502** porque `ligbox-ops-platform_frontend_1` e `api_1` estavam **Exited** na VM122 após reboot. O Traefik estava OK; o **INFRA CODE** do Desk não alertou porque depende da própria API.
**Princípio:** Watchman **não morre** quando o Desk morre.
---
## Problema
| Lacuna actual | Impacto |
|---------------|---------|
| `stack_health.py` só via API Desk | Sem alerta se API/UI caírem |
| Sem processo 24/7 dedicado | Falhas após reboot passam despercebidas |
| Docker sem `restart: unless-stopped` | Containers ficam parados (exit 255) |
| Wazuh (VM104) | SIEM — não substitui uptime sintético por URL |
---
## Decisões de arquitectura
| # | Tema | Decisão |
|---|------|---------|
| 1 | Onde corre | **VM122** — host com LAN para 112/114/123/130; processo **fora** do container `api` |
| 2 | Catálogo | **Fonte única** YAML `contracts/stack-services.yaml`; Desk importa o mesmo catálogo (refactor Spec 033) |
| 3 | Intervalo | **Tier crítico** 30 s · **tier standard** 60 s · **tier slow** 120 s — não 1 s global (anti-ruído) |
| 4 | Anti-flap | Alerta só após **N falhas consecutivas** (default **2**) |
| 5 | Recovery | Notificação **RESOLVED** quando serviço volta OK após alerta |
| 6 | Estado | SQLite local `watchman.db` (~MB) — histórico 7 dias |
| 7 | Alertas | **ntfy** (primário) + **email** Postfix (secundário) + webhook Desk (opcional) |
| 8 | Recursos | 1 processo Python, `asyncio` + pool HTTP, RAM alvo **&lt; 64 MB** |
| 9 | Deploy | `systemd` `ligbox-watchman.service` + opcional container Docker dedicado |
| 10 | RBAC | Sem UI própria v1 — leitura via ficheiro status JSON + integração futura Desk |
---
## Arquitectura
```mermaid
flowchart LR
subgraph VM122
W[watchman.py systemd]
DB[(watchman.db SQLite)]
W --> DB
end
subgraph LAN
VM112[VM112]
VM114[CT114]
VM123[VM123]
VM130[CT130]
end
W -->|HTTP/TCP probes| VM112
W -->|HTTP/TCP probes| VM114
W -->|HTTP/TCP probes| VM123
W -->|HTTP/TCP probes| VM130
W -->|HTTPS WAN| desk.ligbox.com.br
W -->|POST| ntfy[VM115 ntfy]
W -->|SMTP| postfix[Postfix Proxmox]
W -.->|opcional| desk_hook[Desk webhook]
```
---
## Catálogo de serviços (v1)
Herda Spec 033 (`stack_health.py`) — **~22 probes** em 5 VMs:
| VM | Serviços |
|----|----------|
| **112** | Onboard API, Wizard UI, Carbonio, Domain Admin API |
| **114** | Traefik API, Router Desk WAN, Router API Ops WAN |
| **122** | Desk API LAN, Desk UI :8091, Redis, integrações locais |
| **123** | FOSSBilling, Odoo, OpenPanel, OpenAdmin, Bridge :18087, Ops Console, phpMyAdmin, Ollama |
| **130** | Forgejo, Spec Portal LAN, Spec Hub WAN |
**Fase 2 (extensão catálogo):** VM104 Wazuh, CT107 Fluxus, VM116 RustDesk, VM124 Nextcloud, pfSense, Proxmox host ping.
Schema: [contracts/stack-services.yaml](./contracts/stack-services.yaml).
---
## Tiers e intervalos
| Tier | Intervalo | Exemplos |
|------|-----------|----------|
| `critical` | 30 s | `desk.ligbox.com.br`, `api.ops.ligbox.com.br`, Traefik, Desk API/UI LAN |
| `standard` | 60 s | Onboard, OpenPanel, Forgejo, FOSSBilling |
| `slow` | 120 s | Ollama, phpMyAdmin, Carbonio HTTPS LAN |
Cada ciclo do Watchman executa apenas probes **due** (scheduler por `next_check_at`), não todos de uma vez cada segundo.
---
## Alertas
### Canais (ordem de envio)
1. **ntfy**`https://ntfy.ligbox.com.br/ligbox-watchman` (tópico configurável; auth opcional)
2. **Email**`admin@ligbox.com.br` via SMTP LAN (Postfix host ou VM122)
3. **Desk** (opcional) — `POST` evento interno para tab Eventos
### Formato ntfy (DOWN)
```
Title: 🔴 DOWN vm122-desk-ui
Body: Desk Frontend — HTTP timeout (3 failures)
Tags: warning,skull
Priority: high
```
### Formato ntfy (RESOLVED)
```
Title: 🟢 OK vm122-desk-ui
Body: Desk Frontend — recovered after 19m down
Tags: white_check_mark
Priority: default
```
### Anti-spam
- Máximo **1 alerta DOWN** por serviço por janela de **15 min** (repeat throttle)
- RESOLVED sempre enviado uma vez por incidente
---
## Complemento Docker (VM122)
Independentemente do Watchman, aplicar na **produção** Desk:
```yaml
restart: unless-stopped
```
em `frontend`, `api`, `worker`, `redis` no `docker-compose.mvp.yml` — previne classe de incidente 2026-06-22.
---
## Estrutura no monorepo
```
projects/integrations/watchman/
watchman.py # daemon principal
probes.py # HTTP, TCP, Redis, Docker (opcional)
catalog.py # load YAML
alerts.py # ntfy, email, desk webhook
store.py # SQLite state
requirements.txt
README.md
contracts/stack-services.yaml # catálogo partilhado (fonte de verdade)
deploy/watchman/
install.sh # systemd + venv
ligbox-watchman.service
watchman.env.example
docker-compose.watchman.yml # alternativa container
specs/035-ligbox-watchman/
spec.md
tasks.md
quickstart.md
contracts/
```
**Refactor Spec 033:** `stack_health.py` passa a ler `contracts/stack-services.yaml` + registry de probe handlers (evita duplicação).
---
## API / ficheiros de status (v1)
| Artefacto | Path | Descrição |
|-----------|------|-----------|
| Status JSON | `/var/lib/ligbox-watchman/status.json` | Snapshot último ciclo (para Desk/nginx) |
| SQLite | `/var/lib/ligbox-watchman/watchman.db` | Histórico incidentes |
| Log | `journalctl -u ligbox-watchman` | stdout estruturado |
Formato `status.json`: ver [contracts/status-snapshot.md](./contracts/status-snapshot.md).
**Fase 2:** `GET /api/v1/watchman/status` no Desk (read-only proxy do JSON).
---
## RBAC e segurança
| Item | Regra |
|------|-------|
| Credenciais ntfy/SMTP | `watchman.env` — não commitar |
| Probes WAN | só URLs públicas; LAN sem secrets em logs |
| OPENPANEL_BRIDGE_TOKEN | herdado de env VM122 (mesmo que Desk) |
| Acesso status.json | `root:ligbox-watchman` ou leitura Desk via grupo |
---
## Fases de implementação
### Fase 0 — Mitigação imediata (sem Watchman)
- [ ] `restart: unless-stopped` em `docker-compose.mvp.yml` produção VM122
- [ ] Documentar no quickstart
### Fase 1 — Watchman MVP
- [ ] `contracts/stack-services.yaml` extraído de `stack_health.py`
- [ ] `watchman.py` + systemd na VM122
- [ ] Alertas ntfy
- [ ] SQLite + anti-flap
- [ ] `status.json` para consulta
### Fase 2 — Integração Desk + email
- [ ] Refactor `stack_health.py` → YAML
- [ ] Email Postfix
- [ ] Card “Watchman” em INFRA CODE (último alerta)
- [ ] Catálogo VM104, 107, 116, 124
### Fase 3 — Auto-remediação (opcional)
- [ ] Probe `docker` local: se `frontend`/`api` down → `docker start` (com confirmação Roger)
- [ ] Integração Agentic Ops (Spec 029) — ticket automático
---
## Critérios de aceitação (Fase 1)
1. Parar `ligbox-ops-platform_frontend_1` → alerta ntfy em **&lt; 2 min** (2×30 s critical).
2. Subir frontend → alerta **RESOLVED** em **&lt; 2 min**.
3. Parar Desk API → Watchman **continua** a alertar (processo systemd independente).
4. `status.json` actualizado a cada ciclo; `summary.degraded` correcto.
5. RAM do processo **&lt; 64 MB** em steady state (medido 1 h).
6. Catálogo YAML versionado no Forgejo; alteração de URL = PR no repo.
---
## Fora de escopo (v1)
- UI web própria do Watchman (usar ntfy + Desk INFRA)
- Prometheus/Grafana (peso excessivo para P0)
- Monitorização Proxmox host metrics (Fase 2+)
- Substituição do Wazuh
---
## Documentos relacionados
| Documento | Conteúdo |
|-----------|----------|
| [quickstart.md](./quickstart.md) | Instalação VM122 |
| [tasks.md](./tasks.md) | Checklist |
| [contracts/stack-services.yaml](./contracts/stack-services.yaml) | Catálogo probes |
| [contracts/status-snapshot.md](./contracts/status-snapshot.md) | Schema JSON |
| `specs/033-desk-infra-console-ui/spec.md` | UI stack health |
| `projects/ops-desk/api/app/stack_health.py` | Implementação actual (a migrar) |
---
## Incidente de referência
**2026-06-22:** `https://desk.ligbox.com.br/` → HTTP **502** (Traefik OK, backend VM122:8091 down). Containers `Exited (255)` ~19 h. Causa provável: reboot VM sem restart Docker. **Watchman teria alertado em &lt; 1 min** (tier critical).