6.2 KiB
Implementation Plan: Webhook VM112 → Ops Platform
Branch: 001-webhook-vm112-integration | Date: 2026-06-08 | Spec: spec.md
Input: Feature specification from /specs/001-webhook-vm112-integration/spec.md
Summary
Implementar integração LAN entre o portal de onboarding VM112 (ibytera-mail-portal) e a API Ops VM122 (ligbox-ops-platform). O portal emite webhooks autenticados após marcos do onboarding; o Ops regista eventos, cria tickets automaticamente e garante idempotência. Falhas de webhook são não-bloqueantes para o cliente.
Abordagem: módulo ops_webhook no portal (httpx + retry), extensão mínima do receptor existente em VM122 (idempotência + índice), secret partilhado via .env em ambas as VMs.
Technical Context
Language/Version: Python 3.11+ (portal VM112 Ubuntu 24.04; Ops VM122 Debian 12)
Primary Dependencies: FastAPI, httpx, pydantic-settings (portal); FastAPI, httpx, redis, sqlite3 (Ops — já deployados)
Storage: SQLite ops.db (VM122) — tabelas webhook_events, tickets existentes; sem alteração de schema obrigatória (índice lógico para idempotência)
Testing: curl manual + script scripts/verify-webhook.sh; teste portal com Ops offline
Target Platform: VM112 10.10.10.112:8090 → VM122 10.10.10.122:8080 (LAN only)
Project Type: Integração cross-VM (dois repositórios/deploy paths)
Performance Goals: Webhook entrega < 5s p95; não adicionar > 500ms ao tempo de resposta do portal
Constraints: LAN-only; secret em header X-Webhook-Secret; fail2ban inalterado; onboarding nunca bloqueado por falha Ops
Scale/Scope: ~10–50 onboardings/dia; 4 tipos de evento MVP + 4 fase 2
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
| Princípio | Status | Notas |
|---|---|---|
| I. vmbr1 / LAN | ✅ PASS | Tráfego 112→122 na LAN 10.10.10.0/24 via vmbr4000 |
| II. Interfaces Proxmox | ✅ PASS | Nenhuma alteração de rede |
| III. Anti-scan Hetzner | ✅ PASS | Sem novas regras NAT/iptables |
| IV. Mail vs Ops separation | ✅ PASS | Portal emite; Ops recebe — sem mail stack em 122 |
| V. fail2ban | ✅ PASS | Sem alteração SSH |
| VI. pfSense API | N/A | Não usado nesta feature |
| VII. Spec-Driven | ✅ PASS | spec → plan em curso |
| VIII. Documentation | ✅ PASS | Artefactos em specs/001-* |
| IX. YAGNI | ✅ PASS | Sem novos serviços; extensão mínima |
Post-design re-check: ✅ Nenhuma violação. Sem Complexity Tracking necessário.
Project Structure
Documentation (this feature)
specs/001-webhook-vm112-integration/
├── spec.md
├── plan.md # este ficheiro
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── webhook-onboard.md
├── checklists/
│ └── requirements.md
└── tasks.md # gerado por /speckit-tasks
Source Code (deploy targets)
# VM112 — /opt/ibytera-mail-portal/ (sync desde obsidian-infra/carbonio/ibytera-mail-portal/)
backend/app/
├── config.py # + ops_webhook_url, ops_webhook_secret, ops_webhook_enabled
├── services/
│ └── ops_webhook.py # NOVO: emit_event(), retry logic
└── routers/
└── onboarding.py # chamar ops_webhook após account/create
# VM122 — /opt/ligbox-ops-platform/ (já deployado)
api/app/
└── main.py # + idempotência, índice dedup, log melhorado
Structure Decision: alterações mínimas em dois deploy paths existentes; código fonte versionado em obsidian-infra (portal) e workspace/projects/ligbox-ops-platform (ops).
Phase 0: Research Summary
Ver research.md — conclusões:
- Endpoint receptor MVP já funcional (
POST /api/v1/webhooks/onboard) session_iddisponível via headerX-Onboarding-Session/request.state- Portal não tem cliente webhook — criar
ops_webhook.py - Idempotência: lookup
event+session_id+domainantes de INSERT ticket - Secret dev
ligbox-ops-dev-secret— rotacionar em produção
Phase 1: Design Artifacts
| Artefacto | Ficheiro | Conteúdo |
|---|---|---|
| Data model | data-model.md | Payload, entidades, dedup key |
| API contract | contracts/webhook-onboard.md | Request/response, eventos |
| Quickstart | quickstart.md | Testes manuais e deploy |
Implementation Phases
Phase A — Ops receptor (VM122) — ~1h
- Adicionar verificação idempotente em
webhook_onboard - Query
webhook_eventspor(event_type, session_id, domain)antes de criar ticket - Melhorar subject do ticket:
[account.created] dominio.com — admin@dominio.com - Log estruturado em falha 401
Phase B — Portal emissor (VM112) — ~2h
config.py:ops_webhook_url,ops_webhook_secret,ops_webhook_enabled(default true)services/ops_webhook.py:emit_event(event, domain, session_id, data, timeout=5)- Retry 3x: 1s, 3s, 9s backoff
- Header
X-Webhook-Secret activity_log.warnem falha, nunca raise para o router
onboarding.py→create_account: após sucesso, chamar:ops_webhook.emit_event("account.created", domain, session_id, {...})
Phase C — Config + validação — ~30min
.envVM112:OPS_WEBHOOK_URL=http://10.10.10.122:8080/api/v1/webhooks/onboard.envVM122: confirmarWEBHOOK_SECRETigual- Script
scripts/verify-webhook.shno repo ops - Teste E2E: criar conta teste → ticket no desk
Phase D — Eventos P3 (opcional, pós-MVP)
domain.validatedem/validate-domaindns.appliedem/cloudflare/applyonboarding.completed/onboarding.failednos respectivos pontos
Risk & Mitigation
| Risco | Mitigação |
|---|---|
| Ops offline durante onboarding | Retry + non-blocking; email admin continua |
| Secret exposto em log | Nunca logar secret; só "auth failed" |
| Tickets duplicados | Idempotência no receptor |
| Latência no portal | Fire-and-forget async (BackgroundTasks FastAPI) |
Complexity Tracking
Nenhuma violação da constitution — tabela vazia.