# Spec 039 — Catálogo Mestre de Autorização Ligbox OPS

**Criado:** 2026-06-29  
**Solicitado por:** Roger  
**Status:** Aprovado Roger 2026-06-29 — catálogo completo (TODAS superfícies)  
**Prioridade:** P0 (governança — pré-requisito da UI Matriz + Controle de acesso)  
**Estende:** Spec **003** (RBAC base), **027** (matriz por função), **015** (módulos Desk), **019** (Console)  
**Sistemas:** VM122 Desk · VM123 Console/Finance · VM112 Onboard · VM104 Wazuh · Infra (Proxmox, Traefik, pfSense)

---

## 1. Problema (demanda Roger)

Hoje sabemos que **root / `super_admin`** faz tudo (criar, autorizar, gerir, trocar senha), mas **não está documentado nem exposto na UI** o mapa completo de:

- O que **cada função** (`SU`, `CO`, `TEC`, `NOC`, …) pode fazer
- **Porquê** (regra de negócio / risco)
- **Onde** (Desk, Console, FOSS, Odoo, OpenPanel, VM112, APIs internas)
- **A nível de ação** (não só “módulo ON/OFF”): ex. deletar instância OpenPanel, atualizar processo, purge domínio, aprovar runbook A7

Sem este catálogo, a **Matriz de Acesso** e o **Controle de acesso** mostram toggles e módulos, mas **não fecham o mapa** de atribuições por posição.

**Objetivo desta spec:** ser a **fonte única de verdade** para enumerar operações × funções × ambientes, alimentar a Matriz UI (Spec 027-UI) e implementação RBAC (`permissions.py`, bindings Odoo-style, provisionamento VM123).

---

## 2. Relação com specs existentes

| Documento | Nível | O que falta para Matriz completa |
|-----------|-------|----------------------------------|
| [Spec 027](../027-desk-rbac-function-matrix/spec.md) | Função × módulo × ambiente (✅🔒🔗) | Granularidade por **ação** dentro de cada módulo/produto |
| [Spec 027 UI](../027-desk-rbac-function-matrix/ui-access-matrix.md) | Wireframes Matriz | Dados estruturados (YAML) por ação |
| [vm123-product-roles](../027-desk-rbac-function-matrix/contracts/vm123-product-roles.md) | FOSS/Odoo/OpenPanel grupos | Lista exaustiva endpoints FOSS + OpenPanel CE |
| `platform_role_catalog.py` | Bindings por função | Sincronizar com catálogo 039 |
| `permissions.py` | ~30 helpers `can_*` | Um helper por `action_id` do catálogo |
| Controle de acesso (UI) | Toggles informativos | Persistência + ligação a `action_id` |

**Regra:** Spec 039 **não substitui** 027 — **detalha** cada célula da matriz 027 em linhas de ação auditáveis.

---

## 3. Taxonomia de autorização

### 3.1 Camadas

```text
FUNÇÃO (desk_role)          ex.: super_admin, ops_lead  → código UI: SU, CO, TEC
    └── MÓDULO / PRODUTO    ex.: admin-users, foss.client, openpanel.site
            └── AÇÃO        ex.: user.create, domain.purge, instance.delete
                    └── RECURSO   ex.: desk_user, vm112_domain, openpanel_vhost
```

### 3.2 Níveis de efeito (`access_level`)

| Código | Significado | UI Matriz |
|--------|-------------|-----------|
| `full` | CRUD / executar sem aprovação | ✅ |
| `approve` | Executar após aprovação humana | 🟡 |
| `read` | Só leitura (pode mascarar PII) | 🔒 |
| `link` | Deep-link / abrir consola externa | 🔗 |
| `api` | Só via API Desk (M2M ou token) | ⚙️ |
| `system` | Conta sistema / agente | 🤖 |
| `none` | Proibido | ❌ |

### 3.3 Superfícies (`surface`)

