ligbox-ops-platform/specs/001-webhook-vm112-integration/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

151 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Implementation Plan: Webhook VM112 → Ops Platform
**Branch**: `001-webhook-vm112-integration` | **Date**: 2026-06-08 | **Spec**: [spec.md](./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**: ~1050 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)
```text
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)
```text
# 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](./research.md) — conclusões:
1. Endpoint receptor MVP já funcional (`POST /api/v1/webhooks/onboard`)
2. `session_id` disponível via header `X-Onboarding-Session` / `request.state`
3. Portal não tem cliente webhook — criar `ops_webhook.py`
4. Idempotência: lookup `event+session_id+domain` antes de INSERT ticket
5. Secret dev `ligbox-ops-dev-secret` — rotacionar em produção
## Phase 1: Design Artifacts
| Artefacto | Ficheiro | Conteúdo |
|-----------|----------|----------|
| Data model | [data-model.md](./data-model.md) | Payload, entidades, dedup key |
| API contract | [contracts/webhook-onboard.md](./contracts/webhook-onboard.md) | Request/response, eventos |
| Quickstart | [quickstart.md](./quickstart.md) | Testes manuais e deploy |
## Implementation Phases
### Phase A — Ops receptor (VM122) — ~1h
1. Adicionar verificação idempotente em `webhook_onboard`
2. Query `webhook_events` por `(event_type, session_id, domain)` antes de criar ticket
3. Melhorar subject do ticket: `[account.created] dominio.com — admin@dominio.com`
4. Log estruturado em falha 401
### Phase B — Portal emissor (VM112) — ~2h
1. `config.py`: `ops_webhook_url`, `ops_webhook_secret`, `ops_webhook_enabled` (default true)
2. `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.warn` em falha, nunca raise para o router
3. `onboarding.py``create_account`: após sucesso, chamar:
```python
ops_webhook.emit_event("account.created", domain, session_id, {...})
```
### Phase C — Config + validação — ~30min
1. `.env` VM112: `OPS_WEBHOOK_URL=http://10.10.10.122:8080/api/v1/webhooks/onboard`
2. `.env` VM122: confirmar `WEBHOOK_SECRET` igual
3. Script `scripts/verify-webhook.sh` no repo ops
4. Teste E2E: criar conta teste → ticket no desk
### Phase D — Eventos P3 (opcional, pós-MVP)
- `domain.validated` em `/validate-domain`
- `dns.applied` em `/cloudflare/apply`
- `onboarding.completed` / `onboarding.failed` nos 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.