Plataforma en construcción
Avance global de la plataforma 91%

307 de 339 hitos completados · 18 módulos en construcción

Última actualización:

Plan de entrega por ola

La plataforma se construye en olas escalonadas. Cada ola entrega valor de negocio autocontenido y desbloquea a la siguiente. Detalle completo en docs/WORKFLOW.md.

Ola 1 — MVP

10 módulos

Bot funcional, panel de administración, auditoría inalterable y bridge mínimo al sistema legacy. Primera entrega con valor operativo real.

  • svc-identity
  • svc-conversation
  • svc-legacy-bridge
  • svc-audit
  • web-admin
  • status
  • infra-auth
  • infra-events
  • infra-observability
  • infra-cicd

Ola 2 — Hacer real lo embebido

3 módulos

Vinculación verdadera con operadoras, extracción de svc-recharge a microservicio independiente y observabilidad completa. Paso a producción.

  • svc-recharge
  • svc-inventory
  • svc-vinculacion

Ola 3 — Inteligencia de negocio

3 módulos

Historial filtrable, reportes ejecutivos automáticos y desempeño por distribuidor. Dirección recibe visibilidad analítica.

  • svc-history
  • svc-reports
  • svc-distributors

Ola 4 — Proactividad

2 módulos

Notificaciones proactivas vía WhatsApp y monitor en vivo de la operación con alertas de negocio.

  • svc-monitoring
  • svc-notifications

Servicios backend

Microservicios core que componen el dominio de negocio.

Backend Ola 1 · MVP svc-identity

Identidad y Acceso

Servicio de autenticación y autorización. Integra Keycloak (OIDC + Google Identity, ADR-0007/0055) para autenticación. Autorización vía Keycloak Realm Roles validados localmente en PermissionGuard (sin PDP externo, ADR-0055). CASL se mantiene solo en frontend para checks UI declarativos.