| ID | Descrição | Host |
|----|-----------|------|
| `desk` | Ligbox Ops Desk (VM122 UI + API) | desk.ligbox.com.br |
| `console` | Ligbox Ops Console (Spec 019) | console.ligbox.com.br |
| `vm112` | Wizard + Carbonio + API onboard | onboard.ligbox.com.br |
| `vm123_foss` | FOSSBilling Admin API | financeiro.ligbox.com.br |
| `vm123_odoo` | Odoo 16 XML-RPC | financeiro.ligbox.com.br/odoo |
| `vm123_openpanel` | OpenPanel / OpenAdmin / bridge | openpanel.ligbox.com.br |
| `infra` | Proxmox, Traefik, pfSense, SSH hosts | LAN |
| `agents` | Agentes A0–A7 | VM122 agentic |

### 3.4 Identificador de ação (`action_id`)

Formato: `{surface}.{domain}.{verb}` — exemplos:

- `desk.auth.user.create`
- `desk.auth.user.freeze`
- `vm112.domain.purge`
- `vm123_openpanel.instance.delete`
- `vm123_foss.invoice.void`
- `console.case.runbook.execute`

---

## 4. Catálogo de funções (códigos UI)

| Código | `desk_role` | Categoria | Mandato resumido |
|--------|-------------|-----------|------------------|
| **RO** | `root` (utilizador) | Sistema | Conta física dono — bypass humano; não é role atribuível |
| **SU** | `super_admin` | Ops | Tudo no Desk + purge + users + módulos + agentes |
| **CO** | `ops_lead` | Ops | Operação diária, audit, domínios, tickets, aprovações ops |
| **TEC** | `technician` | Ops | Tickets atribuídos, assist/takeover, migração e-mail |
| **NOC** | `noc` | Ops | Monitorização read-only, Wazuh, dados mascarados |
| **SAD** | `sales_admin` | Comercial | Pipeline, billing validation, FOSS/Odoo manager |
| **SSU** | `sales_support` | Comercial | CRM, pedidos, clientes — sem validar billing |
| **FIN** | `finance` | Negócio | FOSS/Odoo fiscal, faturas, inadimplência |
| **MKT** | `marketing` | Negócio | Campanhas, produtos FOSS, leads |
| **SEO** | `seo` | Negócio | DNS, sites OpenPanel, performance |
| **DEV** | `developer` | Plataforma | Código, GitHub, APIs, deploy Desk |
| **DVO** | `devops` | Plataforma | Infra, Proxmox, OpenAdmin, SSH |
| **SOC** | `security_analyst` | Plataforma | Incidentes, Wazuh rules, resposta |
| **CMS** | `content_editor` | Plataforma | Sites clientes OpenPanel |
| **AIO** | `agentic_operator` | Plataforma | Aprovar runbooks A7, findings |
| **SVC** | `api_service` | Sistema | M2M webhooks, provisionamento |
| **AGT** | `agent_system` | Sistema | Agentes IA autónomos |
| **PTR** | `partner` | Comercial | Revendedor — clientes próprios, OpenPanel Reseller, FOSS scoped |

### 4.1 Funções RBAC custom (UI)

Funções criadas via `POST /rbac/roles` **MUST** herdar de um único template:

| Template permitido | `desk_role` base | Uso |
|--------------------|------------------|-----|
| **CO** | `ops_lead` | Coordenação, aprovações ops, purge (sem gestão users) |
| **TEC** | `technician` | Suporte restrito — tickets assigned, assist |

**Proibido:** herdar de SU, FIN, DVO ou escolha livre de permissões avulsas no MVP.

---

## 5. Inventário de ações — Controle de identidade (Desk VM122)

### 5.1 Gestão de utilizadores (`desk.auth.*`)

