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>
275 lines
8.9 KiB
Markdown
275 lines
8.9 KiB
Markdown
# 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 **< 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 **< 2 min** (2×30 s critical).
|
||
2. Subir frontend → alerta **RESOLVED** em **< 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 **< 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 < 1 min** (tier critical).
|