# Feature Specification: Orquestração de Serviços — Cliente & Catálogo (018)

**Criado:** 2026-06-16  
**Actualizado:** 2026-06-25 (performance + Escopo OPS clicável + camadas tiles)  
**Solicitado por:** Roger  
**Status:** Fase 1 concluída (Desk VM122)  
**Wizard cliente:** inalterado na VM112 — só e-mail (`vm112-mail`)  
**Prioridade:** P1  
**Sistema:** Desk VM122 (+ proxies VM112, futuro multi-wizard)  
**Módulo Desk:** `overview-home` (nav **Serviços**)  
**Depende de:** Spec 015 (módulos), Spec 017 (purge domínio VM112)

---

## Resumo

A página **Serviços** substitui a visão estreita «Contas / lista de domínios» por um painel operacional estilo **cPanel/WHM**: o técnico sénior selecciona um **cliente** e vê **tiles de serviços** contratados ou disponíveis (e-mail tenant, servidor dedicado, firewall, cloud, Wazuh, site).

**Fase 1 (esta entrega):** UI e modelo conceptual no frontend; clientes derivados dos domínios VM112; apenas **E-mail Tenant** activo com purge Spec 017 intacto.  
**Fase 2:** API Desk `clients` + `service_instances` em SQLite.  
**Fase 3:** Registry de wizards por `service_catalog.code` e purge por instância.

### Performance — Serviços IaaS (2026-06-25)

**Cadeia:** Desk `servicos.js` → VM122 `GET /api/v1/vm112/domains` → VM112 `GET /api/admin/domains` → **`zmprov gad`** (~5s sem cache).

| Camada | Problema | Mitigação | Deploy |
|--------|----------|-----------|--------|
| **VM112** | `zmprov gad` a cada pedido | Cache 90s `gad_all_domains` + lista orquestrada 60s; invalidação em `cd`/`dd`/purge | `deploy/vm112-wizard/perf-domains-list-20260625/` ✅ 2026-06-25 |
| **VM122 API** | Proxy repetia VM112 lenta | Cache 60s `VM112_DOMAINS_CACHE_TTL` | `vm112_domains.py` ✅ |
| **Desk frontend** | Ecrã preso «A carregar…» | `sessionStorage` stale-while-revalidate + dedup fetch | `servicos.js` ✅ |
| **Poll** | 30s sobrecarregava VM112 | Poll **90s** em `overview-home` | `app.js` ✅ |
| **Sessão** | API restart expulsava user | Rede ≠ logout (`ensureValidSession`) | `auth.js` ✅ |

**Resultados medidos (2026-06-25):**

| Pedido | Antes | Depois |
|--------|-------|--------|
| VM112 `/api/admin/domains` (2.º hit) | ~5–11s | **~0,03s** |
| VM112 `/api/admin/domains/{domain}` (2.º hit) | ~15–20s | **~0,04s** |
| VM112 detalhe (1.º hit, cold) | ~20s serial | **~16s** paralelo (infra+gaa+CF) |
| Desk `/api/v1/vm112/domains` (cache quente) | ~16s | **~0,08s** |
| Desk `/api/v1/vm112/domains/{domain}` (cache quente) | ~15s+ | **~3s** (1.º após lista) / **~0,1s** (2.º) |
| 1.º hit pós-restart wizard | — | ~6s lista / ~16s modal purge |

### Modal Purge / «Gerir» (2026-06-25)

**Endpoint:** `GET /api/v1/vm112/domains/{domain}` → VM112 `get_domain_detail()`.

| Gargalo | Tempo | Mitigação |
|---------|-------|-----------|
| `get_status()` — SSH CT114, DNS, HTTPS | ~9s | Cache `infra_status:{domain}` 90s |
| `zmprov gaa` (contas) | ~3s | Cache `accounts_list:{domain}` 45s |
| Cloudflare API | ~1–2s | Paralelo + cache `domain_detail:{domain}` 120s |
| UI ecrã branco | UX | **Skeleton** imediato com dados da lista clientes |

**Frontend:** `servicos.js` — modal abre com dados do cliente seleccionado; purge form activa após load.

**Loading UX (2026-06-25):** barra indeterminada, etapas VM112 rotativas, botão spinner — ver Spec **017** § UI Serviços loading.

