🔬 Auditoría Estructural — ALUS Codebase

Análisis forense de 3,566 archivos · 20,860 nodos · 28,924 edges · 57 comunidades

7
CRÍTICOS
12
ALTOS
8
MEDIOS

📊 Resumen de Hallazgos por Categoría

CategoríaCríticosAltosMedios
🔐 Seguridad321
⚡ Confiabilidad (HTTP/Timeouts)241
🏗 Estabilidad (Panics/Races)231
📡 Observabilidad023
🔄 CI/CD & DevOps012

🔴 Top 7 Issues Bloqueantes para 1M Usuarios

#1
validation-v2/app/service/otp_code.go:69
CRÍTICO OTP hardcodeado: strings.Repeat("1", form.Size) — siempre genera "11111...", nunca aleatorio. Vulnerabilidad de fuerza bruta trivial.
#2
archetype-1/pkg/service_client/service_client.go
CRÍTICO TODAS las llamadas HTTP usan http.DefaultClient sin timeout. Get/Post/Put/Delete/Patch — 0 timeouts. Una llamada bloqueada al MS de Validations colapsa el Gateway entero.
#3
archetype-1/pkg/orm/connection.go
CRÍTICO Pool de conexiones MySQL sin configurar: 0 en MaxOpenConns, MaxIdleConns, ConnMaxLifetime. Cada microservicio abre conexiones ilimitadas a MySQL. A 1M reqs, la BD colapsa.
#4
archetype-1/pkg/api/middlewares/rate_limit_middleware.go:88-96
CRÍTICO Race condition fatal: Si NewRateLimiter falla, responde 500 pero rateLimiter queda nil. La siguiente request hace rateLimiter.isKeyAllowed()PANIC (nil pointer dereference).
#5
validation-v2/app/pkg/middlewares/rate_limit.go:14,33-43
CRÍTICO Rate limiter roto: flag global restarLitmit permite solo 1 timer a la vez. El mapa userRequests crece sin límite (memory leak). Tras 1 minuto solo resetea 1 entrada. Inútil a escala.
#6
archetype-1/settings.yaml + 5 servicios más
CRÍTICO Secretos en texto plano: tokens JWT, claves de encriptación AES, DSNs con user/pass en YAML sin encriptar. Si el repo es comprometido, toda la infraestructura cae.
#7
catalog-service/pkg/api/v2/clients/catalog.go:1539,1631
CRÍTICO Emails personales hardcodeados en código productivo: marcos@alus.com.mx, alicia.aguero@alus.com.mx, artziel@alus.com.mx, rodrigo.uscanga@alus.com.mx. GDPR/compliance.

⚠ Issues por Microservicio

🏛 archetype-1 (Gateway)

IDArchivo:LíneaSeveridadDescripción
A1pkg/service_client/service_client.goCRÍTICOhttp.DefaultClient sin timeout — todas las llamadas inter-MS
A2pkg/orm/connection.go:52-83CRÍTICOSin MaxOpenConns/MaxIdleConns/ConnMaxLifetime en MySQL
A3pkg/api/middlewares/rate_limit_middleware.go:88-96CRÍTICOrateLimiter nil tras init fallido → panic
A4settings.yaml + settings.dev.yamlCRÍTICOSecretos JWT/AES/DSN en texto plano
A5pkg/api/ext/participant/orders.go:100-125ALTO15+ type assertions sin safe check (posibles panics)
A6pkg/api/ext/participant/orders.goALTOSAGA: data race en authEmbed compartido entre pasos
A7pkg/api/ext/participant/orders.go:300ALTOproductOfferedId=10066, userIdentifierTypeId=1 hardcodeados
A8pkg/api/v1/api.go:44-46MEDIOReadTimeout=100s, WriteTimeout=300s — susceptible slow-loris
A9pkg/service_client/externalapi/externalapi.go:168-177ALTOReflect para auth fields — fragile, panics on nil
A10pkg/service_client/externalapi/externalapi.go:392-447ALTOPII en logs — request/response bodies completos
A11pkg/api/v1/api.goALTOCORS: allow_origin "*" en producción

✅ validation-v2

IDArchivo:LíneaSeveridadDescripción
V1app/service/otp_code.go:69CRÍTICOOTP = strings.Repeat("1", n) — nunca aleatorio
V2app/pkg/middlewares/rate_limit.go:14CRÍTICOrestarLitmit global: solo 1 timer, memory leak en userRequests
V3app/pkg/middlewares/rate_limit.go:24MEDIOLímite hardcodeado >=5, no configurable

📦 catalog-service