| action_id | Descrição | SU | CO | Demais | API / UI |
|-----------|-----------|:--:|:--:|:------:|----------|
| `desk.auth.user.list` | Listar utilizadores Desk | full | none | none | GET `/v1/auth/users` |
| `desk.auth.user.create` | Criar utilizador (directo) | full | none | none | *gap — hoje só via registo+aprovação* |
| `desk.auth.user.approve_registration` | Aprovar pedido cadastro | full | **full** | none | POST `/v1/auth/registration-requests/{id}/approve` |
| `desk.auth.user.reject_registration` | Rejeitar pedido | full | **full** | none | POST `.../reject` |
| `desk.auth.user.edit` | Editar nome, role, display | full | none | none | PATCH `/v1/auth/users/{username}` |
| `desk.auth.user.freeze` | Desactivar conta (`active=false`) | full | none | **SSU: none** | PATCH `active` |
| `desk.auth.user.password.reset` | Definir nova senha (admin) | full | none | none | PATCH `password` |
| `desk.auth.user.2fa.reset` | Reset TOTP + backup codes | full | none | none | POST `.../reset-2fa` |
| `desk.auth.user.delete` | Remover utilizador | none | none | none | *não implementado — usar freeze* |
| `desk.auth.user.groups.assign` | Ligar grupos/bindings VM123 | full | none | none | Fase 3 `vm123/identity` |
| `desk.auth.role.create` | Criar função custom RBAC | full | none | none | POST `/rbac/roles` |
| `desk.auth.role.clone` | Clonar função | full | none | none | POST `/rbac/roles/{id}/clone` |
| `desk.auth.role.freeze` | Pausar função | full | none | none | PATCH `/rbac/roles/{id}/status` |
| `desk.auth.modules.toggle` | Activar/desactivar módulos Desk | full | none | none | PATCH `/v1/modules/{id}` |

**Porquê:** credenciais humanas Ligbox = superfície de ataque máxima. **Excepção Roger:** CO aprova cadastros; freeze/reset senha permanece **SU only**; **SSU nunca congela**.

### 5.2 Conta própria (`desk.account.*`)

| action_id | Descrição | Quem |
|-----------|-----------|------|
| `desk.account.password.change` | Trocar própria senha + 2FA | Qualquer autenticado |
| `desk.account.2fa.setup` | Configurar TOTP | Qualquer autenticado |
| `desk.account.console_handoff` | Token para Console | Staff com acesso Console |

---

## 6. Inventário — Módulos Desk (por `module_id`)

Cada módulo (Spec 015 `registry.py`) decompõe-se em **acções UI + API**. Resumo por módulo:

### 6.1 `core` — Dashboard, Tickets, Conta

| action_id | Descrição | SU | CO | TEC | NOC | SAD | SSU |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|:---:|
| `desk.ticket.list` | Ver tickets | full | full | full | read* | full | full |
| `desk.ticket.read` | Detalhe ticket | full | full | full | read* | full | full |
| `desk.ticket.patch` | Actualizar estado/notas | full | full | assigned | none | full | full |
| `desk.ticket.assign` | Atribuir responsável | full | full | self | none | full | full |
| `desk.ticket.close` | Fechar ticket | full | full | assigned | none | full | full |
| `desk.dashboard.kpi` | KPIs resumo | full | full | full | read | full | full |

\* NOC: dados sensíveis mascarados (Spec 003).

### 6.2 `overview-home` — Serviços IaaS (Spec 018)

| action_id | Descrição | SU | CO | TEC | DVO | FIN |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|
| `desk.services.client.list` | Listar clientes/domínios | full | full | full | read | read |
| `desk.services.catalog.read` | Catálogo cPanel/serviços | full | full | full | read | read |
| `desk.services.process.update` | Actualizar processo onboard | full | full | read | api | none |
| `desk.purge.auth.generate` | Gerar código purge (root pwd) | full | none | none | none | none |

### 6.3 `vm112-domains` + purge (Spec 017, 032)

| action_id | Descrição | SU | CO | TEC | DVO |
|-----------|-----------|:--:|:--:|:---:|:---:|
| `vm112.domain.list` | Listar domínios orquestrados | full | full | read | read |
| `vm112.domain.read` | Detalhe domínio | full | full | read | read |
| `vm112.domain.purge` | Purge completo domínio | full | full | none | approve |
| `vm112.purge.job.recover` | Recuperar job purge falhado | full | full | none | full |

### 6.4 `assist` — Wizard takeover (Spec 010)