**Desk API:** cache proxy detalhe 45s (`VM112_DOMAIN_DETAIL_TTL`).

### Escopo OPS — cards clicáveis (2026-06-25)

Coluna direita: cada escopo é **card botão** com **camada**, **Spec** e **destino**.

| ID | Label | Camada | Spec | Destino ao clicar |
|----|-------|--------|------|-------------------|
| `carbonio` | Carbonio | Aplicação MOSP | 017 | Modal Serviços (detalhe/purge) |
| `site` | ligbox-sites | Tenant utilizador | 017 | Modal Serviços |
| `portal` | Portal users | Tenant utilizador | 028 | Console › `/admin/dominio?domain=` |
| `cloudflare` | Cloudflare | Infraestrutura | 037 | Console › DNS Viewer |
| `traefik` | Traefik / SNI | Infraestrutura | 026 | Desk › Infra CODE |
| `desk` | Desk OPS | Aplicação MOSP | 017 | Desk › Eventos › tab Purges |

**Blocklist UI:** clientes `ligbox.com.br` / `itecnologys.com` — badge 🔒 na lista; banner no painel Escopo; purge modal desactivado (`purge_blocked` API).

**Tiles centro:** badge camada (`Tenant utilizador` · `Aplicação MOSP` · `Infraestrutura`) + badge comercial (`SaaS` / `IaaS`).

**Navegação:** `window.DeskNavigate.go('events', { eventsTab: 'purges' })` — helper em `app.js`.

**Cache bust frontend (VM122):** `servicos.js`, `styles.css`, `app.js` → `?v=20260625align1`.

---

```json
{
  "domains": [ "..." ],
  "cached": true,
  "cache_age_sec": 12
}
```

**Regra deploy Roger:**

1. VM112: `systemctl restart ligbox-wizard` invalida cache wizard (~6s no 1.º Serviços IaaS).
2. VM122: preferir `docker cp` sem restart; se restart API, Desk mostra cache browser.
3. Purge/create domain invalida cache VM112 automaticamente.

Ver também Spec **017** § cache listagem domínios.

---

## Problema

| Hoje | Necessidade |
|------|-------------|
| Lista plana de domínios | Vista por **cliente** |
| Nome «Contas» ambíguo | **Serviços** — escala para novos produtos |
| Purge acoplado à lista | Purge no tile **E-mail Tenant** (Spec 017) |
| Um wizard (mail) | Vários wizards futuros (firewall, cloud, Wazuh) |

---

## Nomenclatura

| Camada | Valor |
|--------|--------|
| ID módulo / view | `overview-home` (sem breaking change) |
| Menu lateral | **Serviços** |
| Título página | **Orquestração de Serviços** |
| Subtítulo | *Clientes Ligbox — serviços activos, estado OPS e acções* |
| JS global | `DeskServices` (alias `DeskAccounts` para compat.) |

---

**Fase 1 (esta entrega):** UI e modelo conceptual no frontend; clientes derivados dos domínios VM112; apenas **E-mail Tenant** activo com purge Spec 017 intacto.  
**Fase 2:** API Desk `clients` + `service_instances` em SQLite.  
**Fase 3:** Registry de wizards por `service_catalog.code` e purge por instância.  
**Fase 4:** Catálogo comercial completo — níveis «Pizza as a Service» + Managed Open Source.

**Posicionamento Ligbox (MSP):**

> *«Pegamos soluções open source e entregamos como serviço gerenciado — em cloud privada Ligbox, VPS dedicado ou infraestrutura local do cliente.»*

---

## Visão de oferta — «Pizza as a Service» (Roger, 2026)

A analogia **Pizza as a Service** descreve **quem gere o quê** entre cliente e provedor. Quanto mais camadas a Ligbox opera, mais «as a service» o produto é — e mais valor (e SLA) o cliente compra.

### Legenda de responsabilidade (camadas da «pizza»)

| Camada (de baixo para cima) | Equivalente técnico Ligbox |
|-----------------------------|----------------------------|
| Eletricidade / Gás | Datacenter, energia, link, Hetzner/host |
| Fogão | Hypervisor — Proxmox VE, VMs, CTs |
| Fogo | SO, rede, firewall base, hardening |
| Pizza (massa/base) | Runtime — Docker, Nginx, Traefik, DB engine |
| Toppings | Aplicação open source — Carbonio, Nextcloud, ERPNext |
| Bebidas | Integrações — DNS, SSL, backup, monitoramento |
| Conversas | Uso pelo cliente — utilizadores finais, dados de negócio |