IDArchivo:LíneaSeveridadDescripción
C1pkg/api/v2/clients/catalog.go:1539,1631CRÍTICOEmails personales hardcodeados
C2pkg/api/v2/clients/catalog.go:1541-1542ALTOgo Mailing.ProductSoldOut() sin recover — posible panic huérfano
C3pkg/api/v2/clients/catalog.go:68ALTOcontext.Get(r,"Client").(AuthClient) — panic si key ausente
C4pkg/api/v2/clients/catalog.goALTON+1 queries: múltiples loops llamando DB por producto
C5settings.yaml:17,23,44,48CRÍTICOaccess_secret, password, tokens en texto plano

👥 users-management

IDArchivo:LíneaSeveridadDescripción
U1app/repository/gorm.go:27ALTORaiseErrorOnNotFound=true en Statement compartido — race entre goroutines
U2app/repository/gorm.goMEDIOCountWhere silencia errores — retorna 0 sin loguear
U3settings.sample.yaml:24,30,38,43,51CRÍTICOSecrets en texto plano: access_secret, password, tokens

🔄 CI/CD & DevOps

IDArchivoSeveridadDescripción
D1Todos los .gitlab-ci.ymlALTOSin SAST, secret scanning, dependency scanning, Trivy
D2Dockerfile (single-stage)ALTOEjecutan como root, COPY . . copia secrets al image layer
D3Todos los .gitlab-ci.ymlMEDIO"E2E tests" = sleep 45 + curl /status — no son tests reales
D4Todos los DockerfilesMEDIOSin HEALTHCHECK en ningún contenedor
D5.gitlab-ci.ymlMEDIOSin coverage gate — tests pueden fallar silenciosamente

🔄 Flujos End-to-End Analizados

Flujo 1: Redención de Puntos (Purchase Order) — El más crítico

sequenceDiagram participant C as Cliente Frontend participant GW as Archetype Gateway participant EXT as API Externa (PagosApp/BimboNet) participant UM as Users MS participant PO as Purchase Orders MS participant CAT as Catalog MS participant COM as Communication MS participant DB as MySQL C->>GW: POST /api/embedded/v1/participant/purchase-orders GW->>GW: Validar JWT (middleware) GW->>GW: Rate Limit (RFC + crédito) Note over GW: ⚠ http.DefaultClient sin timeout GW->>EXT: POST /loyalty/balance/redeemAlusProduct Note over GW,EXT: ⚠ 0 timeout — bloqueo indefinido EXT-->>GW: {qpay_response: true, couponId} GW->>GW: Parse JSON con map[string]interface{} Note over GW: ⚠ Unsafe type assertions GW->>PO: POST /api/v1/purchase-orders Note over GW,PO: Paso SAGA #3 (crear orden) GW->>CAT: PATCH /api/v1/products/{id}/stock Note over GW,CAT: Paso SAGA #4 (descontar stock) Note over GW,CAT: ⚠ Si falla, compensación no cubre PO ya creada GW->>COM: POST /api/v1/notifications Note over GW,COM: go goroutine sin recover GW-->>C: 200 OK {order_id, coupon_id} Note over GW,DB: ⚠ Sin connection pool configurado Note over GW,DB: ⚠ Sin circuit breaker entre MS Note over GW,DB: ⚠ Sin distributed tracing

Flujo 2: Autenticación Externa (BBVA Credits)

sequenceDiagram participant C as Cliente participant GW as Gateway participant CL as Clients MS participant EXT as API BBVA participant DB as MySQL (GORM) C->>GW: POST /api/embedded/v1/{client}/{project}/auth GW->>CL: GET /api/v1/clients/{code}/projects/{code}/parameters CL->>DB: SELECT * FROM projects WHERE... Note over CL,DB: ⚠ GORM RaiseErrorOnNotFound → race condition CL-->>GW: {external_api: {...JSON gigante...}} GW->>GW: json.Unmarshal → map[string]interface{} Note over GW: ⚠ Reflect para extraer auth fields GW->>GW: Desencriptar secret_third_party (AES) GW->>EXT: POST /oauth/token Note over GW,EXT: ⚠ http.DefaultClient — 0 timeout EXT-->>GW: {access_token, user_id} GW-->>C: 200 OK {token, user_data}

Flujo 3: Generación OTP (Validación de Identidad)

sequenceDiagram participant C as Cliente participant GW as Gateway participant V2 as Validation v2 participant DB as MySQL participant COM as Communication C->>V2: POST /otp-codes/{client}/{project}/{user_code} V2->>V2: RateLimit (middleware) Note over V2: ⚠ Solo 1 timer global — todos comparten Note over V2: ⚠ userRequests crece sin límite (memory leak) V2->>DB: SELECT project, project_user V2->>DB: UPDATE otp_codes SET revoked V2->>V2: Generar OTP Note over V2: 🔴 strings.Repeat("1", n) — NO ALEATORIO V2->>DB: INSERT INTO otp_codes (code, expires_at) V2->>COM: Enviar email/SMS con código V2-->>C: 201 {otp_code}

