15 KiB
Implementation Plan: Ligbox Ops Console — Operação Activa (019)
Branch: 019-ops-console-active-operations
Date: 2026-06-16
Spec: spec.md
Summary
Implementar a Ligbox Ops Console — navegação investigativa tipo Wazuh com CH-* como hub central (chamado único, assumir, runbooks, aprovações humano + agentic, Assist Opção A).
| Camada | Host | Deploy |
|---|---|---|
| Console UI | VM123 | Docker Compose (obrigatório — VM já tem serviços) |
| API motor | VM122 | Docker Compose existente (ligbox-ops-platform) |
| Detecção | VM104 | Wazuh manager (inalterado) |
| Eventos onboard | VM112 | systemd wizard |
URL pública: https://console.ligbox.com.br → Traefik CT114 → VM123:8100
API: https://api.ops.ligbox.com.br → VM122:8080
Technical Context
Language/Version
| Componente | Stack |
|---|---|
| Console UI | React 18 + Vite 6 + TypeScript (recomendado) ou React JS |
| API extensões | Python 3.11+ FastAPI (VM122 api/app/) |
| VM112 Assist | Python FastAPI — novo endpoint ops-status |
| Deploy VM123 | Docker Compose v2, nginx:alpine |
Primary Dependencies
- Console: React Router (
/chamados/:publicId= hub), TanStack Query, Zustand (auth) - API: FastAPI, sqlite3, httpx, websockets (Fase 6)
- Agentic: gateway LLM existente (A6/A7 — Fase 6)
Storage (VM122)
Novas tabelas SQLite (ver Phase 1). MVP mantém SQLite; migração Postgres fora de escopo.
Testing
scripts/verify-console-health.sh— VM123 Docker + Traefikscripts/verify-chamado-unico.sh— agregação eventos →CH-*scripts/verify-runbook-r1.sh— fila aprovação + execução mock
Target Platform
- VM123 LAN
10.10.10.123(confirmar IP no Proxmox) - Bind UI:
127.0.0.1:8100ou10.10.10.123:8100 - Comunicação VM123 → VM122: LAN HTTPS via Traefik ou direct
http://10.10.10.122:8080(dev)
Performance Goals
- Console first paint < 2s (gzip brotli via nginx)
- Discover feed < 800ms (paginação 50)
- Chamado detail + timeline < 500ms
- Poll Assist Opção A: 30s (MVP)
Constraints
- VM123: zero alteração em serviços/containers pré-existentes
- Sem fork
wazuh-dashboard/ OpenSearch - Sem replay browser (Opção A apenas)
- Runbooks R2/R3: audit log obrigatório
- JWT Spec 003 em todas as rotas Console
Constitution Check
| Princípio | Status |
|---|---|
| IV. Mail vs Ops | ✅ PASS — UI na VM123; motor VM122; VM112 só webhooks + ops-status read-only |
| V. Security baseline | ✅ PASS — fail2ban host VM123; container non-root; secrets em .env |
| VII. Spec-Driven | ✅ PASS |
| IX. YAGNI | ✅ PASS — SQLite; poll antes de WebSocket |
Project Structure
specs/019-ops-console-active-operations/
├── spec.md
├── plan.md
├── tasks.md
├── deploy/ # Copiar para /opt/ligbox-ops-console/ na VM123
│ ├── docker-compose.yml
│ ├── .env.example
│ ├── nginx/
│ │ └── default.conf
│ └── scripts/
│ ├── preflight-vm123.sh # Inventário portas antes do deploy
│ └── deploy-console.sh
├── contracts/
│ └── chamados-api.md
└── design/
└── tokens.css # Referência paleta Wazuh-like
# Repositório produção (a criar)
/opt/ligbox-ops-console/ # VM123
├── docker-compose.yml # ← cópia de deploy/
├── .env
├── frontend/ # SPA React
│ ├── Dockerfile
│ ├── src/
│ │ ├── views/ # Overview, Discover, ChamadosList, ChamadoHub
│ │ ├── components/ # Timeline, Observables, AssistPanel, DrillDownLink
│ │ ├── api/ # client → api.ops.ligbox.com.br
│ │ └── theme/ # tokens Wazuh-like
│ └── dist/ # build → nginx volume
/opt/ligbox-ops-platform/ # VM122 (existente)
├── api/app/
│ ├── main.py # + rotas /chamados, /discover, /aprovacoes
│ ├── chamados.py # agregação CH-*, estados
│ ├── runbooks/ # executor + políticas R0-R3
│ └── agentic/ # propostas (Fase 6)
└── worker/
└── chamado_aggregator.py # merge eventos → chamado_id
Phase 0: Pré-deploy VM123 (obrigatório)
Objectivo: garantir que Docker Console não conflita com serviços existentes.
# Na VM123 — antes de qualquer deploy
./scripts/preflight-vm123.sh
Checklist:
| # | Verificação | Comando | Critério |
|---|---|---|---|
| 1 | Porta 8100 livre | ss -tlnp | grep :8100 |
Vazio |
| 2 | Docker activo | docker info |
OK |
| 3 | Containers existentes | docker ps |
Listar — não parar |
| 4 | Disco | df -h /opt |
≥ 5 GB livres |
| 5 | IP LAN | ip -4 addr show |
Confirmar 10.10.10.123 |
| 6 | Acesso VM122 | curl -s -o /dev/null -w '%{http_code}' http://10.10.10.122:8080/api/health |
200 |
Documentar resultado em ticket interno ou docs/network/VM123_INVENTARIO.md (criar após 1º preflight).
Phase 1: Data Model — Chamado único (VM122)
-- Migração 019_001_chamados.sql
CREATE TABLE chamados (
id INTEGER PRIMARY KEY AUTOINCREMENT,
public_id TEXT NOT NULL UNIQUE, -- CH-2026-00042
status TEXT NOT NULL DEFAULT 'novo',
assignee TEXT,
domain TEXT,
session_id TEXT,
wizard_step TEXT,
wizard_step_at TEXT,
sources TEXT NOT NULL DEFAULT '[]', -- JSON array
max_severity INTEGER,
title TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX idx_chamados_domain ON chamados(domain);
CREATE INDEX idx_chamados_session ON chamados(session_id);
CREATE INDEX idx_chamados_status ON chamados(status);
CREATE TABLE chamado_eventos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chamado_id INTEGER NOT NULL REFERENCES chamados(id),
webhook_event_id INTEGER REFERENCES webhook_events(id),
event_type TEXT NOT NULL,
source TEXT NOT NULL,
payload TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE runbook_execucoes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chamado_id INTEGER NOT NULL REFERENCES chamados(id),
runbook_code TEXT NOT NULL,
nivel TEXT NOT NULL, -- R0, R1, R2, R3
status TEXT NOT NULL, -- pendente, aprovado, executando, ok, falhou, rejeitado
proposta TEXT, -- JSON agentic
actor TEXT, -- user | agent | system
resultado TEXT,
created_at TEXT NOT NULL,
executed_at TEXT
);
CREATE TABLE aprovacoes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
runbook_exec_id INTEGER NOT NULL REFERENCES runbook_execucoes(id),
aprovador TEXT,
decisao TEXT, -- aprovado | rejeitado
nota TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE chamado_observables (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chamado_id INTEGER NOT NULL REFERENCES chamados(id),
tipo TEXT NOT NULL, -- domain, session_id, agent, rule_id, ip, email
valor TEXT NOT NULL,
fonte TEXT NOT NULL, -- wazuh, onboard, manual, extractor
created_at TEXT NOT NULL,
UNIQUE(chamado_id, tipo, valor)
);
CREATE TABLE chamado_notas (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chamado_id INTEGER NOT NULL REFERENCES chamados(id),
autor TEXT NOT NULL,
texto TEXT NOT NULL,
created_at TEXT NOT NULL
);
-- Denormalizar chamado_id em webhook_events (opcional fase 1b)
ALTER TABLE webhook_events ADD COLUMN chamado_id INTEGER REFERENCES chamados(id);
Lógica de agregação (chamado_aggregator.py)
- Novo
webhook_eventinserido → worker busca chamado aberto:session_id+domainmatch- senão
domain+ status ∉ (fechado,resolvido) + created < 72h - senão
wazuh.agent.id+ domain/IP
- Se não encontrar →
INSERT chamados+public_idsequencial INSERT chamado_eventos+ updatechamados.sources,max_severity,updated_at- Eventos
account.created/wazuh.alertL≥10 podem forçar criação imediata - Extractor popula
chamado_observablesa partir de cada payload (domain, agent, rule_id, …)
Phase 2b: Hub API (VM122)
| Endpoint | Hub section |
|---|---|
GET .../chamados/{id} |
timeline, observables, assist, infra, links |
POST .../notas |
append timeline |
POST .../eventos/anexar |
Discover → hub |
GET .../links/wazuh |
deep link VM104 |
Helper build_wazuh_deep_link(agent, from, to, rule_id) — URL dashboard VM104.
Phase 2: API VM122
Ver contracts/chamados-api.md.
| Prioridade | Endpoint | Fase |
|---|---|---|
| P1 | GET /api/v1/chamados |
F2 |
| P1 | GET /api/v1/chamados/{public_id} |
F2 |
| P1 | POST /api/v1/chamados/{public_id}/assumir |
F2 |
| P1 | PATCH /api/v1/chamados/{public_id} |
F2 |
| P1 | GET /api/v1/discover |
F3 |
| P1 | POST /api/v1/chamados/{id}/runbooks/{code}/executar |
F4 |
| P1 | GET /api/v1/aprovacoes |
F4 |
| P1 | POST /api/v1/aprovacoes/{id}/aprovar |
F4 |
| P2 | WS /api/v1/chamados/{public_id}/live |
F6 |
Compatibilidade: GET /api/v1/desk/tickets mantém-se 90 dias; resposta inclui chamado_public_id quando mapeado.
Phase 3: Console UI — VM123 Docker
Stack deploy (referência)
Ficheiros em deploy/:
# VM123
rsync -av specs/019-ops-console-active-operations/deploy/ root@10.10.10.123:/opt/ligbox-ops-console/
ssh root@10.10.10.123 'cd /opt/ligbox-ops-console && cp .env.example .env && nano .env'
ssh root@10.10.10.123 'cd /opt/ligbox-ops-console && ./scripts/deploy-console.sh'
Importante: docker compose up -d só afecta serviços definidos neste compose — não docker compose down global na VM.
Traefik CT114 (label router)
# Adicionar em dynamic.yml ou labels do service discovery
http:
routers:
ligbox-ops-console:
rule: Host(`console.ligbox.com.br`)
entryPoints: [websecure]
service: ligbox-ops-console
tls:
certResolver: letsencrypt
services:
ligbox-ops-console:
loadBalancer:
servers:
- url: http://10.10.10.123:8100
Views MVP (F3)
| View | Rota | Conteúdo |
|---|---|---|
| Overview | / |
Stats, funil, alertas → link hub |
| Discover | /discover |
Feed + filtros → abrir hub |
| Chamados | /chamados |
Lista fila trabalho |
| Hub | /chamados/:publicId |
Investigação central — timeline + tabs |
| Tenants | /tenants |
Agentes/VMs; drill-down → hub |
| Aprovações | /aprovacoes |
Fila; cada item → hub origem |
Componentes hub (prioridade implementação)
ChamadoHubLayout— shell 2 colunasInvestigationTimeline— eventos + notas + runbooksObservablesPanel— entidades clicáveis (DrillDownLink)AssistPanel— Opção AInfraScorecardEmbed— proxy 009WazuhDeepLinkButton— novo tab VM104DiscoverAttachBar— seleccionar eventos → anexar ao hub activo
Tema Wazuh-like
- Ficheiro referência: design/tokens.css
- Navegação: design/navigation-ia.md
Phase 4: Runbooks + aprovações (VM122)
| Código | Nível | Executor |
|---|---|---|
infra_recheck |
R0 | HTTP VM112 /api/onboarding/infrastructure/status/{domain} |
traefik_cert_sync |
R1 | Script/API CT114 (Spec 011) |
admin_nginx_reload |
R1 | SSH VM112 ou API futura |
zmproxy_admin_provision |
R2 | SSH VM112 — humano obrigatório |
domain_purge |
R3 | API VM122 → VM112 Spec 017 — dupla aprovação |
wazuh_acknowledge |
R1 | Nota + tag chamado (VM104 ack futuro) |
escalate_kimi_human |
R0 | Link OB-* ticket → chamado |
Fluxo R1:
Técnico clica Executar → runbook_execucoes status=pendente
→ Agentic (opcional) gera proposta → aprovacoes fila
→ Humano Aprovar → worker executa → timeline + status ok|falhou
Phase 5: Assist Opção A (VM112 + Console)
VM112 — novo endpoint:
GET /api/onboarding/session/{session_id}/ops-status
Header: X-Ops-Secret: <OPS_STATUS_SECRET>
LAN only: 10.10.10.0/24
Resposta:
{
"session_id": "abc…",
"domain": "myvexx.com",
"wizard_step": "dns_cloudflare",
"wizard_step_label": "DNS Cloudflare",
"time_on_step_sec": 142,
"last_error": null,
"planned_email": "admin@myvexx.com"
}
VM122: poll 30s no detalhe chamado ou cache Redis ops:session:{id}.
Console: painel lateral AssistPanel — sem iframe wizard, sem senhas.
Phase 6: Agentic + WebSocket (pós-MVP)
- Agente A6 analisa chamado → propõe runbook R0/R1
- Políticas em
agentic/policies.yaml— confiança mínima, allowlist runbooks - WebSocket
livepush: novo evento + passo wizard (substitui poll)
Implementation Phases (time estimate)
| Fase | Entrega | VM | ~Semanas |
|---|---|---|---|
| F0 | Preflight VM123 + Traefik DNS | 123, 114 | 0.5 |
| F1 | Schema + aggregator | 122 | 1–1.5 |
| F2 | API chamados | 122 | 1.5–2 |
| F3 | Console Docker MVP (3 views) | 123 | 3–4 |
| F4 | Runbooks R0–R2 + aprovações | 122, 123 | 3–4 |
| F5 | Assist ops-status | 112, 122, 123 | 2–3 |
| F6 | Agentic + WS | 122, 123 | 3–4 |
MVP (F0–F4): ~10–12 semanas
Completo (F0–F6): ~16–20 semanas
Sequencing with other specs
| Spec | Relação |
|---|---|
| 001/002 | Eventos ingress → aggregator alimenta chamados |
| 004 | Funil widget no Overview; timeline enriquece chamado |
| 009 | Tenants view + scorecard drill-down |
| 010 | Runbooks admin + admin.validation.failed → chamado |
| 017/018 | Purge R3 + tile Serviços linka chamado |
Ordem recomendada: F1+F2 (API) em paralelo com F0+F3 scaffold UI Docker; F4 após API estável; F5 quando chamado único validado.
Risk & Mitigation
| Risco | Mitigação |
|---|---|
| Porta 8100 ocupada na VM123 | preflight-vm123.sh; variável CONSOLE_HOST_PORT no .env |
| Docker compose conflita rede | Rede bridge dedicada ligbox-console; não usar network_mode: host |
| CORS Console ↔ API | Allowlist console.ligbox.com.br no FastAPI VM122 |
| Agregação errada (chamados duplicados) | Regra session_id first; ferramenta merge manual ops |
| Runbook destrutivo sem R3 | Middleware bloqueia domain_purge sem dupla aprovação |
| VM123 serviços desconhecidos | Inventário obrigatório F0 antes deploy |
Deploy checklist (cutover)
preflight-vm123.shOKdocker compose ps— sóligbox-ops-console-uihealthycurl -I https://console.ligbox.com.br/health→ 200- Login JWT Spec 003 funcional
- Chamado teste
CH-*com evento Wazuh + onboard desk.ligbox.com.br→ 302console.ligbox.com.br(após 90d aviso)- Rollback:
docker compose downno dir/opt/ligbox-ops-consoleapenas