**Azul (cliente gere)** · **Laranja (Ligbox gere)**

---

### Nível 1 — Tradicional → Consultoria / Suporte sob demanda

*Equivalente: «Feito em casa» — cliente gere tudo; Ligbox ajuda quando chamada.*

| Gerido pelo **cliente** | Oferecido pela **Ligbox** |
|-------------------------|---------------------------|
| Servidores físicos / on-prem | Consultoria Linux |
| Rede | Troubleshooting |
| Sistema operacional | Instalação inicial |
| Banco de dados | Treinamento técnico |
| Backup | Auditoria de segurança |
| Aplicação | Documentação |
| Segurança operacional | — |

| Campo catálogo | Valor |
|----------------|-------|
| `delivery_model` | `traditional` |
| `code` (ex.) | `consulting_hour`, `audit_security`, `linux_training` |
| Stack típica | Ubuntu Server, Debian, Proxmox VE (no lado do cliente) |
| Modelo comercial | Hora técnica · pacote suporte mensal básico |
| Wizard Desk | Não — ticket + assist takeover (Spec 010) |
| Tile UI | «Suporte» — sem instância provisionada |

---

### Nível 2 — IaaS → Infraestrutura gerenciada

*Equivalente: «Leve e Asse» — Ligbox entrega infra pronta; cliente cuida da aplicação.*

| Gerido pela **Ligbox** | Gerido pelo **cliente** |
|------------------------|-------------------------|
| VPS / Cloud | Aplicação |
| Virtualização (Proxmox) | Dados |
| Firewall (pfSense) | Utilizadores da app |
| Backup do servidor | — |
| Monitoramento 24/7 | — |
| SO + hardening | — |

| Campo catálogo | Valor |
|----------------|-------|
| `delivery_model` | `iaas` |
| `code` (ex.) | `managed_vps`, `managed_backup`, `vpn_corporate`, `firewall`, `monitoring_host` |
| Stack Ligbox | Proxmox VE, Docker, Nginx, pfSense, Grafana, Prometheus |
| Modelo comercial | Mensal fixo — ex. *«Servidor Linux totalmente gerenciado»* |
| Wizard Desk | `wizard-iaas-vps` (futuro) — VM, IP, backup job |
| Tile UI | Firewall, Cloud/VPS, Monitoring host — badge **IaaS** |

**Ligbox hoje (parcial):** regras Proxmox, pfSense WAN, VM112 como nó — encaixa neste nível para a camada «fogão+fogo».

---

### Nível 3 — PaaS → Plataforma gerenciada

*Equivalente: «Delivery» — ambiente pronto para deploy; cliente traz código/dados.*

| Gerido pela **Ligbox** | Gerido pelo **cliente** |
|------------------------|-------------------------|
| Infraestrutura (IaaS) | Código da aplicação |
| Banco de dados gerido | Dados de negócio |
| Deploy / CI/CD | — |
| Backup + SSL | — |
| Escalabilidade | — |

| Campo catálogo | Valor |
|----------------|-------|
| `delivery_model` | `paas` |
| `code` (ex.) | `k8s_managed`, `postgres_managed`, `cicd_pipeline`, `api_hosting` |
| Stack Ligbox | Kubernetes, PostgreSQL, Redis, GitLab, Traefik |
| Modelo comercial | Mensal por ambiente / por pipeline |
| Wizard Desk | `wizard-paas-k8s`, `wizard-paas-db` (futuro) |
| Tile UI | DevOps / CI/CD — badge **PaaS** |

---

### Nível 4 — SaaS → Solução completa gerenciada

*Equivalente: «Restaurante» — cliente só utiliza.*

| Gerido pela **Ligbox** | Gerido pelo **cliente** |
|------------------------|-------------------------|
| Tudo (infra → app → users ops) | Apenas **uso** — login, conteúdo, processos de negócio |
| Actualizações, segurança, backup | — |
| Monitoramento, suporte SLA | — |

