ligbox-ops-platform/specs/009-ops-audit-overview/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

4.3 KiB

Implementation Plan: Audit Overview Dashboard (009)

Branch: 009-ops-audit-overview | Date: 2026-06-08 | Spec: spec.md

Summary

Implementar Track A MVP: collectors read-only, persistência SQLite, API overview/scorecard, worker periódico, UI grid estilo Cloudflare. Primeiro tenant: VM112; domínios auto-descobertos via eventos onboarding.

Technical Context

Language/Version: Python 3.11+ (API + worker VM122); JS vanilla (frontend)

Primary Dependencies: FastAPI, httpx, dnspython (ou subprocess dig), sqlite3, redis (worker queue existente)

Storage: SQLite novas tabelas audit_domains, audit_checks

Testing: scripts/verify-audit-overview.sh; mock tenant offline

Target Platform: VM122 worker → VM112 API :8090 + DNS público + HTTPS webmail

Performance Goals: Ciclo completo 1 domínio < 30s; overview API < 500ms

Constraints: Read-only; LAN para VM112; sem novos containers (worker existente)

Constitution Check

Princípio Status
IV. Mail vs Ops PASS — collectors read-only, Ops separado
VII. Spec-Driven PASS
IX. YAGNI PASS — SQLite, 8 checks fixos

Project Structure

specs/009-ops-audit-overview/
├── spec.md
├── plan.md
├── research.md
├── contracts/audit-api.md
├── checklists/requirements.md
└── tasks.md

api/app/
├── main.py                    # routes /audit/*
├── collectors/
│   ├── __init__.py
│   ├── base.py
│   ├── vm112.py               # carbonio, nginx, cert via portal API
│   ├── dns.py                 # mx, spf, dkim, dmarc
│   └── webmail.py             # HTTP check
└── audit_store.py             # SQLite CRUD

worker/
└── audit_runner.py            # loop ou job redis

frontend/assets/
├── app.js                     # view overview + scorecard drill-down
└── styles.css                 # cards health grid

Phase 0: Research

Ver research.md.

Phase 1: Data Model

CREATE TABLE audit_domains (
  id INTEGER PRIMARY KEY,
  tenant_id INTEGER NOT NULL,
  domain TEXT NOT NULL,
  source TEXT NOT NULL DEFAULT 'onboarding',
  created_at TEXT NOT NULL,
  UNIQUE(tenant_id, domain)
);

CREATE TABLE audit_checks (
  id INTEGER PRIMARY KEY,
  tenant_id INTEGER NOT NULL,
  domain TEXT NOT NULL,
  check_id TEXT NOT NULL,
  status TEXT NOT NULL,
  message TEXT,
  evidence TEXT,
  checked_at TEXT NOT NULL,
  UNIQUE(tenant_id, domain, check_id)
);

Phase 2: Collectors

Module Checks
vm112.py carbonio, nginx_vhost, cert_le
dns.py dns_mx, dns_spf, dns_dkim, dns_dmarc
webmail.py webmail_http

Runner: run_audit(tenant_id, domain) -> dict[check_id, result]

Phase 3: API

  • GET /api/v1/audit/overview
  • GET /api/v1/audit/tenants/{id}/scorecard?domain=
  • POST /api/v1/audit/run/{tenant_id}?domain= (manual trigger, ops use)

Phase 4: Worker

  • Env AUDIT_INTERVAL_SEC=600
  • A cada ciclo: list domains → run_audit → upsert audit_checks
  • Auto-register domains from webhook_events where event in (account.created, onboarding.completed)

Phase 5: UI

  • Nova tab Overview ou substituir Infra básica
  • Grid cards: tenant name, score X/8, status badge, last audit
  • Click → scorecard modal/panel com 8 rows

Implementation Phases (time estimate)

Phase Tasks ~Time
A Schema + store T001-T003 1h
B Collectors T004-T008 3h
C API T009-T011 1.5h
D Worker T012-T014 1.5h
E UI T015-T018 2h
F Test + deploy T019-T021 1h

Total ~10h — pode paralelizar com 004 após API base pronta.

Risk & Mitigation

Risco Mitigação
Portal API lenta Timeout 10s por check; partial results
DNS rate limit Cache 10 min; sequential checks
Falso negativo DKIM evidence field com TXT encontrado
Worker sobrecarga 1 tenant MVP; queue single-thread

Sequencing with 004

  • Paralelo possível: equipas diferentes (004 portal+funil, 009 worker+UI)
  • Dependência soft: 009 domain auto-discovery beneficia de 004 onboarding.completed mas funciona com account.created existente
  • Recomendação: implementar 004 Phase A primeiro; 009 Phase A-B em paralelo; UI 004+009 na mesma sprint UI