ligbox-ops-platform/specs/019-ops-console-active-operations/plan.md
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

15 KiB
Raw Blame History

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).

IA: design/navigation-ia.md

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 + Traefik
  • scripts/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:8100 ou 10.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)

  1. Novo webhook_event inserido → worker busca chamado aberto:
    • session_id + domain match
    • senão domain + status ∉ (fechado, resolvido) + created < 72h
    • senão wazuh.agent.id + domain/IP
  2. Se não encontrar → INSERT chamados + public_id sequencial
  3. INSERT chamado_eventos + update chamados.sources, max_severity, updated_at
  4. Eventos account.created / wazuh.alert L≥10 podem forçar criação imediata
  5. Extractor popula chamado_observables a 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)

  1. ChamadoHubLayout — shell 2 colunas
  2. InvestigationTimeline — eventos + notas + runbooks
  3. ObservablesPanel — entidades clicáveis (DrillDownLink)
  4. AssistPanel — Opção A
  5. InfraScorecardEmbed — proxy 009
  6. WazuhDeepLinkButton — novo tab VM104
  7. DiscoverAttachBar — seleccionar eventos → anexar ao hub activo

Tema Wazuh-like


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 live push: 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 11.5
F2 API chamados 122 1.52
F3 Console Docker MVP (3 views) 123 34
F4 Runbooks R0R2 + aprovações 122, 123 34
F5 Assist ops-status 112, 122, 123 23
F6 Agentic + WS 122, 123 34

MVP (F0F4): ~1012 semanas
Completo (F0F6): ~1620 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.sh OK
  • docker compose ps — só ligbox-ops-console-ui healthy
  • curl -I https://console.ligbox.com.br/health → 200
  • Login JWT Spec 003 funcional
  • Chamado teste CH-* com evento Wazuh + onboard
  • desk.ligbox.com.br → 302 console.ligbox.com.br (após 90d aviso)
  • Rollback: docker compose down no dir /opt/ligbox-ops-console apenas

Referências