| Campo catálogo | Valor |
|----------------|-------|
| `delivery_model` | `saas` |
| `code` (ex.) | `email_tenant`, `erpnext`, `suitecrm`, `nextcloud`, `wiki_js`, `bitwarden`, `zammad` |
| Modelo comercial | Por utilizador/mês · mensal por domínio · tier SLA |
| Wizard Desk | `vm112-mail` (e-mail) · wizards por produto (futuro) |
| Tile UI | E-mail Tenant (activo Fase 1) — badge **SaaS** |

**Ligbox hoje:** **E-mail Tenant** (Carbonio + portal + DNS + Traefik) = **SaaS / Managed Open Source** — produto flagship.

---

### Nível 5 — Managed Open Source Services (MOSP) — modelo ideal MSP

Camada comercial que a Ligbox deve priorizar: **software open source operado pela Ligbox; cliente só consome.**

| Serviço | Tecnologia | `catalog.code` | Cobrança sugerida | `delivery_model` |
|---------|------------|----------------|-------------------|------------------|
| E-mail corporativo (tenant) | Carbonio | `email_tenant` | mensal / domínio | `saas` |
| E-mail dedicado | Mailcow / VM dedicada | `mail_dedicated` | mensal / servidor | `saas` |
| Cloud Storage | Nextcloud | `nextcloud` | por utilizador | `saas` |
| ERP | ERPNext | `erpnext` | por utilizador | `saas` |
| CRM | SuiteCRM | `suitecrm` | por utilizador | `saas` |
| Wiki corporativa | Wiki.js | `wiki_js` | mensal | `saas` |
| Password Manager | Bitwarden | `bitwarden` | por utilizador | `saas` |
| Helpdesk | Zammad | `zammad` | mensal | `saas` |
| Chat corporativo | Mattermost | `mattermost` | mensal | `saas` |
| Git privado | Gitea | `gitea` | por utilizador | `saas` |
| VPN empresarial | WireGuard | `vpn_corporate` | por empresa | `iaas` |
| Monitoramento | Zabbix / Wazuh | `wazuh_domain`, `monitoring_host` | mensal | `iaas` / `saas` |
| Backup | Restic + MinIO | `backup_baas` | por GB | `iaas` |
| Firewall | pfSense | `firewall` | mensal | `iaas` |
| Site / CMS | ligbox-sites | `site_cms` | mensal | `saas` |

**Regra de produto:** cada linha do catálogo tem `delivery_model`, `managed_layers[]` (quais camadas da pizza a Ligbox opera) e `wizard_id` quando provisionável.

---

## Portfólio Ligbox — mapa completo (futuro)

### Infraestrutura

| Produto | Nível | `code` | Estado Desk |
|---------|-------|--------|-------------|
| Linux Managed Server | IaaS | `managed_vps` | Planeado |
| VPS Management | IaaS | `cloud` | Tile «Em breve» |
| Backup as a Service | IaaS | `backup_baas` | Planeado |
| Monitoring as a Service | IaaS/SaaS | `monitoring_host` | Parcial (Grafana/Infra) |

### Segurança

| Produto | Nível | `code` | Estado Desk |
|---------|-------|--------|-------------|
| Firewall as a Service | IaaS | `firewall` | Tile «Em breve» |
| VPN as a Service | IaaS | `vpn_corporate` | Planeado |
| Vulnerability Scanning | Tradicional | `vuln_scan` | Planeado |
| Wazuh SOC por domínio | SaaS | `wazuh_domain` | Tile «Em breve» + Infra 2 |

### Aplicações open source (MOSP)

| Produto | Nível | `code` | Estado Desk |
|---------|-------|--------|-------------|
| E-mail Tenant | SaaS | `email_tenant` | **Activo** (Spec 017 purge) |
| E-mail dedicado | SaaS | `mail_dedicated` | Tile «Em breve» |
| Nextcloud | SaaS | `nextcloud` | Planeado |
| ERP (ERPNext) | SaaS | `erpnext` | Planeado |
| CRM (SuiteCRM) | SaaS | `suitecrm` | Planeado |
| Site / CMS | SaaS | `site_cms` | Derivado VM112 |
| Wiki.js | SaaS | `wiki_js` | Planeado |
| Bitwarden | SaaS | `bitwarden` | Planeado |
| Zammad | SaaS | `zammad` | Planeado |
| Mattermost | SaaS | `mattermost` | Planeado |
| Gitea | SaaS | `gitea` | Planeado |