📋 Propuestas — Con Restricción de 5 Semanas

⏰ La Restricción: 5 Semanas

Toda decisión arquitectónica debe evaluarse contra esta ventana de tiempo. Una solución perfecta que toma 8 semanas es un fracaso. Una solución suficiente en 5 semanas es una victoria.

📊 Matriz Esfuerzo vs Escalabilidad

graph LR subgraph "✅ Factible en 5 semanas" A1["Fix 7 críticos
3 días · +5x throughput"] A2["SQLX + Redis
5 días · +8x throughput"] A3["KrakenD Edge
2 días · +3x throughput"] A4["Circuit Breaker
1 día · Previene fallos"] A5["ProxySQL + Réplicas
1 día · +5x reads"] A6["RabbitMQ SAGA
4 días · +20x writes"] end subgraph "⚠ Riesgoso en 5 semanas" B1["DOKS Migración
5-8 días · Alto riesgo"] B2["Kafka Cluster
15-20 días · Muy alto riesgo"] B3["CQRS Completo
20-30 días · Imposible"] end subgraph "❌ Fuera de plazo" C1["Kafka + Event Sourcing
12-20 semanas"] C2["Multi-Region
8-12 semanas"] C3["Reescritura total
20-30 semanas"] end style A1 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A2 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A3 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A4 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A5 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A6 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style B1 fill:#451a03,stroke:#f59e0b,color:#fcd34d style B2 fill:#451a03,stroke:#f59e0b,color:#fcd34d style B3 fill:#450a0a,stroke:#ef4444,color:#fca5a5 style C1 fill:#450a0a,stroke:#ef4444,color:#fca5a5 style C2 fill:#450a0a,stroke:#ef4444,color:#fca5a5 style C3 fill:#450a0a,stroke:#ef4444,color:#fca5a5

🎯 Opción 1: Mínimo Viable (Semanas 1-2, Equipo de 2 devs)

TTM: 2 semanas · Escala a: ~100K usuarios · Riesgo: Bajo · Costo: +$200/mes

Solo arreglar los 7 issues críticos + SQLX en 3 queries. Sin nueva infraestructura. Sin cambios de arquitectura.

Qué incluyeEsfuerzoQué NO incluye
✅ OTP criptográfico30 min❌ KrakenD
✅ HTTP timeouts (5s)2h❌ Redis cache
✅ Pool MySQL configurado1h❌ RabbitMQ
✅ Rate limiters arreglados3h❌ Read replicas
✅ Secrets → env vars4h❌ Circuit breaker
✅ SQLX en top 3 queries1 día❌ Frontend changes
Total:~3 días

⚠ Limitación: A 500K usuarios, el SAGA síncrono vuelve a ser cuello de botella. Sirve para salir a producción con Clip rápido, pero requiere Opción 2 en 1-2 meses.

⭐ Opción 2: Recomendada (Semanas 1-5, Equipo de 3-4 devs + IA)

TTM: 5 semanas · Escala a: ~1M usuarios · Riesgo: Medio · Costo: +$530/mes

Estabilización + Optimización + Desacoplamiento asíncrono del SAGA. Todo lo que escala sin romper el plazo.

SemanaQué se haceEsfuerzoEscala a
1-27 críticos + SQLX top 10 + Redis cache + KrakenD8 días~200K
3Circuit breaker + ProxySQL + Read Replicas + Fix N+14 días~500K
4RabbitMQ/Redis Streams + Workers async (SAGA)4 días~800K
5Frontend polling + Schema Clients MS + Load Testing k65 días~1M

✅ Ventaja decisiva: Cada semana agrega capacidad incremental. Si la semana 5 se complica, la semana 4 ya soporta 800K usuarios — suficiente para el lanzamiento de Clip.

🚀 Opción 3: Máxima Ambición (Semanas 1-5, Equipo de 5-6 devs + IA intensiva)

TTM: 5 semanas (alta presión) · Escala a: ~2M usuarios · Riesgo: Alto · Costo: +$800/mes

Todo lo de Opción 2 + DOKS (Kubernetes) + OpenTelemetry. Solo si hay 5+ devs disponibles.

Extra sobre Opción 2EsfuerzoRiesgo
DOKS (Kubernetes) + HPA + Helm charts3 díasAlto — posibilidad de errores de configuración K8s bajo presión
OpenTelemetry tracing entre MS2 díasBajo
CI/CD con SAST + secret scanning1 díaBajo
Docker multi-stage para todos los MS1 díaBajo

⚠ Solo intentar si: El equipo ya tiene experiencia con Kubernetes en producción. Si es el primer proyecto K8s del equipo, descartar esta opción para el plazo de 5 semanas.

❌ Opción DESCARTADA: Kafka + Event Sourcing + CQRS

TTM real: 12-20 semanas · Costo: +$2,500/mes · Riesgo: Extremo

Kafka requiere: aprendizaje del equipo, configuración de clúster, gestión de tópicos/particiones, manejo de offsets, schema registry, exactly-once semantics. Imposible en 5 semanas. Evaluar como Fase 2 (Q3 2026) si Clip supera 1M usuarios sostenidos.

🔥 Recomendación Final — Con Restricción de Tiempo

CriterioOpción 1Opción 2 ⭐Opción 3
TTM2 sem5 sem5 sem (presión)
Usuarios soportados~100K~1M~2M
Riesgo de no entregar5%15%40%
Devs necesarios23-45-6
Costo mensual extra+$200+$530+$800
Deuda técnica residualAltaMediaBaja
Requiere rework en 3 mesesNoNo

→ Ejecutar Opción 2.
Semanas 1-2: Estabilización — Sin esto, nada funciona.
Semanas 3-4: Optimización + Async — El SAGA deja de ser síncrono.
Semana 5: QA + Load Test + Release — Solo ajustes finos.

Regla de oro: Cada viernes debe haber una versión desplegable. Si algo se atrasa, se corta scope, no calidad. Mejor entregar 800K estables que 2M rotos.

NO fork para Clip. Trunk-based + Feature Flags. Las correcciones benefician a todos los tenants.

🗓 Roadmap de 5 Semanas

gantt title Plan de Ejecución — Clip Scaling (5 Semanas) dateFormat YYYY-MM-DD axisFormat Semana %W section Camino A: Estabilización Fix OTP criptográfico :a1, 2026-05-19, 1d HTTP Client con timeout :a2, 2026-05-19, 1d Pool MySQL configurado :a3, 2026-05-20, 1d Fix rate limiters :a4, 2026-05-20, 2d Secrets → env vars :a5, 2026-05-21, 2d Eliminar PII hardcodeado :a6, 2026-05-21, 1d section Camino B: Optimización SQLX en top 10 queries :b1, 2026-05-26, 3d Redis Cache-Aside :b2, 2026-05-26, 2d KrakenD Edge Gateway :b3, 2026-05-28, 2d Circuit Breaker entre MS :b4, 2026-05-29, 1d Read Replicas + ProxySQL :b5, 2026-05-30, 1d Fix N+1 queries catalog :b6, 2026-05-30, 2d section Camino C: Async RabbitMQ/Redis Streams :c1, 2026-06-02, 4d Workers Points/Stock/Notify :c2, 2026-06-03, 3d Frontend async responses :c3, 2026-06-05, 2d Schema Clients MS :c4, 2026-06-02, 3d DOKS + HPA :c5, 2026-06-05, 2d OpenTelemetry tracing :c6, 2026-06-08, 2d section QA & Release Load Testing (k6 1M reqs) :q1, 2026-06-12, 3d Production Cutover :q2, 2026-06-16, 1d

🤖 Estrategia de Aceleración con IA

TareaCómo la IA aceleraFactor
GORM → SQLXTraduce queries automáticamente. Tests generados.3x más rápido
KrakenD configGenera krakend.json desde rutas actuales del Gateway5x
Circuit breakerGenera wrappers HTTP con gobreaker para cada MS client4x
Schema Clients MSMigraciones SQL + DAOs desde el JSON actual3x
OpenTelemetryInstrumentación automática de handlers Gin/Gorilla5x
k6 load testsScripts de carga simulando flujos SAGA completos4x

🧪 Simulador de Carga — Actual vs Propuesto

🏗 Actual

268ms
P95 Latencia
CPU57%
DB Conn120/200
Errores2.5%

🔧 Camino A+B

48ms
P95 Latencia
CPU22%
DB Conn150/2000
Errores0.3%

⚡ Camino A+B+C

12ms
P95 Latencia
CPU14%
MQ Lag60ms
Errores0.01%

📊 Throughput Comparativo

💰 Costo Mensual DO

RecursoActualCamino A+BCamino A+B+C
Droplets/DOKS$96$192$288
Load Balancer-$20$20
MySQL Managed-$120$120
Redis Managed-$60$60
RabbitMQ--$40
TOTAL$96$392$528