| action_id | Descrição | SU | CO | TEC |
|-----------|-----------|:--:|:--:|:---:|
| `desk.assist.session.list` | Sessões activas wizard | full | full | full |
| `desk.assist.takeover` | Assumir sessão cliente | full | full | assigned |
| `desk.assist.handoff` | Passar sessão | full | full | full |
| `desk.assist.action.execute` | Acções assist aprovadas | full | full | assigned |

### 6.5 `billing-recurrence` (Spec 023)

| action_id | Descrição | SU | CO | FIN | SAD | SSU |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|
| `desk.billing.account.read` | Ficha conta cliente | full | full | full | full | read |
| `desk.billing.state.validate` | Validar billing_state | full | full | full | full | none |
| `desk.billing.link.foss` | Deep-link FOSS | full | full | full | full | link |

### 6.6 `email-migration` (Spec 013, 019)

| action_id | Descrição | SU | CO | TEC | DVO |
|-----------|-----------|:--:|:--:|:---:|:---:|
| `desk.migration.job.create` | Criar job imapsync | full | full | full | read |
| `desk.migration.job.cancel` | Cancelar job | full | full | assigned | none |
| `desk.migration.dns.gate` | Gate DNS pré-migração | full | full | full | read |

### 6.7 `infra` / `infra2-soc` (Spec 033)

| action_id | Descrição | SU | CO | DVO | SOC | DEV |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|
| `desk.infra.stack.read` | Stack health VMs | full | full | full | full | read |
| `desk.infra.stack.probe` | Executar probe serviço | full | full | full | read | api |
| `desk.infra.deploy` | Deploy API/frontend | full | none | full | none | full |

### 6.8 `agentic-ops` (Spec 029, 030)

| action_id | Descrição | SU | CO | AIO | SOC | DEV |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|
| `desk.agent.finding.read` | Ver findings | full | full | full | full | full |
| `desk.agent.runbook.approve` | Aprovar remediação A7 | full | full | full | scope | none |
| `desk.agent.binding.toggle` | UI/Focus/Approve agente×função | full | none | none | none | none |

### 6.9 `events`, `leads`, `audit`, `dns`

Ver matriz Spec 027 §3.2 — cada endpoint `main.py` mapeia para `action_id` (anexo A em `contracts/action-catalog.yaml`).

---

## 7. Inventário — VM123 produtos (granular)

### 7.1 FOSSBilling (`vm123_foss.*`)

Módulos API: `client`, `order`, `invoice`, `product`, `service`, `staff`, `support`, `extension`, hosting→OpenPanel.

| action_id | sales_admin | sales_support | finance | marketing | developer |
|-----------|:-----------:|:-------------:|:-------:|:---------:|:---------:|
| `vm123_foss.client.create` | full | full | full | none | api |
| `vm123_foss.client.delete` | full | none | full | none | none |
| `vm123_foss.order.create` | full | full | read | none | api |
| `vm123_foss.invoice.create` | full | read | full | none | none |
| `vm123_foss.invoice.void` | full | none | full | none | none |
| `vm123_foss.product.edit` | full | read | read | full | api |
| `vm123_foss.staff.manage` | none | none | read | none | api |
| `vm123_foss.hosting.provision` | full | full | read | none | api |

Detalhe endpoints: [vm123-product-roles.md §2](../027-desk-rbac-function-matrix/contracts/vm123-product-roles.md).

### 7.2 Odoo 16 (`vm123_odoo.*`)

| action_id | sales_admin | sales_support | finance |
|-----------|:-----------:|:-------------:|:-------:|
| `vm123_odoo.partner.read` | full | full | full |
| `vm123_odoo.partner.write` | full | full | full |
| `vm123_odoo.sale.order.create` | full | full | read |
| `vm123_odoo.invoice.post` | full | none | full |
| `vm123_odoo.account.move.validate` | none | none | full |

Grupos: `group_sale_manager`, `group_sale_salesman`, `account.group_account_manager`.

### 7.3 OpenPanel (`vm123_openpanel.*`)