### DevOps

| Produto | Nível | `code` | Estado Desk |
|---------|-------|--------|-------------|
| Docker Hosting | PaaS | `docker_hosting` | Planeado |
| Kubernetes Hosting | PaaS | `k8s_managed` | Planeado |
| CI/CD Pipeline | PaaS | `cicd_pipeline` | Planeado |

### Suporte transversal

| Produto | Nível | `code` | Canal Desk |
|---------|-------|--------|------------|
| SLA empresarial | Overlay | `sla_enterprise` | Tickets + SLA fields |
| Monitoramento 24/7 | Overlay | `noc_24x7` | Infra + alertas |
| Administração remota | Tradicional | `remote_admin` | Assist takeover |
| Consultoria Linux | Tradicional | `consulting_hour` | Tickets |

---

## Modelo conceptual (actualizado)

```
Cliente (org)
  └── Instância de serviço (service_instance)
        ├── service_catalog.code       (email_tenant, firewall, nextcloud, …)
        ├── service_catalog.delivery_model (traditional | iaas | paas | saas)
        ├── managed_layers[]           (datacenter, hypervisor, os, runtime, app, ops)
        ├── status                     (planned | provisioning | active | degraded | suspended)
        ├── commercial_plan            (hourly | monthly_fixed | per_user | per_gb)
        ├── wizard_id                  (vm112-mail, wizard-iaas-vps, …)
        ├── sla_tier                   (basic | business | enterprise)
        └── bindings[]                 (domain, vm_id, zone_id, agent_id, k8s_ns)
```

### Matriz de responsabilidade por `delivery_model`

| Camada | traditional | iaas | paas | saas |
|--------|:-----------:|:----:|:----:|:----:|
| Datacenter / link | C | L | L | L |
| Hypervisor / VM | C | L | L | L |
| SO / rede / firewall | C | L | L | L |
| Runtime (Docker, proxy) | C | C | L | L |
| BD / deploy / SSL | C | C | L | L |
| Aplicação open source | C | C | C | L |
| Backup / monitoramento | C | L | L | L |
| Utilizadores finais / dados negócio | C | C | C | C |

*C = Cliente · L = Ligbox*

### Catálogo de serviços — MVP + roadmap MOSP

| code | Label UI | delivery_model | Wizard | Fase Desk |
|------|----------|----------------|--------|-----------|
| `email_tenant` | E-mail Tenant | saas | `vm112-mail` | **Activo** |
| `site_cms` | Site / CMS | saas | `vm112-mail` | Derivado VM112 |
| `mail_dedicated` | Servidor E-mail Dedicado | saas | TBD | Em breve |
| `firewall` | Firewall (pfSense) | iaas | `wizard-iaas-fw` | Em breve |
| `cloud` | Cloud / VPS gerenciado | iaas | `wizard-iaas-vps` | Em breve |
| `wazuh_domain` | Wazuh / SOC por domínio | saas | `wizard-soc-wazuh` | Em breve |
| `vpn_corporate` | VPN empresarial | iaas | TBD | Planeado |
| `backup_baas` | Backup as a Service | iaas | TBD | Planeado |
| `nextcloud` | Nextcloud | saas | TBD | Planeado |
| `erpnext` | ERP (ERPNext) | saas | TBD | Planeado |
| `monitoring_host` | Monitoramento 24/7 | iaas | TBD | Planeado |
| `consulting_hour` | Consultoria / suporte | traditional | — (ticket) | Planeado |

### Derivação Fase 1 — Cliente a partir do domínio VM112

Enquanto não existir tabela `clients`:

| Campo cliente | Origem |
|---------------|--------|
| `client_id` | `domain` (chave estável) |
| `display_name` | `domain` |
| `subtitle` | `portal_admin_email` ou «sem admin portal» |
| `health` | `ok` se `carbonio_exists`, senão `warn` |

Cada domínio VM112 = **1 cliente** com pelo menos uma instância `email_tenant`.

### Separação VM122 vs VM112 (Roger — clarificação)

