DevOps & Operaciones AUDITORÍA #25

WhatsApp CRM · AMR TECH™ · streamingapps.online

Portal de Auditorías
FOLIO: AUD-20260925-005 Secuencia: Auditoría #005 del día 2026-09-25 Dimensión: DevOps & Operaciones
2026-09-25 10:45:00 COT Auditor: Anderson Mares (AMR TECH) VIGENTE
4

Score DevOps: 4 / 10

RIESGO ALTO

El 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.

Deploy
2/10
Monitoring
3/10
Backup/DR
2/10
Secretos
6/10
Ambientes
7/10
Escalabilidad
2/10
Rollback
1/10

Críticos — Bloqueantes para MVP

C1 — Sin CI/CD: deploy 100% manual CRÍTICO

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.

# Makefile línea 123 — deploy-prod (todo lo que hay)
rsync -avz --delete --exclude '.env*' ./ hostinger-streaming:~/domains/.../public_html/
Remediación: Agregar GitHub Actions con jobs: 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.
C2 — Cero backup automatizado de MySQL en producción CRÍTICO

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.

Remediación: Cron diario en Hostinger: 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.
C3 — Sin monitoreo de uptime ni alertas de caída CRÍTICO

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.

Remediación: Registrar en UptimeRobot (gratis, 5-min polls) el endpoint /webhook.php con notificación a Slack. Implementar un /health endpoint que retorne DB ping + Green API status. Tiempo: 30 minutos.
C4 — Archivo .env.bak-20260924 con posibles credenciales activas CRÍTICO

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.

Remediación: Eliminar todos los archivos .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.
C5 — Sin mecanismo de rollback CRÍTICO

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.

Remediación mínima: Antes de cada deploy, crear un git tag 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

W1 — Sin migration runner AVISO

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.

W2 — Hostinger shared hosting: techo duro de escalabilidad AVISO

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í.

W3 — swap-number.sh con SSH hardcodeado AVISO

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.

W4 — Logging no estructurado (solo error_log PHP) AVISO

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.

W5 — Sin endpoint de health check AVISO

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.

W6 — deploy-staging y deploy-prod apuntan al mismo servidor AVISO

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

Docker Dev :8082 Xdebug
Docker Staging :8083 OPcache
MySQL local :3307
Ollama hermes3 :11434

PRODUCCIÓN (Hostinger)

Shared Hosting PHP 8 1 nodo
MySQL Hostinger Sin backup
Deploy: rsync manual Sin CI/CD

SERVICIOS EXTERNOS

Green API Primario
UltraMsg Fallback
Groq API (LLM) Cascade
Slack (escalations) Activo

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.

✅
P0 completados
= MVP listo para monetizar
⏱️
~2.75 horas
para P0 completos
🚫
Sin P0
= riesgo inaceptable