| action_id | Descrição | SU | **CO** | SAD | SSU | SEO | CMS | DVO |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|:---:|:---:|
| `vm123_openpanel.site.create` | Novo site/vhost | full | full | full | full | full | full | full |
| `vm123_openpanel.site.delete` | **Remover instância** | full | **full** | none | none | none | none | **none** |
| `vm123_openpanel.ssl.manage` | Certificados LE | full | full | link | link | full | full | full |
| `vm123_openpanel.db.create` | Base de dados | full | full | link | link | full | full | full |
| `vm123_openpanel.cron.manage` | Cron jobs | full | full | none | none | read | full | full |
| `vm123_openpanel.backup.restore` | Restaurar backup | full | full | none | none | none | read | full |
| `vm123_openpanel.openadmin.access` | Painel OpenAdmin :2087 | full | full | link | none | link | none | full |
| `vm123_openpanel.autologin.client` | Login cliente (bridge) | full | full | full | full | full | full | link |

**Gap actual:** Desk não expõe toggles por `action_id` OpenPanel — só deep-links e provisionamento M2M.

---

## 8. Inventário — Console (Spec 019)

**Decisão Roger:** Console na **mesma Matriz** do Desk (superfície `console`, aba dedicada, RBAC herda role Desk via handoff).

| action_id | Descrição | SU | CO | TEC | SOC | AIO |
|-----------|-----------|:--:|:--:|:---:|:---:|:---:|
| `console.case.create` | Abrir chamado CH-* | full | full | full | full | read |
| `console.case.assign` | Assumir chamado | full | full | full | full | none |
| `console.case.timeline.read` | Timeline correlacionada | full | full | full | full | full |
| `console.discover.search` | Discover (estilo Wazuh) | full | full | read | full | read |
| `console.runbook.execute` | Executar runbook | full | full | none | scope | approve |
| `console.wizard.assist.view` | Passo actual wizard | full | full | full | read | read |
| `console.link.wazuh` | Deep-link VM104 | full | full | read | full | read |

Console autentica via **handoff** Desk (`/v1/auth/console-handoff`) — RBAC herda role Desk.

---

## 9. Inventário — Partner / Revendedor (`partner` · PTR)

| action_id | Descrição | PTR | Notas |
|-----------|-----------|:---:|-------|
| `desk.partner.dashboard.read` | Dashboard revendedor (clientes próprios) | full | Módulo `partner-portal` |
| `vm123_foss.client.create` | Criar cliente FOSS (scoped) | full | Só tenants do partner |
| `vm123_foss.order.create` | Pedidos FOSS | full | |
| `vm123_foss.invoice.read` | Ver faturas clientes | read | Sem void |
| `vm123_openpanel.site.create` | Provisionar hosting cliente | full | Via bridge |
| `vm123_openpanel.autologin.client` | Painel cliente | full | |
| `vm123_openpanel.site.delete` | Deletar instância | none | Só SU/CO Ligbox |
| `desk.auth.*` | Qualquer gestão Desk | none | |
| `console.*` | Console ops | none | |

**OpenPanel nativo:** role `reseller` (Spec 028). **FOSS:** grupo `ligbox-partner` (criar no Admin).

---

## 10. Inventário — VM112 Wizard

| action_id | Descrição | SU | CO | TEC | DEV |
|-----------|-----------|:--:|:--:|:---:|:---:|
| `vm112.wizard.session.read` | Estado sessão onboard | full | full | assigned | read |
| `vm112.carbonio.mailbox.create` | Criar caixa mail | api | api | api | api |
| `vm112.carbonio.block.release` | Libertar ACCOUNT_EXISTS | full | full | none | api |
| `vm112.dns.verify` | Verificar DNS wizard | full | full | read | read |
| `vm112.api.webhook.emit` | Emitir evento (sistema) | system | system | system | system |

---

## 11. Matriz resumida — Quem faz o quê (visão Roger)

