# Plan de habilitación multi‑comunidades con control de acceso Propósito - Permitir que el bot opere en varias comunidades/grupos sin perder control. - Introducir un mecanismo de whitelist (allowed_groups) y un flujo de aprobación administrativa. - Mantener las mismas funcionalidades en todos los grupos aprobados. Resumen de estrategia - Instancia única multi‑comunidad con “gating” por grupo permitido. - Descubrimiento seguro: registrar grupos desconocidos como pending y no operar hasta aprobación. - Comandos/admin y notificaciones para aprobar/bloquear grupos. Estructura del plan por etapas Cada etapa indica objetivos y archivos a tocar. La implementación es incremental y desplegable paso a paso. Etapa 0 — Preparación y criterios Objetivos - Alinear alcance y criterios de aceptación. - Definir nuevas variables de entorno y defaults. Criterios de aceptación - Variables definidas: - ADMIN_USERS: lista de user_ids normalizados con permisos de administración. - ALLOWED_GROUPS: lista inicial (semilla) de group_ids a marcar como allowed. - AUTO_APPROVE_PATTERNS (opcional): patrones de nombre de grupo confiables (con cautela). - Comportamiento por defecto para grupos desconocidos: registrar como pending y no operar. - Decidir si el bot responde (o guarda silencio) en grupos pending/blocked. Archivos a tocar - Documentación (docs/operations.md, docs/architecture.md) — actualización en Etapa 6. Etapa 1 — Esquema y servicio AllowedGroups (sin gating todavía) Objetivos - Crear tabla allowed_groups con estados: pending | allowed | blocked. - Implementar servicio con caché en memoria y API: - upsertPending(groupId, label?, discoveredBy?) → pending - isAllowed(groupId) → boolean - setStatus(groupId, status, label?) → boolean - listByStatus(status) → {group_id, label}[] - seedFromEnv(ALLOWED_GROUPS) - Métricas básicas por estado (gauges). Cambios de datos - Tabla allowed_groups: - group_id TEXT PRIMARY KEY - label TEXT NULL - status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','allowed','blocked')) - discovered_at TEXT NOT NULL DEFAULT now - updated_at TEXT NOT NULL DEFAULT now - discovered_by TEXT NULL Archivos a tocar - src/db/migrations/index.ts: añadir una nueva migración (v9) para crear allowed_groups e índices. - src/services/allowed-groups.ts (nuevo): implementación del servicio con caché y métodos arriba. - src/services/metrics.ts: gauges allowed_groups_total{status} (opcional en esta etapa). - tests/unit/services/allowed-groups.test.ts (nuevo). Etapa 2 — Descubrimiento seguro de grupos (registrar, no operar) Objetivos - Al recibir tráfico de un group_id desconocido: - Insertar/actualizar en allowed_groups con status=pending y label (si disponible). - No ejecutar lógica de negocio (comandos, sync, recordatorios). - Métricas y logs del descubrimiento. Archivos a tocar - src/server.ts: - handleMessageUpsert: antes de cualquier operación de grupo, consultar AllowedGroups. - Si no existe: upsertPending y retornar sin procesar comandos. - Añadir métricas: unknown_groups_discovered_total. - src/services/contacts.ts: - Helper opcional para obtener/actualizar el nombre del grupo si el webhook lo trae (si no, dejar label=null). - src/services/metrics.ts: contador unknown_groups_discovered_total. - tests/unit/server/unknown-group-discovery.test.ts (nuevo). Etapa 3 — Gating mínimo en superficies críticas Objetivos - Bloquear procesamiento de comandos si el group_id no está allowed. - Asegurar que sincronización de grupos/miembros solo opere sobre grupos allowed. - Responder con mensaje breve o permanecer en silencio para pending/blocked (configurable). Archivos a tocar - src/services/command.ts: - Al inicio de processTareaCommand cuando se esté en grupo: consultar AllowedGroups.isAllowed(). - Si no allowed: devolver mensaje breve o [] según política (config). - Sustituir usos de GroupSyncService.isGroupActive por AllowedGroups.isAllowed para el gating de creación de tareas con group_id. - src/services/group-sync.ts: - Filtrar y operar únicamente sobre grupos allowed al sincronizar (scheduler + reconcile). - Donde se cachean grupos activos, cruzar con allowed_groups o cambiar la fuente de verdad a AllowedGroups si procede. - src/server.ts: - En handleMessageUpsert, antes de encolar/ejecutar comandos, exigir allowed en caso de que sea un grupo. - src/services/metrics.ts: - commands_blocked_total - sync_skipped_group_total (si se decide contabilizar grupos saltados por no allowed) - tests/unit/services/command.gating.test.ts (nuevo). - tests/unit/services/group-sync.gating.test.ts (nuevo). Etapa 4 — Flujo de aprobación administrativa y notificaciones Objetivos - Comandos admin: - /admin habilitar-aquí - /admin deshabilitar-aquí - /admin pendientes - /admin allow-group - /admin block-group - Validación de permisos con ADMIN_USERS (IDs normalizados). - Notificación automática a ADMIN_USERS al descubrir un grupo pending (con nombre si se conoce). Archivos a tocar - src/server.ts: - Router para comandos /admin → nuevo servicio AdminService. - Validación de ADMIN_USERS en validateEnv (no obligatorio pero recomendable). - src/services/admin.ts (nuevo): - Implementa los subcomandos y policy de permisos. - src/services/allowed-groups.ts: - Exponer setStatus, listByStatus, maybe get(groupId). - src/services/response-queue.ts o src/services/webhook-manager.ts: - Utilizar ResponseQueue para notificar a ADMIN_USERS por DM cuando se descubra un grupo pending. - tests/unit/services/command.admin-approval.test.ts (nuevo). - tests/unit/services/admin.test.ts (nuevo). Etapa 5 — Integración con servicios dependientes (recordatorios, tareas, rate limit) Objetivos - Recordatorios: si incluyen contexto de grupo, operar solo en grupos allowed; si no, DM sin dependencia del gating de grupo. - Tareas: validar group_id permitido al crear desde un grupo (ya resuelto en Etapa 3). - Rate limit (opcional): aislar por grupo si se desea (clave compuesta groupId+userId). Archivos a tocar - src/services/reminders.ts: - Al componer secciones por grupo (y “sin responsable”), filtrar por AllowedGroups.isAllowed(gid) si aplica. - src/tasks/service.ts: - Mantener la lógica de setear group_id=null si no existe/permitido, delegando la decisión al llamador (CommandService). - src/services/rate-limit.ts (opcional): - Añadir variantes de clave por grupo (ej. `${userId}:${groupId || 'dm'}`) si se activa por env. - tests/unit/services/reminders.gating.test.ts (nuevo). - tests/unit/tasks/service.gating.test.ts (nuevo). Etapa 6 — Observabilidad y operaciones Objetivos - Métricas agregadas y documentación operativa. - Logs claros en cambios de estado y descubrimientos. Archivos a tocar - src/services/metrics.ts: - allowed_groups_total{status}, unknown_groups_discovered_total, commands_blocked_total, admin_actions_total{action}. - src/server.ts: - Confirmar exposición de métricas en /metrics. - docs/operations.md: - Documentar ADMIN_USERS, ALLOWED_GROUPS, AUTO_APPROVE_PATTERNS, comportamiento de pending/blocked, y comandos admin. - docs/architecture.md: - Añadir sección “Control de acceso por grupos (allowed_groups)”. Etapa 7 — Hardening, pruebas de regresión y rollout Objetivos - Pruebas E2E con múltiples grupos en estados allowed/pending/blocked. - Verificación de que no hay rutas sin gating (comandos, schedulers, envíos). - Plan de despliegue gradual: 1) Activar registro (Etapas 1–2) sin gating duro. 2) Activar gating (Etapa 3) con política de silencio o mensaje breve. 3) Habilitar comandos admin (Etapa 4). 4) Ajustar recordatorios/tareas (Etapa 5). 5) Cerrar documentación y métricas (Etapa 6). Archivos a tocar - tests/** (nuevos casos integrados multi‑grupo). - scripts de CI (si aplican) para asegurar la ejecución de nuevas suites. Mapa rápido de archivos por etapa (resumen) - Etapa 1: - src/db/migrations/index.ts (nueva migración v9) - src/services/allowed-groups.ts (nuevo) - src/services/metrics.ts (gauges) - Etapa 2: - src/server.ts - src/services/contacts.ts (opcional) - src/services/metrics.ts (counter) - Etapa 3: - src/services/command.ts - src/services/group-sync.ts - src/server.ts - src/services/metrics.ts - Etapa 4: - src/server.ts - src/services/admin.ts (nuevo) - src/services/allowed-groups.ts (extensiones) - src/services/response-queue.ts (notificaciones a admins) - Etapa 5: - src/services/reminders.ts - src/tasks/service.ts (validación indirecta ya cubierta por llamador) - src/services/rate-limit.ts (opcional) - Etapa 6: - src/services/metrics.ts - docs/operations.md - docs/architecture.md - Etapa 7: - tests/** integración/regresión - CI (si aplica) Variables de entorno previstas (añadidos) - ADMIN_USERS: lista separada por comas de IDs de usuario administradores (normalizados). - ALLOWED_GROUPS: lista separada por comas de group_ids iniciales a permitir. - AUTO_APPROVE_PATTERNS (opcional): patrones para auto‑aprobar por nombre (con cautela). - MAX_MEMBERS_SNAPSHOT_AGE_MS: ya usada; garantizar coherencia en gating si se cruza con membresía. - METRICS_ENABLED, NODE_ENV, TZ: respetadas. Riesgos y mitigaciones - Fuga de operaciones en grupos no permitidos: tests específicos (Etapas 3 y 7). - Identificación de nombres de grupo: si no se dispone de “subject” en eventos, almacenar solo group_id; completar label más tarde desde comando admin o tras un fetch. - Sobrecarga de DB por descubrimiento: caché en AllowedGroups + operaciones idempotentes; escribir solo si no existe o cambió estado/label. Criterios de “hecho” globales - El bot solo opera en grupos con status=allowed. - Flujo de aprobación funciona desde grupo (/admin habilitar-aquí) y por DM (/admin allow-group ). - Métricas y logs permiten auditar descubrimientos y cambios de estado. - Documentación de operación actualizada. Notas de implementación - Mantener compatibilidad hacia atrás: en creación de tareas, si el group_id no está allowed, persistir sin group_id (null). - No bloquear DMs por diseño (gating aplica solo a grupos). - Reutilizar ResponseQueue para todas las notificaciones (incluyendo admins).