| Onde | Papel |
|------|--------|
| **Desk VM122** (`/opt/ligbox-ops-platform`) | Orquestração MOSP — clientes, tenants de oferta, purge OPS, estado |
| **Portal VM112** (`/opt/ligbox-wizard`) | **Apenas** wizard e-mail/domínio — Hero e `/onboard` **não** recebem catálogo multi-produto |
| **Futuro** | Cada oferta MOSP → wizard próprio (pode provisionar Proxmox, servidor físico, etc.) |

A página Serviços no Desk é o **painel do técnico**; os wizards são **um por produto**, nunca um megamenu na Hero da 112.

---

## Reteste E2E — wizard e-mail/domínio (após purge)

### Pré-requisitos

1. Domínio de teste **ausente** em VM112 (lista Serviços vazia para esse domínio)
2. Desk: menu **Serviços** → purge Spec 017 se ainda existir lixo
3. Utilizador Desk: `super_admin` ou `ops_lead`

### Passos

| # | Acção | Verificação |
|---|--------|-------------|
| 1 | Desk → **Serviços** → seleccionar domínio teste | Tile **E-mail Tenant** activo ou cliente ausente |
| 2 | Se existir: tile E-mail → **Purge** (senha Root + confirmar domínio) | Domínio desaparece da lista |
| 3 | Portal `onboard.ligbox.com.br` ou `onboard.ibytera.com` | Self-Service → registo → `/onboard` |
| 4 | Wizard: domínio → DNS → conta → infra | Webhooks no Desk (Tickets/Eventos) |
| 5 | Desk → **Serviços** → Actualizar | Cliente reaparece; tenant E-mail **Activo** |
| 6 | Modal: infra steps verdes, contas Carbonio | Purge disponível para próximo ciclo |

### Domínios protegidos (sem purge)

`ligbox.com.br`, `itecnologys.com`


## UI — Layout 3 colunas

```
┌─────────────────────────────────────────────────────────────────┐
│ Orquestração de Serviços                    [Actualizar]        │
│ stats: clientes | e-mail activo | sites | logins portal         │
├──────────────┬────────────────────────────┬─────────────────────┤
│ CLIENTES     │ SERVIÇOS DO CLIENTE        │ ESCOPO OPS            │
│ [pesquisa]   │ (tiles cPanel)             │ (contexto serviço)    │
│ • domain A   │ [E-mail Tenant]  activo    │ Carbonio, CF, …       │
│ • domain B   │ [Site/CMS]       activo    │ nota purge            │
│              │ [Firewall]       em breve  │                       │
│              │ [Cloud]          em breve  │                       │
│              │ [Wazuh]          em breve  │                       │
└──────────────┴────────────────────────────┴─────────────────────┘
```

### Coluna Clientes

- Lista scrollável de todos os clientes (domínios VM112)
- Pesquisa: domínio, e-mail admin, login portal
- Badge saúde (verde/laranja)
- Clique selecciona cliente e actualiza tiles + escopo

### Coluna Serviços (centro)

- Grid de tiles por entrada do `SERVICE_CATALOG`
- Estados visuais: `active`, `inactive`, `planned`
- **Fase 2+:** badge `delivery_model` (IaaS / PaaS / SaaS / Suporte) e cor por nível
- **Fase 2+:** agrupamento por categoria — Infra · Segurança · Apps · DevOps · Suporte
- Tile **E-mail Tenant** activo → clique abre **modal Spec 017** (detalhe + purge)
- Tile **Site/CMS** → informativo (sem purge separado na Fase 1)
- Tiles `planned` → não clicáveis, label «Em breve» + tooltip com stack e modelo comercial

### Coluna Escopo OPS

- **Cards clicáveis** (2026-06-25) — camada (tenant / app / infra), Spec, destino OPS
- Lista dos escopos purge / operação quando serviço seleccionado
- **E-mail Tenant:** 6 escopos Spec 017 (Carbonio → Desk) — mapa na tabela § Escopo OPS cards
- **Blocklist:** banner + badge 🔒 para `ligbox.com.br`, `itecnologys.com`
- **Futuro:** escopo dinâmico por `service_catalog.purge_scopes_json`
- Indicador visual **quem gere** cada camada (matriz pizza — cliente vs Ligbox)
- Nota: purge requer senha Root no modal (serviços SaaS provisionados)
- Sem cliente seleccionado: texto de ajuda + link para portfólio (doc interna)