Avance 49/49 (100%)
  • Esquema DB y migraciones (ADR-0010 + external_identities)
  • TrustTokenSigner (ADR-0026 §1, contraparte de svc-audit)
  • Health probes + métricas Prometheus
  • JwtVerifier con JWKS Keycloak + errores tipados
  • ForwardAuth endpoint POST|GET /v1/auth/verify (ADR-0008)
  • Cache hardening completo: jitter + single-flight + SWR + circuit-breaker + deny-list + TTL superadmin (ADR-0008 §1-6)
  • Endpoint admin POST /v1/admin/sessions/:userId/revoke-now
  • Migración a PermissionGuard local (ADR-0055 PR-I4): elimina /v1/authorize HTTP + subsistema CASL completo
  • Audit emission a svc-audit por NATS JetStream (reyva.identity.access)
  • Health probes completos (Postgres/Redis/NATS) con detail per-dep + métricas
  • Graceful shutdown hooks: NATS drain, Redis quit, Postgres pool end (rolling deploys K8s/Cloud Run)
  • Hardening env hardcoded: DATABASE_URL obligatoria sin fallback dev (production-ready, sin secretos en code)
  • Tests integración + e2e (156 con docker-compose: postgres+keycloak+redis+nats)
  • Rate limiting endpoints públicos (@nestjs/throttler + Fastify trustProxy + métrica Prom identity_rate_limited_total)
  • Trust token con exp + nonce anti-replay (ADR-0026)
  • Audit event decision_source diferencia fail-open de allow real (auditoría 2026-05-12 alto #7 + #12)
  • POST /v1/authorize endpoint PDP central (ADR-0009)
  • Catálogo de permisos Keycloak Realm Roles (ADR-0055 PR-I1): 32 atómicos + 8 Composite Roles en @reyva/shared-contracts + check-permissions-sync CI
  • Endpoints HTTP proxy Keycloak Admin API (ADR-0055 PR-I4): GET/PUT/DELETE /v1/admin/keycloak/{roles,users/:id/role}
  • Eliminación /v1/authorize (ADR-0055 PR-I4): validación pasó a PermissionGuard local en cada svc-*, 0 HTTP calls a auth por request
  • JwtRoleGuard any-of (JWT Keycloak admin OR trust token S2S) + GET catalog + GET users search (ADR-0054 PR-H5 C1)
  • Lockout-prevention DELETE /v1/permissions/roles last_admin_role_protected (ADR-0054 PR-H5 A)
  • Métrica Prometheus identity_jwt_role_guard_total (PR-H5 closeout)
  • GET /v1/admin/keycloak/users/:userId/roles (ADR-0055 PR-I4 — consumido por UserRolesAssigner)
  • Motor de permisos consolidado en PermissionGuard local (ADR-0055 PR-I8) + cleanup del sidecar previo (PR-H7)
  • Cierre auditoría 2026-05-12 (altos): CWE-117 log sanitization + bloqueo Unicode confusables en forwardedUri
  • Application layer migrado a Result<T, E> (errores tipados, sin throw en use-cases/repos)
  • Lazy provisioning Google OIDC (ADR-0011) — MVP: schema + use-case + FOR UPDATE + dominio whitelist + Workspace port stub
  • Migración ADMIN_API_TOKEN fallback → trust token vía Traefik ForwardAuth (escape hatch IDENTITY_DEV_AUTH_BYPASS=1 para dev local)
  • Reverse-proxy GET /v1/admin/audit-events a svc-audit (firma trust token con principal admin, consumido por web-admin P12)
  • Endpoint admin GET /v1/admin/business-subjects con paginación + filtro subject_kind + enrichment teléfono vía RPC NATS a svc-legacy-bridge (P5)
  • Endpoints admin detalle/PATCH/CRUD atributos business_subjects (P6) + publisher reyva.identity.business_subject_changed + repo user_attributes con soft-delete versionado
  • Endpoints admin invitaciones (LIST/CREATE/EXPIRE/REFRESH) (P7+P8) + publisher reyva.identity.invitation_changed sobre pending_user_provisioning
  • Reverse-proxy admin chat-sessions + bot-settings a svc-conversation (P3/P4/P11) — SvcConversationClient firma trust token con principal admin
  • KeycloakAdminClient con cache de admin token + 5 métodos REST (ADR-0055 PR-I4)
  • AdminRolesController: proxy Keycloak para gestión de roles desde apps/web (ADR-0055 PR-I4)
  • PermissionChangedPublisher: emite reyva.identity.permission_changed tras assign/revoke (ADR-0055 FU-3)
  • POST /v1/admin/business-subjects/:userId/reveal-phone + PiiRevealedPublisher (audit P0-3 S2.4 — granularidad fina LFPDPPP ADR-0027 §6)
  • Catálogo permisos: agregado Identidad.Contacto.Revelar (admin_security/admin_plataforma/superadmin) + sync Keycloak realm-reyva.json
  • Restauración GET /v1/permissions/users (ADR-0055 PR-I4 follow-up): lookup admin read-only
  • S5.4: VendedorSnapshotConsumer NATS materializa business_subjects (kind=vendedor) desde reyva.legacy.vendedor.changed — gemelo del consumer de subdistribuidor (S5.1), tier default ACT (vendedores legacy sin enum tier)
  • S4.6: TrustToken guards (JwtRoleGuard + TrustTokenOnlyGuard) deny-by-default sin IDENTITY_TRUST_HMAC_SECRET — opt-in dev local via IDENTITY_TRUST_HMAC_BYPASS_DEV=1 (audit P1-3, cierra ventana staging/preview auth-less)
  • S4.6: JwtRoleGuard valida x-user-id == claims.sub pre-rewrite (audit P1-7 v2) — bloquea forge attempt antes del rewrite a users.id interno, cierra impersonation forense LFPDPPP §3.1 ARCO en TODOS los endpoints admin (audit-events, business-subjects, invitations, conversation, sessions). Métrica deny_header_user_mismatch + log forensic con email_hash
  • PR-A empleado-recarga-bot: columna identity.employees.telefono (+ índice parcial) + endpoint admin GET/PATCH /v1/admin/employees/:userId (normaliza 10 díg MX vía normalizeMxPhone, PII masking en respuesta/logs, audit-on-write reyva.identity.employee_changed con hashes). El audit-on-write quedó COMPLETO recién el 2026-08-11: el publisher existía desde PR-A pero nadie consumía el evento —expiraba en el stream sin llegar al audit trail WORM—; lo cierra EmployeeChangedConsumer de svc-audit (resource_kind=identity_employee), medido 0→N filas en audit.audit_events
  • RPC NATS resolve_for_recharge (PR-B empleado-recarga-bot): resuelve teléfono→empleado activo + evalúa Recargas.Recarga.Crear EN identity (ADR-0054) vía CompositeRoleDefinitions; trust token del caller (ADR-0026), fail-closed (DB caída→unavailable, nunca found:false), cache negativo del miss. Consumido por el gate del bot (PR-C)
  • Endpoint HTTP privado POST /internal/employees/resolve-for-recharge (feature 014 PR-1, #882): reemplaza el transporte del RPC NATS resolve_for_recharge por HTTP; ingress interno + allowed_invokers=[sa-svc-conversation] + ConversationTrustTokenGuard (reusa verifyTrustToken per-caller, ADR-0026) + firma OIDC de GCP. Misma lógica de resolución fail-closed; el RPC NATS se retira en PR-3
  • Grant read-only reyva_ro sobre schema identity (feature 014, migración 0011): USAGE+SELECT para el Job db-query (ADR-0062) que audita SC-008 (empleados resolubles por tabla); patrón canónico de 0008_grant_reyva_ro_conversation, guard IF EXISTS del rol
  • Fix C11 (audit 2026-07-29): GET/PATCH /v1/admin/employees/:userId exigen el permiso atómico Identidad.Empleado.{Ver,Editar} in-handler, no solo el allowlist plano del JwtRoleGuard. Ese allowlist incluye monitor_operativo (rol read-only de monitoreo, ADR-0057) que podía leer el padrón de empleados y MUTAR el teléfono que dirige las recargas del bot, quedando registrado en el audit como acción administrativa legítima. 7 tests adversariales (403 sin permiso, fail-closed sin claims, permisos no fungibles) verificados en rojo al revertir el fix
  • El PADRÓN sale de identity: se retiran los 2 consumers de snapshot, el repo de sync, los endpoints admin de business_subjects y la tabla identity.business_subjects. Vendedores y subdistribuidores no autentican ni autorizan (medido: 56.364 filas con CERO roles contra 9 empleados con rol) e inflaban 6.000× una tabla del camino crítico del bot. Los recibe svc-distributors, directo del puente. Identity conserva empleados, roles, atributos, permisos y autenticación. Rotura declarada: las pantallas /admin/users quedan sin backend
Backend Ola 2 svc-recharge

Recargas y Productos Digitales

🔄

Materializa el saldo con el que se vende cada SIM (AT&T, Movistar, Unefon): REYVA vende SIMs con saldo incluido, no saldo suelto. El monto lo lee del inventario al cobrar — nadie lo elige. Conecta con TurboCarga vía bridge ACL y maneja idempotencia exactly-once para que una SIM se cobre una sola vez.

Avance 46/47 (98%)
  • Scaffold NestJS + Fastify + health probes + bootstrap (PR-A)
  • RechargeProviderPort + MockProvider (ADR-0050 §1)
  • Schema DB inicial (recharge_requests, attempts, sim_reservations, outcomes)
  • NATS publisher/consumer + stream REYVA_RECHARGE + event RechargeRequested (ADR-0050 §4)
  • Saga orchestrator + state machine + ports stub (ADR-0050 §3 PR-1)
  • Saga + ports migrados a Result<T, E> (errores tipados, sin throw en orchestrator)
  • Compensaciones saga + trail recharge_attempts + handling provider_exception (ADR-0050 §3 PR-2)
  • Manejo indeterminate + pending_reconciliation (ADR-0050 §3 PR-3)
  • Fix: conciliación escala filas con provider sin QUERY_STATUS capability (ADR-0050 §6, evita filas huérfanas)
  • Fix: claimBatch atómico claim-and-mark con lease (audit P1-10, evita doble-claim entre réplicas del cron)
  • Lock distribuido Redis anti doble-tap (ADR-0050 §4 PR-4)
  • Política reintentos por capability (ADR-0050 §6 PR-5)
  • API REST POST /v1/recharges (sin auth, pre-prod) (ADR-0050 §3 PR-6)
  • TrustTokenGuard HMAC en endpoint REST (ADR-0026 / PR-F)
  • Nonce store anti-replay Redis SET NX EX en TrustTokenGuard (ADR-0026 §Notas P0-2, audit 2026-06-14 P0-2): bloquea replay del mismo token v2 → recarga duplicada; fail-closed ante Redis caído
  • Aislamiento del secret HMAC s2s per-caller (ADR-0026 §Notas #31): el verifier solo acepta tokens firmados por svc-conversation (su caller autorizado); comprometer otro servicio NO permite forjar recargas (CRIT-11)
  • Authorization via PermissionGuard local con shared-infrastructure (ADR-0055 PR-I5): elimina HTTP call a /v1/authorize
  • PermissionGuard con @RequirePermission(Permissions.Recargas.Recarga.Crear) (ADR-0055 PR-I5)
  • SLI de éxito de recarga (reyva_recharge_initiated/executed_total) para SLO ≥95% (ADR-0056)
  • Tests integración + e2e saga (7 escenarios — scenario 8 audit hash chain diferido a svc-audit integration)
  • Adapter `primary` real Recargaki (BLOQUEADO 2026-05-26: requiere ficha técnica del proveedor — ver claudedocs/solicitud-recargaki-ficha-tecnica-2026-05-26.md)
  • HttpVinculacionAdapter consume svc-vinculacion (Ola 2)
  • Endpoint admin POST /v1/admin/recharges/:id/dispute (ADR-0050 §7, Ola 2)
  • Endpoint admin POST /v1/admin/recharges/:id/refund capability-gated (Ola 2)
  • Endpoint admin POST /v1/admin/recharges/:id/reconcile (Ola 2)
  • Eventos NATS reyva.recharge.{disputed,refunded,reconciled} (Ola 2)
  • Tabla recharge_admin_actions (audit append-only, PR-8 Ola 2)
  • HttpInventoryAdapter consume endpoints svc-inventory /v1/sims/* (Ola 2 follow-up PR-2)
  • Job conciliación nocturna BullMQ + cruce SIMs↔recargas (ADR-0050 §6, Ola 2)
  • AnomalyDetectionService cross-service (sim_sold_no_recharge + recharge_succeeded_no_sim_sold) + métrica recharge_reconciliation_anomalies_total
  • Emit reyva.recharge.reconciled tras resoluciones del job nocturno (ADR-0050 §5)
  • Persist-first del tRequestID antes del cobro + reconciliación real con queryStatus(tRequestID) + rescate de executing_provider (Incremento 0, ADR-0050/0058)
  • Deploy staging
  • Deploy producción + canary
  • Eliminación AuthorizeClient + PermissionsBootstrapService (-1,222 LOC, ADR-0055 PR-I5)
  • Audit P0-3 fix: orden mark-sold ANTES de emit reyva.recharge.completed (workflow S3.7)
  • Contract tests s2s svc-recharge ↔ svc-inventory ↔ svc-vinculacion via @reyva/shared-contracts/contracts (workflow S4.1)
  • Retención stream REYVA_RECHARGE 7d→365d (prep Ola 3: rebuild read-models history/distributors/reports desde NATS replay, ADR-0052 §3/§313)
  • ADR-0058: adaptador SOAP RecargaAqui (GetTRequestID→doT→checkTransaction, fail-fast por env) + reserva pesimista sobre id_linea vía bridge ANTES de cobrar (anti doble-cargo bot↔portal legacy, persistir-luego-actuar)
  • Política fail-open/fail-closed de vinculación configurable por operadora (VINCULACION_FAILMODE__<OP>, default fail-closed CRT 2026, override auditado, ADR-0051 §A7)
  • Detector huérfanas succeeded↔TBL_REYVA_TURBOCARGA (Fase 3 reconciliación nocturna): cruce por dn+ventana fecha_recarga (RPC list_by_window, índice IDX_TB_fecha_recarga, anti full-scan) + clasificación recuperable/no-recuperable + auto-re-asiento flag-gated DETECTOR_ASIENTO_AUTORESEAT (default off), cierra deuda ADR-0058
  • Fix detector huérfanas: la referencia a la tabla externa iba SIN calificar en los subqueries correlacionados (Drizzle renderiza "id" desnudo → scope capture contra recharge_attempts.id → predicado ra.internal_correlation_id = ra.id::text, el attempt contra sí mismo) → provider_correlation_id NULL para TODAS las filas → el detector clasificaba toda recarga succeeded como huérfana. Suite de integración contra Postgres real (el unit test mockeaba el db entero y el SQL nunca se ejecutaba)
  • Fix causa raíz de huérfanas AUTOINFLIGIDAS: el provider_correlation_id (tRequestID = id_solicitud del asiento legacy) se persiste ahora en los 2 caminos que lo descartaban — el rescate de reconciliación (lo recibía fresco de queryStatus y lo tiraba) y el path saga clásico execute() sin SPLIT_EXECUTE — más RecargaquiProvider.queryStatus, que no lo exponía y volvía el fix inerte en prod. Sin ese dato la recarga cobrada queda succeeded sin id con que cruzarla contra TBL_REYVA_TURBOCARGA = huérfana irreparable POR CONSTRUCCIÓN. Sin DDL (columna 0005 ya existía, campos del puerto ya opcionales); filtro de audit trail outcome IS NULL intacto
  • Kill-switch operacional de emergencia: gate en POST /v1/recharges (503 si activo, cubre bot + monitor-sweep, NO afecta conciliación/refund) + flag Redis recharge:killswitch (caliente, sin re-deploy, fail-safe al leer) + endpoints admin pause/resume/status (superadmin) + métricas + runbook real
  • Consumer monitor_recharge_succeeded → correo auto-recarga (enriquece correo del vendedor/sub vía bridge + monto local por recharge_id, emite reyva.notification.requested channel=email, idempotencia correlation_id=monitor_id, fail-silent; deliver=new sobre REYVA_CONVERSATION)
  • Hardening prod del consumer de correo (specs/005): retry con backoff solo para db_error en el lookup de monto (3 intentos; not_found no reintenta) + causa raíz encadenada (err.cause) en WARN; desplegado a prod con cpu_always_allocated
  • GET /v1/recharges/:id read-only para reconciliación de monitores atascados (fix carrera monitor-recharging §9 P1): status colapsado succeeded/failed/pending (pending_reconciliation/escalated→pending), 404 si no existe, sin efectos ni money-path; grant Recargas.Recarga.Ver a service:svc-conversation vía SERVICE_GRANTS (least-privilege); un failed con cobro capturado (recharge_attempts.outcome=success) reporta pending, nunca failed
Backend Ola 1 · MVP svc-conversation

Bot de WhatsApp

🔄

Bot conversacional sobre WhatsApp Business API (Meta). Procesa mensajes entrantes, valida firmas de webhook, y orquesta flujos de venta vía estados. Modular: cada flujo (recargas, soporte, ventas) es un módulo independiente.

Avance 20/26 (77%)
  • Bootstrap svc (PR-A): scaffold Nest+Fastify + pgSchema conversation + Redis dedupe/rate-limit + NATS publisher reyva.conversation.* + BullMQ queue process-message
  • Webhook Meta (PR-B): HMAC fail-closed + anti-replay timestamp+dedupe + phone rate limit + ack optimista <200ms p99 (ADR-0029)
  • Domain state machine (PR-C): aggregate FSM + value objects + ports (LineValidator/Vinculacion/Recharge/Sender) + stub adapters (ADR-0015)
  • Orchestrator (PR-D): svc-recharge client HMAC trust token + Meta outbound sender HSM templates + ConversationOrchestrator + timeout sweep
  • Hardening (PR-E): observabilidad Prometheus + rate limit Traefik + security tests + runbook rotación Meta App Secret
  • Tests integración + e2e end-to-end con Meta Cloud API real
  • Endpoints admin chat-sessions + bot-settings (P3/P4/P11) con PermissionGuard + InternalRequestGuard de @reyva/shared-infrastructure (ADR-0055 PR-I6) + tabla bot_settings singleton + publisher reyva.conversation.bot_settings_changed
  • docker-compose dev incluye svc-conversation (HMAC compartido con identity para reverse-proxy admin)
  • Catálogo plantillas HSM (P9/P10): schema conversation.hsm_templates + endpoints admin GET /templates [+:id] + seed mock 3 plantillas (recarga_exitosa, recarga_fallida, linea_no_vinculada) hasta entrega cliente
  • Migración a PermissionGuard + InternalRequestGuard de shared-infrastructure (ADR-0055 PR-I6): elimina TrustTokenGuard local duplicado
  • Traefik IP rate limit en docker-compose dev via labels (conv-ratelimit, avg=30/min, burst=50) — audit P0-4 (ADR-0029 §5)
  • Respuestas in-session del bot como texto libre, no HSM (Regla #13): bot 100% user-initiated → send_text dentro de ventana 24h; gate CI check-template-usage impide regresión
  • Monto de recarga server-authoritative desde el inventario (ADR-0015 addendum §A4): elimina el prompt de monto (AWAITING_AMOUNT) — la SIM se vende con su recarga ya definida (modelo venta-de-SIM, SCOPE §3.1/§3.2)
  • Acuse de vinculación verificada en el happy path (ADR-0015 addendum §A5.2): el bot confirma "línea verificada y vinculada" antes del prompt de confirmación — cierra el gap de feedback reportado por pilotos (send_text, Regla #13)
  • Reconciliación de monitores de auto-recarga atascados en recharging (fix carrera publish-antes-de-respuesta, cierra deuda §9 P1): nak acotado en el consumer + reconciliación read-only del sweep vs GET /v1/recharges/:id de svc-recharge + guard del TTL (STUCK_RECHARGING) — un monitor cuya recarga cobró nunca se marca "no vinculada" por perder el evento NATS
  • Corte diario de pendientes de monitoreo — US1/MVP (feature 015): Job batch espejo del monitor-sweep que lee las líneas que NO terminaron en recarga exitosa, las agrupa por destinatario y encola UN email por vendedor. El correo presenta 2 CATEGORÍAS de negocio, no 3 estados ("No se vinculó" = active+expired, "La recarga falló" = failed). Idempotencia en 2 capas (advisory lock con key propia anti-solape concurrente + tabla daily_digest_sent anti-duplicado secuencial) + aislamiento de fallas por destinatario. El resolver de correo es un stub hasta US2 → NO se envía ningún correo todavía (gate anti-big-bang); done cuando US2 cablee el RPC del bridge y se valide runtime
  • Wake-on-demand del puente legacy (feature 010 US1, cierra gap residual #745): ping OIDC fire-and-forget al mensaje entrante + retry único ante no-responders con espera acotada; no-op estructural en prod (WAKE_TARGET_URL ausente) — done al validar runtime en staging (SC-004)
  • Mensajes de error diferenciados por clase de causa (feature 010 US3, contrato mensajes-error.md): operadora caída (provider_unavailable/breaker US2 abierto) → texto contractual con nombre de operadora del contexto; transitorio/infra → genérico con reintento; gate SC-006 del catálogo (denylist de términos internos + registro estructural) — done al validar runtime con piloto (quickstart US3)
  • Acuse de recibo al validar la línea (#791, addendum ADR-0015 §A5): send_text incondicional antes de check_vinculacion + typing indicator extendido a ese estado — elimina los 15-17s de silencio bajo WSNED degradado que llevaban al vendedor a reenviar/duplicar. Corrige la premisa caducada del ADR ("la validación resuelve síncrona"). Ambas capas incondicionales (texto libre, Regla #13) — done al validar runtime con tráfico real
  • El bot deja de inventar la razón de un rechazo: dos motivos que se corrompían entre capas. (1) El motivo por el que una línea no está vinculada llegaba filtrado por una lista fija —lo que no reconocía se descartaba en silencio y aguas abajo se rellenaba con "sin vincular"—, así que un motivo nuevo no dejaba un hueco visible: se convertía en otro motivo, indistinguible del verdadero. Ahora el motivo se clasifica sin descartarse (presente / conocido / desconocido con su texto crudo) y un motivo que el bot no nombra se registra COMO desconocido. (2) De las 12 causas de rechazo que el servicio de recargas devuelve, una —el usuario no está dado de alta como vendedor— no estaba contemplada y salía como "intenta de nuevo en un momento": un rechazo permanente vestido de transitorio, que mandaba al vendedor a reintentar algo que no se arregla reintentando. Ahora tiene su mensaje propio, que dice a quién acudir. Una prueba lee el catálogo REAL del servicio de recargas y falla si aparece una causa que el bot no distingue — la reincidencia deja de depender de que alguien recuerde actualizar la lista
  • Mensaje de fallo de recarga diferenciado por causa (feature 012 FR-014, contrato mensajes-v2 §9): el adapter dejó de colapsar 7 causas de svc-recharge (6×422 + 502) en un solo business_rule_violation — el bot ya no dice "intenta de nuevo" cuando reintentar es imposible (SIM ya recargada, límite diario). Catálogo completo obligado por el compilador (Record total → agregar causa sin mensaje rompe el build). Todas las causas son pre-cobro o cargo declarado fallido → "no se realizó ningún cargo" es verdad; el caso ambiguo queda fuera (texto libre, Regla #13) — done al validar runtime con piloto
  • Drain-Job del consumer CAPA 4 (feature 008 conversión #3, T015-T017): drain-recharge-result-job drena el durable recharge-result por fetch() acotado (snapshot-y-salir, ADR-0061) para habilitar el flip cpu_idle sin estrangular el cierre de monitores (clase #651); orden sweep→drain garantizado por offset de fase en el cron (*/3 vs 1-59/3, E1 — no grace-window); test adversarial SC-002 que rompe si se reintroduce ACK dependiente de timing; nak acotado del #691 conservado — autoría entregada, apply/flip (T018) gateado por G1/G2/G3+gate 24h
  • Gate de autorización del bot por tabla vía HTTP (feature 014 PR-2, #883): el gate consulta legacy-primero y, ante no-reconocido, resuelve empleado por HTTP a svc-identity (POST /internal/employees/resolve-for-recharge) en vez del RPC NATS; identity-resolve.client.ts mapea 13 modos de error → unavailable (fail-closed, cero cae a legacy); elimina la lista PII employeePhoneSet + el flag EMPLOYEE_RECHARGE_RESOLUTION + la invocación RPC. Una sola forma de resolver (legacy→HTTP identity)
  • Copy conversacional v2 (feature 012 US2, contrato mensajes-v2 §2-§5): comprobante de éxito estructurado (monto/línea/operadora/folio/fecha en *negrita* nativa, escaneable <5s SC-002) + mensajes de error con anatomía de 4 preguntas + vía de escape interpolable (contacto_soporte) + aviso ambiguo honesto sobre el dinero + saludo con propósito. Detección TEMPRANA de "línea ya en monitoreo" (#879): findActiveByDn reusa la clave del índice único anti-dup → el bot ya no re-ofrece monitoreo a una línea con monitor vivo. Gate copy-catalog (checklist 4 preguntas + denylist términos internos SC-006). Todo send_text in-session (Regla #13) — validado runtime con piloto real en staging (2026-07-24): comprobante de éxito (recarga succeeded, saga completa) + error por causa (sim_not_found con las 4 preguntas) confirmados por WhatsApp real
  • Identidad del principal EMPLEADO persistida en el monitoreo de vinculación (#946 Fase 1, migración 0010): el monitor acepta las DOS representaciones ortogonales del actor — eje legacy (vendedor/subdistribuidor) o principal_user_id (UUID de identity.users) — porque el empleado recarga por ownership-BYPASS vía RBAC y legítimamente NO tiene eje legacy. Antes la tabla solo modelaba la representación legacy: el monitor del empleado nacía sin identidad auditable y el guard NO_OWNERSHIP del sweep lo descartaba semanas después sin cobrar (2.092 recargas de SIMs YA vinculadas perdidas; las 8 cuentas afectadas ≡ exactamente el canal EMPLEADO). El fix propaga el UUID que ya existía en el context de la sesión (aggregate → side effect → repo), acota el guard al actor que le corresponde SIN aflojarlo (un monitor legacy sin actor sigue muriendo fail-closed) y firma el cobro con el canal employee del trust token. CHECK XOR con ELSE FALSE como gate estructural: un kind de principal nuevo sin rama declarada hace FALLAR el INSERT en vez de nacer mudo. 4 vectores de reversión verificados en rojo; CHECK validado contra Postgres real en DB throwaway con doble corrida (7 casos: 3 pasan, 4 rechazados)
  • Entrega confiable de las respuestas al vendedor: un envío que falla por una intermitencia se reintenta con espera creciente, y respeta el ritmo que la mensajería pide cuando pide esperar. Antes el fallo se anotaba y se descartaba —el turno se cerraba como exitoso y el mensaje se perdía sin recuperación posible, porque el bot solo puede escribir mientras la conversación siga abierta—. Cubre los siete puntos de salida, incluidos los dos del dinero: el aviso de cobro en duda y el del paso previo a cobrar. El aviso a quien no está registrado queda deliberadamente sin reintento (vive bajo la cuota que frena la enumeración de números). Un envío que agota sus intentos se cuenta y se registra como error desde los siete puntos —antes solo desde uno, y por eso tres días de mensajes sin entregar no se vieron desde afuera—, bajo el guardián que compara los avisos del código contra los filtros que los vigilan
Backend Ola 1 · MVP svc-legacy-bridge

Bridge Legacy (MySQL ACL)

Anti-Corruption Layer (ACL) read-only hacia el MySQL legacy de REYVA. Único componente con acceso al legacy; traduce schema legacy al modelo nuevo de la plataforma. NO maneja TurboCarga (vive en svc-recharge) ni vinculación con operadora (vive en svc-vinculacion).

Avance 11/11 (100%)
  • Bootstrap PR-A (scaffold Nest+Fastify+Drizzle, health + métricas, schema legacy_bridge.bridge_sync_state, Dockerfile + compose)
  • PR-B: MySQL pool (charset utf8) + RPC subdistribuidor/vendedor + mapping operador/estatus/sim + cache Redis + TrustToken
  • PR-C: RPC handlers inventario (find_by_dn + is_assigned con filtro dn != 0)
  • PR-D: scheduler full-scan + diff contra snapshot Postgres + publisher reyva.legacy.*.changed (JetStream + dedup por content_hash)
  • PR-E: hardening (mysql_user_is_read_only probe + circuit breaker + runbook)
  • Tests unit (96/96 verdes — adapter + use-cases + sync + hardening)
  • S5.3: SyncVendedoresJob (snapshot vendedor + cron 5min + publisher reyva.legacy.vendedor.changed) — habilita listado vendedores en panel "Contactos del bot"
  • S5.2: endpoint admin POST /v1/admin/sync/replay-subdistribuidores (backfill one-shot 58k subdistribuidores → events `reyva.legacy.subdistribuidor.changed` para hydrate business_subjects en svc-identity tras S5.1)
  • S5.4: POST /v1/admin/sync/replay-vendedores (TrustTokenGuard HMAC) — backfill on-demand re-publica events para los 633 vendedores cuando el consumer downstream arranca tras dedup window
  • ADR-0058: conector write segregado (GRANTs ⊆ INSERT TURBOCARGA + UPDATE INVENTARIO, guard legacy_write_grants_compliant) + 4 RPCs de reserva pesimista (inventario.reserve/release, turbocarga.register, sku.resolve) — anti doble-cargo bot↔portal
  • Fix contención del gate de admisión: pool MySQL SEGREGADO para los scans del cron (LEGACY_MYSQL_SYNC) → el sync 5-min deja de competir por las conexiones del RPC resolve_for_auth; timeout del gate 2000→3000ms + Redis connectTimeout 3000→1500ms (cabe en budget)
Backend Ola 2 svc-inventory

Inventario

🔄

Lectura read-only del inventario REYVA legacy. Verifica que cada línea consultada por el bot pertenezca al inventario activo de REYVA antes de procesar cualquier recarga.

Avance 12/15 (80%)
  • Esquema DB y migraciones
  • Bootstrap MVP (mock provider determinístico, GET /v1/inventory/lines/:dn, TrustToken, audit log phone_hash SHA-256)
  • API REST/gRPC implementada
  • Schema inventory.sims (status enum available→reserved→sold + reserva TTL, Ola 2)
  • Endpoints POST /v1/sims/:id/{reserve,unreserve,mark-sold} consumidos por saga (Ola 2)
  • GET /v1/sims/by-phone/:phone lookup local (Ola 2)
  • GET /v1/sims?status=sold&since= paginado cursor-based (ADR-0050 §6 — base para anomaly detection svc-recharge)
  • LegacyBridgeInventoryProvider reemplaza mock (ADR-0013, Ola 2)
  • RPC subjects en svc-legacy-bridge reyva.legacy.inventario.* (PR-B mergeado)
  • Job liberación reservas expiradas BullMQ @Cron cada 5min + evento NATS reservation_expired (Ola 2)
  • Lectura read-through vía svc-legacy-bridge
  • Tests integración contra réplica MySQL legacy (Ola 2)
  • Tests integración + e2e
  • Deploy staging
  • Deploy producción (build prod-only, sin canary: no es servicio público del LB)
Backend Ola 2 svc-vinculacion

Vinculación con Operadoras

🔄

Verificación de vinculación con AT&T, Movistar y Unefon. Consulta a cada operadora si una línea está vinculada con su titular (requisito CRT 2026). Solo retorna sí/no — no almacena datos personales (LFPDPPP, ADR-0027).

Avance 19/24 (79%)
  • Scaffold NestJS + estructura hexagonal + value objects
  • Puerto VinculacionProviderPort + registry por operadora (ADR-0051 §1)
  • Use case check-vinculacion con métricas + emisión audit
  • TrustTokenGuard HMAC + controllers HTTP
  • Adapter AT&T scaffold con payload getClientesBiometricos
  • Esquema vinculacion.query_log extendido (result/reason/elapsed_ms/saga_id) + retención 5y (ADR-0027, Ola 2)
  • Endpoint admin GET /v1/admin/vinculacion/checks (RequirePermission Verificar, Ola 2)
  • Job de retención 5y + migración phone_hmac HMAC-SHA256 (ADR-0027 addendum 2026-05-29, S2.11)
  • Spike AT&T: validar auth real contra sandbox/prod (Ola 2)
  • Spike Movistar: descubrir response shape contra QA (Ola 2)
  • Adapter AT&T real WSNED (getClientesBiometricos, parseo robusto jsonResponse + 408, portado del legacy)
  • Adapter Movistar real (GET enrolamiento, parseo enrolado, credenciales por env)
  • Adapter Unefon real (reusa cliente WSNED de AT&T, operator propio)
  • Credenciales/endpoints de operadoras por env (Secret Manager en prod, fail-fast prod+real, .env.legacy gitignored en dev)
  • Spike AT&T/Movistar: validar auth real contra sandbox/prod (requiere credenciales)
  • Política fail-closed validada con tests (ADR-0051 §3)
  • Fix P0 mapStatusVinculacion: "NO EXITOSA" ≠ linked (substring bug) + test adversarial fail-closed
  • Métricas + alertas unknown_rate > 5% por operador
  • Integración saga svc-recharge paso 2 CHECK_VINCULACION
  • Tests integración + e2e contra sandbox/QA
  • Circuit breaker por operadora en la validación de vinculación (feature 010 US2: corto-circuito tras N timeouts, half-open pass-through, métricas de estado/transiciones)
  • Deploy staging (vinculación REAL en el bot: STUB_VINCULACION=false, providers de operadora reales)
  • Deploy producción (build prod-only, sin canary: no es servicio público del LB)
  • Calendario oficial de vinculación por último dígito (DOF 30-jun-2026): el servicio decide sí/no con sus propias reglas, sin que quien pregunta conozca las fechas (ADR-0051 §A11)
Backend Ola 1 · MVP svc-audit

Auditoría Inalterable

🔄

Trazabilidad interna de REYVA: registro permanente e inalterable de cada operación del sistema (recargas, validaciones, cambios de permisos, accesos, configuración). Cubre módulos actuales y futuros. Hash chain criptográfico + WORM en Cloud Storage (ADR-0033) — nadie, ni siquiera un administrador, puede borrar o alterar registros sin que sea detectable.

Avance 21/22 (95%)
  • Esquema DB y migraciones
  • Hash chain HMAC + función append_event (ADR-0033 §1)
  • Idempotencia exactly-once por envelope_event_id (ADR-0028)
  • Verificación periódica de tampering (ADR-0033 §1.4)
  • API POST /audit/events
  • API GET /audit/events con filtros + paginación cursor-based
  • Auth s2s ADR-0026 (X-User-Trust-Token)
  • Export periódico a Cloud Storage WORM (ADR-0033 §2)
  • Métricas Prometheus + endpoint /metrics
  • Consumer reyva.identity.access (JetStream durable + Nats-Msg-Id dedup)
  • Consumer reyva.identity.permission_changed: JetStream durable + Nats-Msg-Id dedup → hash chain (ADR-0055 FU-3)
  • Tests integración + e2e (87 con docker-compose: postgres+nats)
  • Cloud Run staging deploy (Fase 0)
  • Application layer migrado a Result<T, E> (errores tipados, sin throw en use-cases/repos)
  • docker-compose dev incluye svc-audit (Dockerfile dev tsx watch, puerto 47412, HMAC compartido con svc-identity para reverse-proxy P12)
  • PII en payload hasheada/truncada (ip_hash + user_agent_truncated, audit P1-3 / ADR-0033 addendum 2026-05-29)
  • Rotación trimestral de signing keys (cron 90d + NATS reyva.audit.signing_key_rotated, audit P0-4 / ADR-0033 §rotación)
  • Deploy producción (Fase 2): bucket WORM reyva-audit-worm-prod retención 5a + export-to-worm + consumers de REYVA_IDENTITY + pantalla /admin/auditoria (Patrón B)
  • Audit-on-write de empleados: EmployeeChangedConsumer persiste reyva.identity.employee_changed en la hash-chain WORM (resource_kind=identity_employee). Cierra el simétrico del audit-on-read de PII — aquél registra quién MIRÓ el teléfono del empleado, éste quién lo CAMBIÓ (el dato que dirige a quién le cobra el bot). El publisher declaraba este consumer desde PR-A y no existía
  • Auditoría de accesos al MySQL legacy: LegacyAccessConsumer persiste reyva.legacy.access (stream REYVA_LEGACY, el único consumer de svc-audit fuera de REYVA_IDENTITY) como resource_kind=legacy_rpc, con decision=deny derivada de los outcomes unauthorized/denied. Al conectarlo drenó 23 mensajes acumulados desde el 2026-08-06 que iban a expirar sin auditar
  • Lock del bucket WORM (is_locked=true) tras validar ventana lock-later
  • Worker de proyección always-on (minScale=1 + CPU always-allocated) — el trail de auditoría no deja de ingerir accesos/permisos (Constitution Principio VI, compliance LFPDPPP)
Backend Ola 4 svc-monitoring

Monitor en Vivo y Alertas

🔄

Pantalla en tiempo real de la operación (recargas hoy, bloqueadas, fallidas por operador), alertas inteligentes de negocio (operador caído, distribuidor con fallas, tasa de vinculación anómala) y paneles técnicos para soporte.

Avance 11/13 (85%)
  • Scaffold NestJS + Fastify + health probes (BFF stateless, ADR-0057)
  • Cliente Prometheus API + consumer NATS read-only (ADR-0057)
  • Métricas de negocio en tiempo real
  • Reglas de alerta inteligente (SLOs ADR-0056)
  • Alertas de negocio (operador caído) + publish reyva.monitoring.alert_triggered (ADR-0057 §5)
  • Alerta no_traffic con horario operativo + heartbeat scheduler (ADR-0057 addendum)
  • Pantalla /admin/monitor en el panel (Patrón B vía svc-identity, ADR-0057)
  • Integración con Grafana (dashboard svc-monitoring + scrape target)
  • Tests integración + e2e
  • Deploy staging
  • Deploy producción (recharge-live por NATS; monitoreo técnico SLO diferido a Cloud Monitoring)
  • Monitoreo técnico SLO en prod vía Google Cloud Monitoring (trigger: módulo infra-observability)
  • Worker de fondo always-on (minScale=1 + CPU always-allocated) — el consumer recharge-live no escala a cero, el pulso en vivo no se detiene (Constitution Principio VI)
Backend Ola 3 svc-history

Historial de Recargas

🔄

Consulta histórica de cada recarga ejecutada vía bot: número, timestamp, operador, autor, resultado y transacción. Filtros por fecha, operador y punto de venta. Exportación a Excel/CSV.

Avance 15/16 (94%)
  • Scaffold NestJS + Fastify + health probes + bootstrap
  • Esquema DB history.recharges + processed_events + migración (ADR-0052 §2.1)
  • Consumer NATS proyección reyva.recharge.* con dedupe por event_id (ADR-0052 §1)
  • API REST GET /history/recharges con filtros + paginación
  • Exportación CSV/Excel streaming (límite 50k filas, ADR-0052 §2.1)
  • Métricas Prometheus read_model_* (lag, processed, duration) (ADR-0052 §5)
  • Job retención nocturno + pseudonimización PII >5 años (ADR-0052 §4)
  • Fail-fast HISTORY_ERASURE_SALT en prod-like — retención LFPDPPP sin fallback silencioso (#777)
  • Tests unit consumer (mapeo + dedupe + outcomes)
  • Auth s2s (reverse-proxy svc-identity → svc-history + enrichment display_name)
  • Pantalla panel /admin/historial (tabla filtrable + export CSV/Excel)
  • Test rebuild desde NATS — invariante idempotencia (ADR-0052 §3)
  • Worker de proyección always-on (minScale=1 + CPU always-allocated) — no escala a cero (Constitution Principio VI)
  • Tests integración consumer + e2e proyección end-to-end
  • Deploy staging
  • Deploy producción
Backend Ola 3 svc-reports

Reportes Ejecutivos

🔄

Resúmenes automáticos diarios, semanales y mensuales en PDF. Comparativas contra periodos anteriores. Envío automático por email a stakeholders configurados, sin necesidad de entrar al sistema.

Avance 8/10 (80%)
  • Servicio read-model on-demand (registry + serializers Excel/PDF)
  • Reportes semilla: desempeño por distribuidor + resumen diario
  • Evento reyva.reports.generated a NATS (emisión a stream propio REYVA_REPORTS, 30d). Trazabilidad de "quién generó qué reporte" sin persistir el artefacto; AÚN SIN CONSUMER — svc-notifications de Ola 4 lo consumirá para el email (ADR-0052 §Notas svc-reports). Decía "(auditoría)", que prometía una persistencia en el audit trail WORM que no ocurre
  • Tests unitarios (registry, serializers, transformaciones)
  • Auth s2s (reverse-proxy svc-identity → svc-reports con trust token)
  • Pantalla panel /admin/reportes (selector plantilla + filtros + descarga Excel/PDF)
  • Schedule via BullMQ (ADR-0024) — Ola 4
  • Envío email vía svc-notifications — Ola 4
  • Deploy staging
  • Deploy producción
Backend Ola 3 svc-distributors

Desempeño por Distribuidor

🔄

Vista de actividad por punto de venta o subdistribuidor: recargas hechas, monto, tasa de éxito, última actividad. Distingue subdistribuidor vs vendedor (rol de primera clase) y el status administrativo (active/inactive/suspended).

Avance 15/17 (88%)
  • Scaffold NestJS + Fastify + health probes + bootstrap
  • Schema DB (subject_dim + kpi_snapshots + recharge_ref + processed_events) + migración (ADR-0052 §2.3)
  • Consumer NATS agregación KPIs por (distribuidor, día, fuente) + tabla puente para tasa de éxito
  • El padrón de vendedores/subdistribuidores llega DIRECTO del puente (sin escala en svc-identity) y se consulta por TELÉFONO
  • Distinción subdistribuidor/vendedor: la clase viaja en el evento (la dice su subject), ya no se infiere best-effort
  • Columna source extensible (whatsapp_bot + futuras fuentes sin migración)
  • API REST GET /distributors/ranking + /:id/kpis con filtro por rol
  • Métricas Prometheus read_model_* + tests unit consumer
  • Rebuild desde NATS (script rebuild-from-nats + test de integración del invariante, ADR-0052 §3)
  • Auth s2s (reverse-proxy svc-identity → svc-distributors con trust token)
  • Pantalla panel /admin/distribuidores (ranking con filtros + semaforización)
  • Tests e2e (panel admin Inteligencia de negocio)
  • Contador vinculacion_blocked (requiere evento reyva.vinculacion.checked, pendiente)
  • Deploy staging
  • Deploy producción
  • Worker de proyección always-on (minScale=1 + CPU always-allocated) — no escala a cero, la proyección del read-model no se congela (Constitution Principio VI)
  • Backfill kpi_snapshots business_subject → legacy_actor aplicado en prod (migración 0003 + re-atribución 0013 en recharge; cierre de los ceros del panel)
Backend Ola 4 svc-notifications

Notificaciones Proactivas

🔄

Avisos automáticos por email a puntos de venta, distribuidores y dirección (recarga ejecutada por monitoreo, línea no vinculada a tiempo, corte diario de pendientes, fallas de operador, recordatorios). Mensajes masivos / por segmento y notificaciones configurables. WhatsApp se reserva al bot de venta.

Avance 18/23 (78%)
  • Scaffold NestJS + Fastify + health probes + bootstrap
  • Esquema DB notifications.templates + migración (ADR-0053)
  • Sync de estado de plantillas desde Meta (consumer NATS + Graph API) + endpoint admin
  • Panel admin reapuntado a svc-notifications (rejection_reason real, sin mock)
  • Sync automático de plantillas (scheduler 6h + botón real + last_synced_at visible, ADR-0053)
  • Plantillas WhatsApp aprobadas por Meta
  • Canal email: puerto EmailSender + adapter Gmail API (Domain-Wide Delegation keyless) + worker despacha por canal (flag EMAIL_SEND_ENABLED)
  • Canal email: catálogo de plantillas en código (subject+html+text) + plantilla recarga_monitor_completada (comprobante de recarga)
  • Canal email: DWD del SA viva (Gmail API + SPF/DKIM/DMARC + binding serviceAccountTokenCreator) + envío real verificado
  • Go-live prod del canal email (specs/005): infra Terraform dedicada (servicio privado + DWD prod + 7 migraciones), gate OFF validado (suppressed_flag) → flag ON, primer comprobante real entregado e idempotencia verificada con tráfico real (2026-07-09)
  • Consumidor monitor recarga OK → email al vendedor (svc-recharge enriquece correo+monto y emite notification.requested channel=email; envío gated por canal email)
  • Consumidor monitor expirado → email al vendedor
  • Corte diario por vendedor (pendientes de monitoreo) → email (feature 015): plantilla `corte_diario_pendientes` con las 2 categorías de negocio del correo ("No se vinculó" = active+expired, "La recarga falló" = failed) + política de re-envío `no-resend`. El Job que lo dispara vive en svc-conversation. done cuando US2 resuelva el correo real del vendedor/sub por RPC del bridge — hoy el resolver es un stub y NO se envía ningún correo (gate deliberado anti-big-bang)
  • Envío de reportes svc-reports por email a dirección
  • Worker de envío BullMQ + opt-out + error policies (ADR-0053, flag META_SEND_ENABLED)
  • API REST POST /send + consumer NATS reyva.notification.requested
  • Emisor recarga_fallida_retry (svc-recharge → reyva.notification.requested, fuera ventana 24h)
  • Guardia de idempotencia del envío por correlation_id (ADR-0028 §3.2): no reenvía tras reintento interno de BullMQ post-lock-loss
  • Worker refactorizado a registry de canales + pipeline de behaviors (opt-out/idempotencia/emisión): agregar un canal = 1 handler + 1 binding, sin tocar el orquestador (ADR-0053)
  • Recuperación automática de envíos huérfanos + auditoría de aceptación del proveedor (feature 009)
  • Tests integración + e2e
  • Deploy staging
  • Deploy producción + canary

Aplicaciones frontend

Interfaces para administradores, clientes y socios.

Frontend Ola 1 · MVP web-admin

Panel de Administración

Panel administrativo en Next.js 16 + React 19. Vista para subdistribuidores B2B y operadores internos. Maneja invitaciones, conversaciones, recargas y auditoría.

Avance 27/27 (100%)
  • Bootstrap Next.js 16 + React 19
  • Sistema de diseño (tokens shadcn)
  • NextAuth v5 + Keycloak provider + middleware admin
  • Proxy /api/identity/* + API client + TanStack Query hooks (PR-H5 C4)
  • AppShell + RolesTable read-only (8 Composite Roles del catálogo) + lista /admin/permissions/roles (ADR-0055 PR-I7)
  • CompositeRoleViewer read-only + PermissionsTreeEditor checkbox del catálogo typed (ADR-0055 PR-I7)
  • DeleteRoleConfirm modal + lockout-prevention frontend (PR-H5 B)
  • Lista /admin/permissions/users + UserRolesAssigner (PR-H6)
  • P12 Audit log: /admin/audit con filtros + paginación cursor (reverse-proxy a svc-audit)
  • P2 Dashboard del bot cableado a datos reales (KPIs sessions+messages 24h + histograma sesiones/hora + recent closed)
  • P3 Lista de conversaciones (cursor-based + filtros estado FSM + reverse-proxy a svc-conversation)
  • P4 Detalle de conversación + TranscriptViewer (chat bubbles + timeline FSM merged + latency badges)
  • P5 Lista de business_subjects (con enrichment teléfono vía svc-legacy-bridge)
  • P6 Detalle/edición de business_subject + AttributeEditor (CRUD versionado de user_attributes, edit tier/status)
  • P7 Lista pending provisioning (invitaciones) con acciones inline expirar/renovar
  • P8 Crear pending user (form invitación con validaciones tipo+roles+atributos+expiry)
  • P9 Lista plantillas WhatsApp (/admin/templates con tabs APPROVED/PENDING/REJECTED + search + reverse-proxy svc-conversation)
  • P10 Vista previa de plantilla (drawer modal con WhatsApp canvas + placeholders {{1}} resaltados + JSON debug)
  • P11 Configuración del bot (timeout/rate-limit/operadores activos editables runtime + publisher reyva.conversation.bot_settings_changed)
  • Playwright E2E suite robusta (17 specs cubriendo P2-P12 + roles + login + admin-shell; mock-first + seeds reales; edge cases + error states + filtros + paginación)
  • CASL frontend (@casl/ability + @casl/react): useAbility/getAbility hooks desde JWT realm_access.roles sin HTTP (ADR-0055 PR-I7)
  • AdminSessionProvider: SessionProvider wrapper next-auth para componentes client (ADR-0055 PR-I7)
  • Hooks re-cableados a Keycloak Admin API: use-roles, use-role, use-user-roles, use-assign-role, use-revoke-role (ADR-0055 FU-1)
  • Suite E2E Playwright 116 passed + 5 skipped (admin-dashboard, bot-settings, templates, audit, business-subjects, users, roles, users-assign refactored) (ADR-0055 FU-2)
  • PII masking subdistribuidores (audit P0-3 S2.4): phones masked por default, botón Mostrar dispara reveal + emisión del evento reyva.identity.pii_revealed (ADR-0027 §6 LFPDPPP)
  • Audit-on-read de PII persistido: consumer en svc-audit de reyva.identity.pii_revealed → hash-chain WORM (ADR-0033). Cierra el hito anterior, que emitía el evento sin que ningún consumer lo leyera
  • Modal warning consent en /admin/conversations/[id] antes de cargar transcript (sessionStorage por session_id) + redactPii helper en AuditEventsTable + ConversationDetail context (audit P1-3 S2.4)
Frontend Ola 1 · MVP status

Portal de Estado (este sitio)

Portal público de estado y progreso de la plataforma. Esta página. Sirve primero como dashboard de construcción, después como dashboard operacional con uptime y latencia por servicio.

Avance 6/6 (100%)
  • Bootstrap Astro 6
  • Sistema de diseño
  • Vista de progreso de construcción
  • Vista operacional (uptime + latencia)
  • Historial de incidentes
  • Deploy producción (status.reyva.mx, LB + cert SSL separado Opción C)

Infraestructura compartida

Componentes transversales que sirven a todos los servicios.

Infra Ola 1 · MVP infra-auth

Autenticación (Keycloak)

🔄

Stack de autenticación: Keycloak 26 con realm Reyva pre-configurado, OIDC para usuarios humanos, Google Identity para SSO. Servicio compartido por todos los servicios backend vía Traefik forwardAuth.

Avance 4/6 (67%)
  • Keycloak 26 en Docker Compose
  • Realm reyva con clientes
  • Realm reyva seedado con 32 permisos atómicos + 8 Composite Roles (ADR-0055 PR-I1)
  • Script permissions-seed.ts idempotente (Admin API) + check-permissions-sync CI blocking (ADR-0055 PR-I1+PR-I2)
  • Google OIDC configurado
  • Traefik forwardAuth integrado
Infra Ola 1 · MVP infra-events

Bus de Eventos (NATS)

Bus de mensajes NATS JetStream. Comunicación asíncrona entre servicios vía pub/sub con persistencia y semántica exactly-once. Versionado de eventos con esquema evolucionable.

Avance 6/6 (100%)
  • NATS 2.14 con JetStream local
  • Subjects y namespacing definidos
  • Envelope estándar + versionado (event_id + Nats-Msg-Id dedup)
  • Stream REYVA_IDENTITY con duplicate_window 2min
  • Consumer durable svc-audit-identity-access-consumer
  • Cliente SDK compartido (@reyva/shared/nats + @reyva/shared/events) — todos los streams REYVA_* registrados
Infra Ola 1 · MVP infra-observability

Observabilidad (OTel + Grafana)

🔄

Stack de observabilidad: OpenTelemetry para traces/metrics/logs en cada servicio. Tempo para traces, Loki para logs, Prometheus para métricas, Grafana como UI única. Self-hosted en compose (lift-and-shift a GCP cuando exista contrato cloud). Audit log con hash chain WORM para compliance.

Avance 14/16 (88%)
  • Health probes spec (live/ready)
  • startup_probe HTTP en Cloud Run (ADR-0037 §3): reemplaza el probe TCP implícito —que solo verificaba que el puerto abriera— por /health/startup, que valida la conexión real a Postgres/Redis/NATS por VPC antes de recibir tráfico
  • Señal conversation_monitor_closed re-semantizada a backlog observable (feature 010 US4, #775): COUNT + MIN(updated_at) sobre monitores en recharging — verde sin exigir tráfico, alerta real solo ante monitor atascado >900s; elimina el falso positivo estructural del branch (c) de la sonda
  • Métricas Prometheus en cada servicio (svc-identity, svc-audit, svc-conversation, svc-recharge, svc-legacy-bridge, svc-vinculacion, svc-inventory)
  • Audit log hash-chain WORM (ADR-0033 §1+§2): append_event + verify_chain + export-to-worm GCS adapter
  • OTel SDK en cada servicio (traces/spans distribuidos) — 7/7: svc-identity + svc-audit + svc-conversation + svc-recharge + svc-legacy-bridge + svc-vinculacion + svc-inventory
  • Stack observability self-hosted en compose (OTel Collector + Tempo + Loki + Prometheus + Grafana) — sustituye Grafana Cloud
  • Dashboards Grafana versionados (legacy-bridge, audit, recharge, web)
  • Alert rules Prometheus versionadas (cross-cutting + per-service)
  • Defense-in-depth counter unificado cross-service (reyva_validation_layer_total) — workflow S4.2
  • Alertas money-path en Google Cloud Monitoring (prod): log-based metrics + alert policies A1/A3/A4/A6 + canal email on-call (módulo infra-observability, ADR-0057 §c)
  • Alerta proactiva por operadora de vinculación caída (A7 breaker abierto CRITICAL + A8 degradación WARNING), log-based sobre el circuit breaker (feature 010 US2) — detección sin depender de vendedores (issue #796, ADR-0064)
  • CAPA 3 del watchdog del drain de notifications (feature 008/T005): log-based metric DELTA/DISTRIBUTION del num_pending por durable + alerta de backlog sostenido — caza "el Job corre pero NO drena", el punto ciego de la sonda sending (CAPA 1) y del watchdog de ausencia (CAPA 2), sin binding IAM
  • Observabilidad del detector de huérfanas del money-path (#847/#848/#849): A9 reapuntada a los 4 strings del detector nocturno (antes solo veía el rescate de saga → no capturaba NINGÚN evento del detector) + A11 (el detector corrió CIEGO: ceguera de 2º grado, fallaba 7 de 30 noches mientras su resumen decía orphan_detected=0 y el latido quedaba verde) + A12 (el Job nunca corrió: 3er grado, condition_absent con REDUCE_SUM para no dar incidente fantasma por revisión). Panel de arquitectura 2026-07-27 BORRÓ 2 piezas del diseño original en vez de agregarlas (Regla #17): el canal digest (creaba un recurso GCP que apuntaba al MISMO buzón del oncall) y la policy A10 (su propia doc decía que el fix es automático → era un correo que pedía no hacer nada; su métrica se conserva para consulta). Gate CI check-anomaly-string-sync ata los literales TS↔HCL — done al aplicar en prod y confirmar ≥1 datapoint real (las log-based metrics no backfillean)
  • Gobernanza de costos cloud: budget GCP con alertas por email (prod/staging) + apagado nocturno del entorno de staging (feature 006)
  • Alerting + on-call rotation — requiere Grafana Cloud + PagerDuty/Opsgenie
Infra Ola 1 · MVP infra-cicd

CI/CD y Deploy

Pipeline de integración y despliegue continuo: GitHub Actions con Workload Identity Federation (WIF) hacia GCP, Cloud Run con canary 10/50/100, migraciones zero-downtime expand/contract.

Avance 5/5 (100%)
  • GitHub Actions: lint/typecheck/build
  • Conventional commits + commitlint
  • Branch protection (husky pre-push)
  • observe-canary.sh + rollback.sh funcionales (audit P0-3)
  • Custom ESLint rule no-zod-parse-at-boundary: bloquea .parse() en boundaries HTTP/NATS (workflow S4.3)

Sobre esta página

Este portal refleja el avance de construcción de la plataforma REYVA y, una vez en producción, la salud operacional de cada servicio (uptime, latencia, incidentes).

Los datos provienen de archivos versionados en el repositorio. Cada módulo tiene un archivo Markdown editable que se actualiza vía Pull Request — la misma cadencia que cualquier otro cambio del código. Especificación en ADR-0035.