12 KiB
CLAUDE.md
Guía para Claude Code (claude.ai/code) al trabajar en este repositorio.
Antes de invocar cualquier herramienta
- Carga los secretos:
. .\.env.local.ps1(gitignored). Sin esto, todas las llamadas a API lanzan excepción. - Lee docs/TOOL-INDEX.md — es el catálogo canónico de todo lo ejecutable: firma real de cada script, variables de entorno que requiere, y si es solo-lectura o mutante. Su §1 lista seis gotchas que producen resultados silenciosamente incorrectos. No los adivines.
- Para el estado actual del sistema (qué existe, qué versión corre): docs/proxmox-inventory.md.
Los seis gotchas, en una línea cada uno (detalle en el índice):
-Rawestá invertido entre wrappers. En Coolify y Cloudflare, sin-Rawrecibes un string, no objetos: filtrar da vacío sin error.Invoke-ProxmoxSsh.ps1corrompe comillas anidadas. Para comandos con más de un nivel de comillas, codifica en base64.- Los nombres de contenedor llevan sufijo uuid y cambian en cada redeploy. Resuélvelos siempre; nunca los escribas a mano.
/applications/*de la API de Coolify da 404 por el hostname público (Cloudflare), no por Coolify. Contra el origen (http://192.168.0.117:8000/api/v1,$env:COOLIFY_API_URL_ORIGIN) la API completa responde; y desde v4.2 los endpoints de estado exigen POST.- "Verde en Coolify" no es "enrutado en Traefik". La UI mira
running; Traefik exigehealthy. Un servicio nuevo aún arrancando da503 no available serversin que nada esté mal configurado. - Un
502casi siempre es el puerto. Coolify saca el puerto de Traefik del:puertodel FQDN guardado; sin él cae alEXPOSEde la imagen. Si no coinciden,connection refused.
Qué es este repo
No es el código de una aplicación. Es un toolkit de operaciones: wrappers de
PowerShell más documentación de contexto y runbooks que permiten a un agente
diagnosticar y administrar un homelab — un host Proxmox local (192.168.0.200,
nodo thinkcentre) y el stack Coolify que corre dentro de su LXC 102. No hay
build, lint ni tests: los "comandos" son los scripts operativos.
Convención de idioma: la documentación (docs/, README.md, este archivo)
está en español. Los scripts y los SKILL.md/TOOLS.md de cada skill están en
inglés.
Router de intención → herramienta
Lo que pide el usuario, y con qué se resuelve. Para la firma completa de cada script, ve al índice.
| El usuario pide… | Usa |
|---|---|
| "¿está bien el servidor?", "revisa el Proxmox" | .\scripts\Test-ProxmoxConnection.ps1 y luego .\scripts\Get-ProxmoxInventory.ps1 |
| "inventario de LXC/VMs", "qué hay corriendo" | .\scripts\Get-ProxmoxInventory.ps1 |
| "¿está corriendo X?", "estado de los contenedores" | .\coolify_skill\scripts\Get-CoolifyDockerStatus.ps1 -Filter <regex> |
| "logs de X" | .\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct exec 102 -- docker logs <nombre-resuelto> --tail 100" |
| "qué apps hay en Coolify", "dame el uuid de X" | .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/resources" -Raw |
| cualquier cosa de la API de Coolify | .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 — pero revisa primero qué endpoints viven (§1.4 del índice) |
| "¿está online el sitio X?" | .\deploy_skill\scripts\Test-ServiceOnline.ps1 -Fqdn https://x.urieljareth.org |
"el servicio nuevo está en verde pero da 503 / no available server" |
.\coolify_skill\scripts\Test-CoolifyServiceReady.ps1 -Uuid <uuid> -WaitSeconds 600 — espera, no redeployes; ver §1.5 del índice |
| "arregla el túnel de Cloudflare", rutas/DNS | .\scripts\Invoke-CloudflareApi.ps1 + docs/runbooks/cloudflare-tunnel.md |
| "Chatwoot perdió el enterprise" | diagnostica con .\scripts\Get-ChatwootLicenseStatus.ps1 -Deep; repara con Apply-ChatwootEnterprisePatch.ps1 -ReenableAccountFeatures |
| "que arranque solo tras un apagón" | .\scripts\Install-CoolifyAutostart.ps1 -VerifyOnly primero |
| "publica este proyecto en Coolify" | lee §4 del índice antes. Stack compose → New-CoolifyService.ps1 (arreglado y verificado el 2026-08-24); app git → flujo UI con Playwright |
"una app de Coolify sigue el main de upstream y se rompió" |
docs/casos/firecrawl-stack-minimo.md — cambiar a imágenes precompiladas y compose propio |
| "valida que este proyecto se puede deployar" | .\deploy_skill\scripts\Test-PreDeployChecklist.ps1 -Path <ruta> -Strict |
| "estoy construyendo una app para este Coolify" | docs/AGENTS-coolify-apps.md (contrato de red, puertos, dominios, volúmenes) |
| "crea/lista un repo en Gitea", "sube esto a Gitea" | .\gitea_skill\scripts\ — ver §5 del índice |
| "diagnosticar/iniciar Hermes (LXC 100)", "MiniMax-M3" | .\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct status 100" + docs/casos/hermes-minimax-m3-setup.md |
| "un contenedor no conecta a su base de datos" | ver "Gotcha de red Docker" abajo |
Arquitectura
Todo llega al host por un único camino SSH. No hay acceso directo a Docker ni a la red interna:
script PowerShell → Invoke-ProxmoxSshCommand (scripts/ProxmoxAgent.ps1)
→ ssh [email protected]
→ pct exec 102 -- docker ... (cualquier trabajo de Docker/Coolify)
- scripts/ProxmoxAgent.ps1 es la librería compartida.
Hazle dot-source (
. .\scripts\ProxmoxAgent.ps1) para obtenerGet-ProxmoxConfig,Invoke-ProxmoxSshCommandeInvoke-ProxmoxApi. Todos los demás scripts la consumen en lugar de reimplementar la conexión. - Resolución de configuración:
Get-ProxmoxConfiglee variables de entorno (PROXMOX_HOST,PROXMOX_NODE,PROXMOX_SSH_KEY,PROXMOX_COOLIFY_LXC…) y cae a los defaults locales hardcodeados. SSH funciona solo con los defaults; las llamadas a la API exigenPROXMOX_API_TOKEN_ID+PROXMOX_API_TOKEN_SECRET(si faltan,Invoke-ProxmoxApilanza). El ID del LXC de Coolify (102) viene de la config: usa$config.CoolifyLxc, no lo hardcodees en scripts nuevos. - Cuatro APIs independientes, con scripts y variables distintas: Proxmox REST
(
curl.exe -k, header de token PVE), Coolify (Invoke-RestMethod, Bearer, basehttps://coolify.urieljareth.org/api/v1), Cloudflare y Gitea. - Docker no se administra en el host Proxmox: vive dentro del LXC
102. Todo comando de contenedor va envuelto enpct exec 102 -- docker ....
Gotcha de red Docker
Cuando una app de Coolify y su base de datos son contenedores hermanos en la
misma red Docker, la app debe alcanzar la DB por su nombre de servicio Docker,
no por localhost (Nextcloud, por ejemplo, usa el host nextcloud-db). El DNS
entre servicios es la causa raíz habitual de los fallos de "conexión a la base de
datos" aquí. Compruébalo con:
.\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct exec 102 -- docker exec <app> getent hosts <nombre-servicio>"
Reglas de operación (las imponen las skills — cúmplelas)
- Solo-lectura primero. El default es diagnosticar: list, status, logs, inspect, health checks.
- Confirma antes de cualquier cambio de estado. Pregunta explícitamente antes
de:
pct/qmstart/stop/reboot/destroy;dockerrestart/stop/rm/compose up-down; deploys de Coolify o cualquierPOST/PUT/PATCH/DELETE; escritura de variables de entorno; y todo cambio de firewall, red, storage, volumen, clave o token. Para acciones riesgosas: captura el estado actual y enuncia primero el camino de rollback. - Nunca escribas secretos en el repo. Ni tokens, passwords, claves privadas,
cookies o secretos de token PVE — no en Markdown, no en scripts, no en logs.
Viven solo en
.env.local.ps1(gitignored) o en el almacén del SO..gitignorebloquea ademásACCESS.md,*.key,*.pem,*.crt. Al depurar bases de datos, verifica conectividad sin imprimir credenciales. - Prefiere los scripts del repo antes que cadenas de comandos manuales largas, y no ejecutes comandos destructivos amplios construidos desde strings generados.
Chatwoot: el parche enterprise se degrada solo — pero un guard lo auto-repara
El parche no se pierde al actualizar. Internal::CheckNewVersionsJob hace ping
diario a hub.2.chatwoot.com (a los MD5(INSTALLATION_IDENTIFIER).hex % 1440
minutos pasada la medianoche UTC = 16:16 UTC en esta instalación) y reescribe
INSTALLATION_PRICING_PLAN con la respuesta del hub; después
ReconcilePlanConfigService apaga los 9 feature flags premium en todas las
cuentas. De ahí tres consecuencias:
- El SQL de 3 filas no basta. Los flags por cuenta viven en
accounts.feature_flags(bitmask). Usa.\scripts\Apply-ChatwootEnterprisePatch.ps1 -ReenableAccountFeatures; comprueba el estado con.\scripts\Get-ChatwootLicenseStatus.ps1 -Deep. - Los
UPDATEpor SQL no invalidan la caché Redis deGlobalConfig(TTL de 1 día,V1:GLOBAL_CONFIG:*) porque se saltan elafter_commit :clear_cachedeInstallationConfig. Cualquier parche por SQL debe llamar además aGlobalConfig.clear_cache. - No bloquees
hub.2.chatwoot.com. Ese mismo host relaya el push móvil (ChatwootHub.send_push), activo aquí porqueFIREBASE_*está vacío. En su lugar, scripts/chatwoot-enterprise-guard.sh corre en el host Proxmox desde/root/scripts/, agendado por/etc/cron.d/chatwoot-enterprise-guardcada 5 minutos, y repara un revert detectado en ~13s (log:/var/log/chatwoot-enterprise-guard.log, solo escribe cuando actúa).
Nunca pulses Refresh en /super_admin/settings.
Estado al 2026-08-07: corre
chatwoot/chatwoot:v4.16.2(el pin documentado antes erav4.16.1, así que el pin no sostuvo la versión). El plan está enenterprisey una ejecución manual del guard pasa correctamente, pero el log registraERROR: no se pudo leer INSTALLATION_PRICING_PLANdurante la ventana del update. El guard tiene el nombre del contenedor hardcodeado (postgres-c11xzy2tx2cdapm32f5b89vy), así que un redeploy que cambie el sufijo lo deja ciego. Análisis completo, registro de ejecución y rollback: docs/runbooks/chatwoot-update.md.
Túnel de Cloudflare
El túnel es gestionado desde el dashboard (el ingress baja del edge — se ve
como INF Updated to new configuration version=N en los logs de cloudflared).
Corrige rutas en el dashboard o vía
scripts/Invoke-CloudflareApi.ps1, nunca
editando archivos en el host. Los puertos 6001/6002 deben usar http://. Ver
docs/runbooks/cloudflare-tunnel.md.
Dónde vive el contexto
| Qué | Dónde |
|---|---|
| Catálogo de herramientas + gotchas | docs/TOOL-INDEX.md |
| Estado verificado del sistema | docs/proxmox-inventory.md |
| Procedimientos vigentes | docs/runbooks/ — conexion.md, diagnostico.md, coolify-docker.md, seguridad.md, cloudflare-tunnel.md, autostart-coolify.md, nextcloud.md, baserow.md, chatwoot-update.md |
| Casos resueltos paso a paso | docs/casos/ — chatwoot-enterprise-patch.md, hermes-minimax-m3-setup.md, coolify-servicio-nuevo-503-no-available-server.md, firecrawl-stack-minimo.md |
| Incidentes archivados (no fuente de verdad) | docs/incidentes/ |
| Contrato para construir una app deployable | docs/AGENTS-coolify-apps.md |
| Reglas operativas por dominio | agent/SKILL.md, coolify_skill/SKILL.md, deploy_skill/SKILL.md, gitea_skill/SKILL.md |
| Ejemplos ejecutables por dominio | el TOOLS.md de cada carpeta *_skill/ |
| Referencia de la API de Coolify | coolify_skill/references/ops/*.md — busca con rg, no cargues el árbol |
Para la referencia de la API de Coolify: no leas el árbol completo. Busca en
coolify_skill/references/ con rg y abre el único references/ops/*.md que
coincida.