| Área | Quem manda | Porquê |
|------|------------|--------|
| Criar/editar/freeze utilizadores Desk | **SU** | Credenciais internas |
| **Aprovar cadastros** | **SU**, **CO** | Roger — CO autónomo |
| **Congelar users** | **SU only** — **SSU nunca** | Segregação comercial |
| Purge domínio / dados cliente | **SU**, **CO** (DVO recover job) | Irreversível — Spec 032 |
| Validar billing / faturação | **SU**, **CO**, **FIN**, **SAD** | Segregação comercial vs financeira |
| FOSS pedidos e clientes | **SAD**, **SSU**, **PTR** (scoped) | Linha de frente |
| FOSS faturas / void | **FIN**, **SU** | Risco fiscal |
| **OpenPanel delete instance** | **SU**, **CO only** | Downtime — Roger 2026-06-29 |
| OpenPanel conteúdo sites | **CMS**, **SEO**, **MKT** | Operação editorial |
| Tickets / assist | **TEC** (assigned), **CO**, **SU** | Menor privilégio |
| Infra / deploy | **DVO**, **DEV**, **SU** | Separação código vs infra |
| Agentes A7 remediação | **AIO**, **CO**, **SU** | Human-in-the-loop |
| Módulos Desk ON/OFF | **SU** | Feature flags globais |
| **Console** | Mesma matriz Desk | Handoff — Roger |
| **RBAC custom** | Herda **CO** ou **TEC** | Templates only |

---

## 12. Implementação — fechar o mapa na UI

### Fase A — Catálogo (esta spec) ✅

- [x] Taxonomia `action_id`
- [x] Inventário por superfície (Desk + VM123 + Console + VM112 + Partner)
- [x] Decisões Roger validadas (§13)

### Fase B — `contracts/action-catalog.yaml` ✅ v1.1

- Lista machine-readable: `action_id`, `label`, `surface`, `roles{}`, `api_route`, `spec_ref`
- Gerar toggles Controle de acesso + export CSV Matriz

### Fase C — API RBAC

- `GET /rbac/actions` — catálogo completo
- `PATCH /rbac/roles/{id}/actions` — persistir overrides (só SU)
- `permissions.py`: `can_action(role, action_id)` único entry point

### Fase D — UI

- Aba **Controle de acesso**: toggles ligados a YAML (não só informativos)
- Abas Matriz: filtrar por `surface` (Desk | FOSS | Odoo | OpenPanel | Console)
- Sidebar função: mostrar **contagem de acções** full/read/none

---

## 13. Decisões Roger (2026-06-29) — FECHADAS

| # | Pergunta | Decisão |
|---|----------|---------|
| 1 | `sales_support` congela utilizadores? | **Não** — só SU |
| 2 | `ops_lead` aprova cadastros sem SU? | **Sim** — CO autónomo |
| 3 | Deletar instância OpenPanel | **SU + CO only** (não DVO) |
| 4 | Console na mesma Matriz? | **Sim** — mesma matriz Desk |
| 5 | RBAC custom herda de? | **CO ou TEC only** |
| 6 | Partner/revendedor | **Nesta spec** — função `partner` (PTR) |
| 7 | Prioridade granularidade | **TODOS** — Desk + FOSS + Odoo + OpenPanel + Console |

**Implementação código (parcial):** `can_approve_registration`, `can_openpanel_delete` → SU+CO; rotas registo actualizadas.

---

## 14. Critérios de aceite

- **FR-039-001**: Cada `action_id` MUST ter exactamente uma linha no catálogo YAML.
- **FR-039-002**: Cada função humana MUST ter coluna em todas as tabelas §5–9.
- **FR-039-003**: UI Controle de acesso MUST reflectir catálogo (não lista hardcoded).
- **FR-039-004**: Alteração de permissão MUST gerar audit log (`rbac_audit`).
- **FR-039-005**: RO/root MUST NOT aparecer como role atribuível — só nota §4.
- **FR-039-006**: Função `partner` MUST estar no catálogo com scope tenant isolado.
- **FR-039-007**: RBAC custom MUST declarar `inherits_from: ops_lead | technician`.

---

## Anexos

- `contracts/action-catalog.yaml` — inventário estruturado (geração automática)
- `../027-desk-rbac-function-matrix/spec.md` — matriz macro
- `../../projects/ops-desk/api/app/platform_role_catalog.py` — bindings código
- `../../projects/ops-desk/api/app/permissions.py` — guards actuais