---

## Purge (sem regressão — Spec 017)

| Item | Mantido |
|------|---------|
| API | `POST /api/v1/vm112/domains/{domain}/purge` |
| Body | `confirm_domain`, `root_password` |
| RBAC | `super_admin`, `ops_lead` |
| Blocklist | `ligbox.com.br`, `itecnologys.com` |
| Escopos VM112 | Carbonio, site, portal, CF, Traefik, Desk |
| Modal | `#vm112-domain-modal` (index.html) |

O purge continua **por domínio** na Fase 1; na Fase 3 passa a `POST /api/v1/service-instances/{id}/purge` com escopo do catálogo.

---

## RBAC

Igual Spec 017 — `can_manage_vm112_domains()` → `super_admin`, `ops_lead`.

---

## API — Fase 1 (sem alteração)

Reutiliza endpoints Spec 017:

| Método | Path |
|--------|------|
| GET | `/api/v1/vm112/domains` |
| GET | `/api/v1/vm112/domains/{domain}` |
| POST | `/api/v1/vm112/domains/{domain}/purge` |

---

## API — Fase 2 (planeada)

| Método | Path | Descrição |
|--------|------|-----------|
| GET | `/api/v1/services/catalog` | Catálogo fixo + `delivery_model`, stack, wizard, preço ref |
| GET | `/api/v1/services/catalog/{code}` | Detalhe produto + matriz responsabilidade |
| GET | `/api/v1/clients?q=` | Lista clientes |
| GET | `/api/v1/clients/{id}` | Cliente + instâncias + bindings + SLA |
| POST | `/api/v1/service-instances` | Provisionar (trigger wizard por produto) |
| PATCH | `/api/v1/service-instances/{id}` | Suspender, reactivar, alterar plano |
| POST | `/api/v1/service-instances/{id}/purge` | Purge por instância (escopo do catálogo) |

### SQLite (Fase 2)

```sql
clients (
  id, name, tax_id, primary_email,
  hosting_mode,  -- ligbox_cloud | dedicated_vps | customer_onprem
  sla_tier, created_at
)

service_catalog (
  code, label, category,           -- infra | security | apps | devops | support
  delivery_model,                  -- traditional | iaas | paas | saas
  managed_layers_json,             -- ["hypervisor","os","app",…]
  technology_stack_json,           -- ["Carbonio","Traefik",…]
  wizard_id,
  commercial_model,                -- hourly | monthly_fixed | per_user | per_gb
  purge_scopes_json,
  default_enabled
)

service_instances (
  id, client_id, catalog_code, status,
  external_ref, meta_json,
  commercial_plan, monthly_value_cents,
  provisioned_at, expires_at
)

service_bindings (
  instance_id, resource_type, resource_id
  -- resource_type: domain | vm_id | zone_id | agent_id | k8s_namespace | ticket_id
)
```

### `hosting_mode` do cliente

| Valor | Significado | Pizza |
|-------|-------------|-------|
| `ligbox_cloud` | Hospedado na infra Ligbox (Proxmox/Hetzner) | Ligbox gere datacenter+fogão |
| `dedicated_vps` | VPS dedicado gerido pela Ligbox | IaaS+ |
| `customer_onprem` | Infra no cliente; Ligbox suporta/audita | Tradicional+ |

Um mesmo cliente pode misturar modos por instância de serviço (ex.: e-mail SaaS Ligbox + ERP on-prem com suporte tradicional).

---

## Ficheiros — Fase 1

| Ficheiro | Alteração |
|----------|-----------|
| `frontend/assets/accounts.js` | Refactor → `DeskServices`, layout 3 colunas |
| `frontend/assets/styles.css` | Classes `.servicos-*` |
| `frontend/index.html` | Nav «Serviços», cache bust |
| `frontend/assets/app.js` | Títulos view |
| `api/app/modules/registry.py` | Label módulo «Serviços» |

---

## Critérios de aceite — Fase 1

