# Caso: evolution-go (API WhatsApp en Go) desplegado junto a Chatwoot > Resuelto el **2026-09-02** contra el host real. > Target: **LXC 102** (`coolify`), proyecto `AI AGENCY` / `production`. > Resultado: `https://evo.urieljareth.org/server/ok` → **HTTP 200** > (`{"status":"ok"}`), Manager UI operativo, contenedores `healthy`. > **Licencia ACTIVA desde el 2026-09-02** (vía OAuth Google del portal; ver §3.0). --- ## 0. Resumen ejecutivo El usuario pidió clonar [evolution-foundation/evolution-go](https://github.com/evolution-foundation/evolution-go) (rewritten en Go de Evolution API, motor WhatsApp sobre whatsmeow) e "implementar la funcionalidad" en el servicio Chatwoot (`/project/cho4d488omzjm4noyz98mwq7/environment/xhcy6urwmtk0onnxoeec24hq/service/c11xzy2tx2cdapm32f5b89vy`). **Decisión:** el stack se desplegó como **servicio hermano** (`evolution-go`, uuid `j0jkacsfcgypm2jmpillls01`) en el **mismo proyecto y entorno** que Chatwoot, no dentro del compose de Chatwoot. Motivos: - Editar el compose del stack Chatwoot fuerza un redeploy completo de Chatwoot (riesgo sobre un stack parcheado a mano — ver [chatwoot-enterprise-patch.md](chatwoot-enterprise-patch.md)). - Evolution-go necesita su propio PostgreSQL; meterle una DB ajena al stack de Chatwoot complica el rollback. - La integración WhatsApp→Chatwoot se hace por webhook/API hacia el FQDN público de Chatwoot — no requiere red compartida. El clon local vive en `projects/evolution-go` (tag 0.7.2). El compose de despliegue vive versionado en [`stacks/evolution-go/docker-compose.coolify.yml`](../../stacks/evolution-go/docker-compose.coolify.yml). | Pieza | Valor | |---|---| | Servicio Coolify | `evolution-go` (`j0jkacsfcgypm2jmpillls01`) | | Proyecto / entorno | `AI AGENCY` / `production` (mismo que Chatwoot) | | Imagen app | `evoapicloud/evolution-go:0.7.2` (pinada, Docker Hub) | | Imagen DB | `postgres:16-alpine` (hermana, `evolution-postgres`) | | FQDN | `https://evo.urieljareth.org` (wildcard del túnel, sin cambios CF) | | Healthcheck | `wget http://127.0.0.1:8080/server/ok` (200 sin licencia) | | Secretos | `GLOBAL_API_KEY` (40 car.) y `POSTGRES_PASSWORD` (24 car.) como envs del servicio, generados en memoria | --- ## 1. Bugs y trampas encontrados (lo que costó tiempo) ### 1.1 `POSTGRES_AUTH_DB` vacía = panic (bug upstream 0.7.2) El primer arranque crash-loopeaba (exit 2). Stack trace: `NewPollService → autoMigrate` sobre un `*sql.DB` **nil**. Cadena exacta en el código: - `cmd/evolution-go/main.go:300` — `initPostgresAuthDB()` devuelve `(nil, nil)` cuando `POSTGRES_AUTH_DB == ""`: **sin error**. - `main.go:408` pasa ese nil a `setupRouter`. - `pkg/poll/service/poll_service.go:39` — `autoMigrate` dereferencia el nil → panic. Es decir: la variable **parece opcional** (README no la marca obligatoria) pero sin ella el binario muere en loop. El fix fue setearla como URI completa: `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@evolution-postgres:5432/evogo_auth?sslmode=disable` (interpolada por Coolify al deploy, el secreto no vive en el compose). ### 1.2 Los nombres de env del README están desactualizados `docker/examples/docker-compose.yml` y el README usan `WADEBUG`/`LOGTYPE`, pero el código 0.7.2 (`pkg/config/env/env.go`) lee **`DEBUG_ENABLED`** y **`LOG_TYPE`**. Además `DATABASE_SAVE_MESSAGES` es obligatoria no-vacía (`panicIfEmpty`) aunque parezca opcional. La fuente de verdad es `env.go`. ### 1.3 `PATCH /services/{uuid}` rechaza campos de creación `New-CoolifyService.ps1` en su flujo de actualización (`-ServiceUuid`) enviaba `project_uuid`/`environment_name`/`server_uuid` y la API responde **422 "This field is not allowed"** para los tres. Solo acepta `name`, `docker_compose_raw` y `urls`. Corregido en el script el 2026-09-02 (el caso firecrawl solo ejercitó la creación, no la actualización). ### 1.4 La licencia bloquea la API — pero no al Manager `GateMiddleware` (`pkg/core/c0.go:638`) devuelve **503 `LICENSE_REQUIRED`** en todo hasta activar licencia, con excepciones: `/server/ok`, `/health`, `/manager*`, `/assets*`, `/license/*`, `/swagger*`, `/ws`. Esto es crítico para el healthcheck: como Traefik solo enruta contenedores `healthy` (ver [el caso del 503](coolify-servicio-nuevo-503-no-available-server.md)), un healthcheck contra cualquier endpoint bloqueado habría dejado al Manager **inalcanzable** — deadlock imposible de activar. `/server/ok` responde 200 siempre. ### 1.5 No se puede montar `init-db.sql` por ruta El compose de upstream monta `./init-db.sql` en el postgres. En Coolify el compose se guarda como `docker_compose_raw` en la DB — no hay árbol de archivos. No hace falta: `ensureDBExists()` (`pkg/config/config.go:79`) crea las DBs del DSN al arrancar (evogo_auth, evogo_users). --- ## 2. Cómo se reproduce ```powershell . .\.env.local.ps1 # 1. Crear (sin arrancar) .\deploy_skill\scripts\New-CoolifyService.ps1 ` -AppPath .\stacks\evolution-go ` -AppName evolution-go ` -Fqdn https://evo.urieljareth.org ` -PrimaryService evolution-go ` -ProjectName "AI AGENCY" -EnvironmentName production -NoDeploy -Force # 2. Secretos (POST /envs da 409 con vars ya sembradas: usar bulk PATCH; # generados en memoria, nunca en disco) # PATCH /services/j0jkacsfcgypm2jmpillls01/envs/bulk # data: [{POSTGRES_PASSWORD}, {GLOBAL_API_KEY}] con is_literal # 3. Arrancar y esperar (primer boot: minutos) Invoke-CoolifyApi.ps1 -Method POST -Path "/services/j0jkacsfcgypm2jmpillls01/start" .\coolify_skill\scripts\Test-CoolifyServiceReady.ps1 -Uuid j0jkacsfcgypm2jmpillls01 -WaitSeconds 600 # 4. Verificación funcional curl.exe -sS https://evo.urieljareth.org/server/ok # {"status":"ok"} curl.exe -sSI https://evo.urieljareth.org/manager/login # 200 ``` --- ## 3. Qué queda pendiente del lado del usuario (manual) 0. **Si el magic link del portal dice "ya usado o expiró"**: `GET /license/register` **cachea en memoria** la primera sesión de registro (`rc._v8` en `pkg/core/c0.go:710`) y devuelve siempre la misma `register_url` aunque el portal ya la haya invalidado (link consumido por preview del cliente de correo, doble click, o expirado). Fix verificado: `docker restart` del contenedor app → siguiente `/license/register` pide sesión nueva al portal. Después: abrir el magic link **una sola vez y directamente** (los previews de Outlook/Gmail consumen links single-use sin que los abras). Mejor aún: la página del portal ofrece **OAuth con Google/GitHub**, que evita el magic link por completo (verificado 2026-09-02: la sesión sobrevive aunque el magic link muera; tras el OAuth queda un `code` que se canjea con `GET /license/activate?code=...`). **Resultado final:** el magic link falló 3/3 (el cliente de correo del usuario consumía el link single-use antes que el navegador — el portal respondía `authorization code expired or already used` al canjear). La activación se completó así: `docker restart` (limpia la sesión cacheada) → iniciar el registro **desde el Manager** (para que mande su `redirect_uri` y la vuelta sea automática) → en el portal, botón **Entrar com Google** → redirección de vuelta al Manager → `/license/status` = `active`. 1. **Activar la licencia** (requiere cuenta en Evolution Foundation): abrir `https://evo.urieljareth.org/manager/login`, entrar con la API URL (`https://evo.urieljareth.org`) y la `GLOBAL_API_KEY` — visible en Coolify: proyecto AI AGENCY → servicio evolution-go → pestaña Environment. Hasta entonces toda la API responde 503 `LICENSE_REQUIRED` (el Manager sí funciona). Alternativa por API: `GET /license/register` devuelve la URL de registro. 2. **Conectar WhatsApp**: desde el Manager crear una instancia → escanear el QR (`POST /instance/create`, `GET /instance/qr` con header `apikey`). Las sesiones persisten en el volumen `evolution-data` (`/app/dbdata`). 3. **Enlazar con Chatwoot**: evolution-go **no trae** integración Chatwoot nativa (cero menciones en el código, a diferencia de evolution-api Node). El puente sería por webhook (`WEBHOOK_URL`) hacia un inbox tipo API de Chatwoot, o usar el servicio evolution-api Node viejo que sí la tiene. ## 4. Notas de estado - El servicio viejo `evolution-api` (`q6tnsvkvrjw4g0ab532l3r1s`, imagen Node `evoapicloud/evolution-api:v2.3.7`, mismo entorno) **está en producción en https://evoapi.urieljareth.org** (api/postgres/redis, 13+ días healthy; el contenedor app se llama `api-q6tn...`, no `evolution-api-...` — cuidado con los greps). Tiene 4 instancias WhatsApp (Personal, JM, INSTA, Asesoria Personal) sin integración Chatwoot configurada. NOTA 2026-09-02: este repo documentó erróneamente "app detenida" por un filtro truncado de `Get-CoolifyDockerStatus`; corregido tras verificación directa. - El compose normalizado por Coolify borra los comentarios; la versión documentada es la del repo (`stacks/evolution-go/`). - Imagen pinada a `0.7.2` (tag del repo clonado). Para subir de versión: cambiar el tag, repasar `pkg/config/env/env.go` del nuevo tag (los nombres de variables cambian entre versiones) y PATCH + start de nuevo.