Score DevOps: 4 / 10
RIESGO ALTOEl pipeline de despliegue es 100% manual vía rsync sin ninguna puerta de calidad automatizada — un error en cualquier commit llega directamente a producción sin freno. No existe backup automatizado de la base de datos MySQL en producción ni monitoreo de uptime activo, lo que convierte una caída de Hostinger o corrupción de datos en un evento sin recuperación garantizada. El ambiente local (Docker + Makefile) está bien estructurado, pero la brecha operacional entre ese stack y el proceso de deploy a shared hosting es el riesgo más alto para lanzar el MVP hoy.
Críticos — Bloqueantes para MVP
El make deploy-prod hace un rsync --delete directo desde la máquina local a Hostinger sin pasar por ningún test, lint ni revisión. No existe repositorio de GitHub Actions, GitLab CI ni ningún pipeline automatizado. Un commit defectuoso llega a producción en segundos. La única "protección" es un sleep 5 en el Makefile.
rsync -avz --delete --exclude '.env*' ./ hostinger-streaming:~/domains/.../public_html/
test → lint → staging-deploy → smoke-test → prod-deploy. La rama main debe ser protected con requerir PR aprobado. Tiempo estimado de implementación: 1 día.
No existe ningún script de cron, tarea programada ni integración de backup que exporte el MySQL de Hostinger. El único mecanismo hallado es make sync-prod-data que sincroniza archivos JSON del directorio data/, no la base de datos. Si Hostinger pierde datos (falla de disco, error humano en un DELETE sin WHERE, o un atacante), no hay punto de restauración.
mysqldump whatsapp_crm | gzip > backup_$(date +%Y%m%d).sql.gz y rsync a almacenamiento externo (S3, Backblaze o tu Mac local). Tiempo: 2 horas.
No hay servicio de uptime monitoring (UptimeRobot, BetterStack, Pingdom, etc.) configurado para streamingapps.online/whatsapp-crm/public/webhook.php. El logging actual se reduce a error_log() de PHP que va al error_log de Apache — no hay aggregación centralizada, no hay alertas proactivas. Si el webhook deja de responder, los mensajes de clientes se pierden en silencio y solo se descubre cuando el cliente llama o Anderson revisa manualmente.
/webhook.php con notificación a Slack. Implementar un /health endpoint que retorne DB ping + Green API status. Tiempo: 30 minutos.
Existe whatsapp-crm/.env.bak-20260924 en el directorio del proyecto. El patrón .env.* del .gitignore lo excluye del repositorio, pero si el directorio es desplegado con rsync (que excluye .env*) podría quedar expuesto en servidor o en backups. Los archivos .bak de .env representan un vector de exposición de API keys y tokens activos.
.env.bak* localmente. Verificar que el rsync excluye explícitamente *.bak además de .env*. Rotar las credenciales de Green API y Groq si el archivo fue alguna vez enviado.
El deploy es un rsync --delete sin versioning de archivos en servidor. No hay tagged releases en git que correspondan a deploys. No hay carpetas releases/ con symlink current (estilo Capistrano). Si un deploy rompe producción, "rollback" es ejecutar el mismo rsync con el código anterior — que tiene que estar limpio en la máquina local del desarrollador en ese momento.
deploy-YYYYMMDD-HHmm y ejecutar git push origin --tags. Rollback = checkout del tag anterior + rsync. Tiempo: 15 minutos de scripting.
Advertencias — Importantes pero no bloqueantes
Existe init-full.sql (schema completo desde cero) y migration_001_multichannel.sql pero no hay runner que rastree qué migraciones ya fueron aplicadas. Si el schema de producción diverge del local, un deploy puede romper queries sin advertencia. No hay Flyway, Phinx ni tabla schema_migrations.
El shared hosting de Hostinger no permite auto-scaling, no permite persistencia de workers ni WebSockets nativos, y comparte CPU/RAM con otros clientes. Para MVP con tráfico bajo (<100 msgs/día) es suficiente, pero cualquier spike o cliente corporativo lo colapsa. No hay forma de añadir Kubernetes o auto-restart aquí.
swap-number.sh contiene hardcodeados: IP del servidor (151.106.96.98), puerto SSH (65002) y usuario (u305025526). Si el servidor migra o las credenciales cambian, el script falla silenciosamente. Estos valores deberían venir de variables de entorno o de ~/.ssh/config.
Todo el logging usa error_log('[AMR-CRM] ...') nativo de PHP. Esto escribe texto plano al log de Apache en el servidor. No hay JSON logs, no hay correlation IDs, no hay aggregación en Papertrail/Logtail/Datadog. Depurar un error en producción requiere acceso SSH al servidor para leer el archivo raw.
No existe /health ni /status endpoint que verifique: DB conectada, Green API reachable, Groq API respondiendo. El monitoreo externo solo puede hacer un ping HTTP 200 al index, no validar que las integraciones críticas estén vivas.
Tanto deploy-staging como deploy-prod en el Makefile hacen rsync al mismo path ~/domains/streamingapps.online/public_html/. No hay separación real de ambiente staging en el servidor. El staging local (Docker puerto 8083) es útil pero no hay pre-producción en el hosting real.
Lo que está bien hecho
Makefile con ambientes bien separados
DEV (8082, Xdebug), Staging (8083, opcache ON), Full Stack (MySQL + Mailhog). Parity local-staging muy sólida. Comandos make dev / make test claros y documentados.
Docker Compose con healthcheck en MySQL
El compose del CRM tiene healthcheck: mysqladmin ping con retry, y el app espera condition: service_healthy. Race conditions de arranque correctamente resueltos.
Cascade de envío: Green API → UltraMsg → WAHA
Si Green API falla, hay failover automático a UltraMsg y luego a WAHA. Es la única redundancia operacional real del sistema y es muy valiosa para un MVP con un solo número de WhatsApp activo.
Secrets bien excluidos de git
.gitignore excluye .env, .env.*, logs/, database.sqlite y data/*.json. .env.example documenta todas las variables requeridas. Buena higiene base.
Webhook token con hash_equals (timing-safe)
webhook.php usa hash_equals() para comparar tokens, previniendo timing attacks. Acepta múltiples tokens (hasta 5) para multi-canal. Responde 401 si no autorizado.
Slack escalation vía AdminNotifier
EmergencyBrake notifica a Slack cuando activa manual_takeover por loop o pedido de asesor. Es la única alerta operacional activa y funciona bien como señal de operaciones nivel 1.
Mapa de Infraestructura Actual
ENTORNO LOCAL
PRODUCCIÓN (Hostinger)
SERVICIOS EXTERNOS
Roadmap de Remediación — Para poder lanzar el MVP
| Prioridad | Acción | Esfuerzo | Estado |
|---|---|---|---|
| P0 | UptimeRobot free monitor en /webhook.php + alerta Slack |
30 min | Pendiente |
| P0 | Cron de backup MySQL diario en Hostinger → rsync a local | 2 horas | Pendiente |
| P0 | Eliminar .env.bak* y rotar credenciales si fueron expuestas |
15 min | Pendiente |
| P1 | Git tag por deploy (deploy-YYYYMMDD-HHmm) + script de rollback |
1 hora | Planificado |
| P1 | Endpoint /health.php: DB ping + Green API + Groq status |
2 horas | Planificado |
| P2 | GitHub Actions básico: tests → staging rsync → smoke test → prod rsync | 1 día | Futuro |
| P3 | Migration runner simple (tabla schema_migrations + scripts numerados) |
3 horas | Post-MVP |
| P3 | Migrar a VPS (DigitalOcean/Hetzner) cuando tráfico >500 msgs/día | 1-2 días | Post-MVP |
Veredicto para lanzar MVP hoy
El CRM puede lanzarse como MVP con las restricciones actuales de Hostinger, pero solo si se completan los 3 items P0 antes del primer cliente real: (1) monitoreo de uptime, (2) backup de base de datos y (3) limpieza del archivo .env.bak. El riesgo operacional sin estos tres elementos no es aceptable: una caída sin alerta o una pérdida de datos sin backup podría destruir la confianza del primer cliente y la reputación del producto en las primeras 24 horas.