- [x] Menu mostra **Serviços** (não «Contas»)
- [x] Lista **todos** os clientes/domínios VM112 na coluna esquerda
- [x] Seleccionar cliente mostra tiles de catálogo (≥1 activo para e-mail)
- [x] Tile E-mail Tenant abre modal com detalhe + purge funcional
- [x] Purge remove domínio e actualiza lista (Spec 017)
- [x] Tiles futuros visíveis como «Em breve»
- [x] Escopo OPS visível na coluna direita
- [x] Escopo OPS **cards clicáveis** com camada + Spec + navegação (2026-06-25)
- [x] Blocklist visível na UI (lista + banner Escopo + modal)
- [x] Modal purge com **loading animado** durante consulta VM112
- [x] Tiles com badge **camada** (tenant / app / infra)
- [x] RBAC inalterado

---

## Critérios de aceite — Fase 2 (catálogo comercial)

- [ ] `GET /api/v1/services/catalog` devolve todos os produtos MOSP com `delivery_model`
- [ ] Tiles agrupados por categoria (Infra, Segurança, Apps, DevOps, Suporte)
- [ ] Badge IaaS / PaaS / SaaS / Suporte em cada tile
- [ ] Coluna OPS mostra matriz «cliente vs Ligbox» para serviço seleccionado
- [ ] Cliente com `hosting_mode` visível no banner
- [ ] Instâncias `traditional` ligadas a tickets (sem wizard)

---

## Critérios de aceite — Fase 3 (multi-wizard)

- [ ] Cada `catalog.code` com `wizard_id` abre wizard correcto
- [ ] Provisionar firewall → pfSense + regras + binding `vm_id`
- [ ] Provisionar Wazuh → agente + binding + link Infra 2 SOC
- [ ] Purge por `service_instance` com escopo do catálogo (não hardcoded domínio)

---

## Critérios de aceite — Fase 4 (MSP comercial)

- [ ] Plano comercial por instância (`commercial_plan`, valor ref.)
- [ ] SLA tier no cliente e alertas quando degradado
- [ ] Relatório «o que a Ligbox gere» exportável para proposta comercial (PDF/markdown)
- [ ] Upsell: tiles «Não contratado» com CTA interno para técnico sénior

---

## Evolução multi-wizard (Fase 3)

1. `service_catalog.wizard_id` aponta para endpoint VM112 ou outro nó
2. Tile activo com acção «Abrir wizard» / «Retomar onboarding»
3. Wazuh: binding `agent_id` + link para Infra 2 SOC
4. Firewall: binding `vm_id` + link pfSense API
5. Produtos MOSP (Nextcloud, ERPNext): wizard dedicado ou Helm + PaaS base
6. Produtos **traditional**: sem wizard — cria ticket + sessão assist (Spec 010)

### Prioridade sugerida de wizards (Roger)

| Ordem | Produto | Nível | Justificativa |
|-------|---------|-------|---------------|
| 1 | E-mail Tenant | SaaS | **Em produção** — VM112 |
| 2 | Firewall pfSense | IaaS | Já existe stack Proxmox + API |
| 3 | Wazuh por domínio | SaaS | Infra 2 SOC parcial |
| 4 | VPS gerenciado | IaaS | Base para outros produtos |
| 5 | Nextcloud | SaaS | Alto valor MOSP |
| 6 | ERPNext | SaaS | Upsell empresarial |
| 7 | K8s / CI/CD | PaaS | Clientes dev |

---

## Valor para o Técnico de Suporte Sénior

| Necessidade OPS | Como a página Serviços responde |
|-----------------|----------------------------------|
| «O que este cliente comprou?» | Tiles por `delivery_model` + estado |
| «O que nós gerimos vs cliente?» | Matriz pizza / `managed_layers` |
| «Onde está provisionado?» | Bindings (domínio, VM, zona, agente) |
| «Posso apagar para teste?» | Purge Spec 017 (e-mail) → generalizado Fase 3 |
| «Qual wizard retomar?» | `wizard_id` + estado `provisioning` |
| «Isto é incidente ou gap comercial?» | Tile «Não contratado» vs `degraded` |

---

## Referências

- Spec 017 — purge domínio VM112
- Spec 015 — registry módulos `overview-home`
- Spec 010 — assist takeover (suporte tradicional)
- VM112 API — `/api/admin/domains`
- Analogia comercial — **Pizza as a Service** (On-Prem → IaaS → PaaS → SaaS)
- Posicionamento MSP — **Managed Open Source Services (MOSP)**
