ligbox-ops-platform/specs/037-dns-multi-cloudflare-orchestration/spec.md
Ligbox Spec Hub 1f340ef924 docs(dns): sincronizar Spec 037/035 com deploy V1–V3 e V4 pendente
Actualiza dns-viewer, exec/rollback, fichas VM112/122/123, API Console
proxy, token interno wizard e verify script para evitar drift documental.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 16:42:07 +00:00

180 lines
7.4 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.

# Spec 037 — DNS multi-conta Cloudflare + BYO + Registrador
**Criado:** 2026-06-22
**Solicitado por:** Roger
**Status:** 🔄 Em implementação (Fase A activa · B/C/D em construção)
**Prioridade:** P1
**VM:** 112 (wizard API `:8090`)
**Relacionado:** [004 cloudflare-zone-provision](../004-onboard-funnel-events/cloudflare-zone-provision.md) · 025 · 017 · **[dns-viewer.md](./dns-viewer.md)** · [035 §5.5](../035-ligbox-mail-bundles-foss-openpanel/domain-manager-console-ui.md)
---
## Resumo
O passo DNS do onboarding usa **três contas Cloudflare gerenciadas pela Ligbox** (ligit, itecnologys, ibytera). O wizard:
1. **Se a zona já existe** numa das 3 contas → aplica apontamentos com o token dessa conta.
2. **Se o cliente escolhe «Cloudflare Ligbox»** e a zona **ainda não existe** → o wizard **cria a zona do cliente** na conta Ligbox correcta (`provision-zone`), depois aplica MX/SPF/etc.
3. **Se o cliente não quer DNS na Ligbox** → BYO (token CF dele) ou registrador externo.
**Política:** não importar domínios alheios nem criar zonas fora do fluxo wizard; **sim** criar a zona **do cliente** na CF gerenciada pela Ligbox quando esse for o caminho escolhido.
**Evolução (Roger 2026-06-22):** cada cliente novo terá **conta Cloudflare dedicada** — e-mail inicial `proj_{id}@ligbox.com.br`, dados do domínio do cliente; após go-live, convidar gestor `admin@dominio`. Ver [client-cf-account-lifecycle.md](./client-cf-account-lifecycle.md).
**Identidade projeto (Roger 2026-06-22):** o agente **cria** `proj_{id}@ligbox.com.br` no Carbonio, **entrega senha** ao cliente e referencia em CF, DNS, registry e webhooks. Ver [project-email-identity.md](./project-email-identity.md).
---
## Três contas Ligbox (pré-cadastro)
| ID interno | E-mail admin (referência) | Ficheiro token (VM112) | `account_id` CF |
|------------|---------------------------|-------------------------|-----------------|
| `ligit` | admin@ligit.com.br | `secrets/cloudflare-ligit.token` | _configurar_ |
| `itecnologys` | admin@itecnologys.com | `secrets/cloudflare-itecnologys.token` | _configurar_ |
| `ibytera` | ibytera@gmail.com | `secrets/cloudflare-ibytera.token` | _configurar_ |
Catálogo: `deploy/vm112-wizard/dns-accounts.yaml` (não commitar tokens).
Conta **legacy** `cloudflare.token` + `cloudflare_account_id` em `config.py` → migrar para `ibytera` ou deprecar após cutover.
---
## Fluxo decisão (passo DNS)
```mermaid
flowchart TD
A[POST validate-domain] --> B[GET /dns/resolve/{domain}]
B --> C{Zona já existe em conta Ligbox?}
C -->|sim| D[apply na conta encontrada]
C -->|não| E{Cliente escolhe caminho}
E -->|Cloudflare Ligbox| F[provision-zone: cria zona do cliente na conta default]
F --> G[apply apontamentos]
E -->|BYO| H[connect-custom + apply]
E -->|Registrador| I[instructions + verify manual]
D --> V[GET dns/verify]
G --> V
H --> V
I --> V
V --> J[account/create]
```
### Regra «zona do cliente na CF Ligbox»
- **Existente:** probe `ligit → itecnologys → ibytera`; usa a conta onde a zona já está.
- **Nova (wizard):** `POST provision-zone` cria `cliente.com.br` na conta `default_provision_account` (yaml, hoje `ibytera`) — **só** quando o utilizador escolhe Cloudflare Ligbox.
- **Nunca:** criar zona sem escolha explícita Ligbox; nunca BYO/registrador criar em conta Ligbox.
---
## Endpoints novos / alterados
| Método | Path | Descrição |
|--------|------|-----------|
| GET | `/api/onboarding/dns/resolve/{domain}` | Procura zona nas 3 contas; devolve `matched_account`, `zone_id`, `paths_available` |
| POST | `/api/onboarding/dns/cloudflare/connect-custom` | Valida token BYO + zona; guarda em vault sessão |
| POST | `/api/onboarding/dns/cloudflare/apply` | **Alterado:** usa conta resolvida, BYO, ou body `account_id` |
| POST | `/api/onboarding/dns/cloudflare/provision-zone` | **Cria zona do cliente** na CF Ligbox (conta default ou existente) — só caminho Ligbox |
### Response `dns/resolve`
```json
{
"domain": "cliente.com.br",
"matched": true,
"account_id": "ligit",
"account_label": "Ligbox Ligit (admin@ligit.com.br)",
"zone_id": "…",
"zone_status": "active",
"dns_mode": "ligbox_cf_ligit",
"paths_available": ["apply_ligbox", "byo", "external"]
}
```
Se `matched: false` (zona nova):
```json
{
"matched": false,
"can_provision_ligbox": true,
"provision_account_id": "ibytera",
"paths_available": ["provision_ligbox", "byo", "external"],
"message": "Domínio novo — pode criar zona na Cloudflare gerenciada pela Ligbox ou usar BYO/registrador."
}
```
---
## BYO Cloudflare (cliente)
1. Cliente cola **API Token** (scope: DNS Edit + Zone Read **só na zona**).
2. `verify_token()` + `get_zone_by_name(domain)`.
3. Token em vault (`/var/lib/.../dns_tokens/{session_hash}`), TTL 7 dias pós-onboard.
4. `apply` com `CloudflareDNS(token=customer_token)`.
5. Purge Spec 017 **não** apaga zona BYO — só registos criados por nós (futuro: tracking).
---
## Registrador / DNS externo
Sem mudança funcional core: `GET /dns/instructions/{domain}` + verificação `dns_verify`.
Fase 2: adapters EPP/API (Gandi, GoDaddy, Registro.br homologado).
---
## DNS Viewer (read-only) — Spec 037-DNS-VIEWER
Painel unificado para **exibir** apontamentos (staff Desk + gerente Console) **sem editar** na UI.
| Caminho wizard | O viewer mostra |
|----------------|-----------------|
| **Cloudflare Ligbox** | Registos **planeados** (pré-NS) ou **aplicados** (pós-apply) + NS CF |
| **BYO / Registrador / externo** | DNS **actual** (público + instructions) — **não** preview Ligbox |
Documento completo: **[dns-viewer.md](./dns-viewer.md)** — endpoints, links editar, fases V0V4.
**Estado deploy (2026-06-25):** V1V3 produção (VM122/123) · commit `038fb8f` · V4 wizard patches prontos · deploy VM112 pendente.
---
## Código
| Caminho monorepo | Deploy VM112 |
|------------------|--------------|
| `projects/wizard/backend/app/services/dns_account_registry.py` | `/opt/ligbox-wizard/backend/app/services/` |
| `deploy/vm112-wizard/dns-accounts.yaml` | `/opt/ligbox-wizard/dns-accounts.yaml` |
| `deploy/vm112-wizard/dns-accounts.yaml.example` | exemplo versionado |
---
## Critérios de aceitação (Fase 1)
1. Domínio existente na conta `ibytera``resolve` retorna `matched` + `apply` OK.
2. Domínio **novo** + caminho Ligbox → `provision-zone` cria zona na conta default + `apply` OK.
3. BYO com token válido → `apply` na conta do cliente.
4. Caminho registrador → sem `provision-zone` Ligbox.
5. Tokens em `secrets/` — nunca no Git.
---
## Decisões Roger (2026-06-22, corrigido)
| # | Regra |
|---|--------|
| 1 | Não importar domínios alheios nas contas Ligbox |
| 2 | **Sim** criar zona **do cliente** na CF gerenciada Ligbox via wizard (escolha explícita) |
| 3 | Conta para zona nova: `default_provision_account` no yaml (hoje `ibytera`) |
| 4 | BYO / registrador = DNS fora das contas Ligbox |
---
## Fase 2 — Conta CF dedicada por cliente
Ver [client-cf-account-lifecycle.md](./client-cf-account-lifecycle.md):
| Fase | E-mail Cloudflare | Quando |
|------|-------------------|--------|
| A — Onboarding | `proj_{id}@ligbox.com.br` | Criação conta + zona |
| B — Handoff | `admin@{dominio_cliente}` convidado como gestor | DNS + email + infra OK |
**Orquestração agentica (edge):** [cf-agents-sdk-architecture.md](./cf-agents-sdk-architecture.md) — Cloudflare Agents SDK (Durable Objects, multi-agent, human-in-the-loop) + roster Spec 029 A0A7.