From 119daaae8b318c3f59364fe8007da9fd1942b4b2 Mon Sep 17 00:00:00 2001 From: brobert Date: Mon, 29 Sep 2025 00:48:25 +0200 Subject: [PATCH] =?UTF-8?q?a=C3=B1ado=20el=20plan=20multicomunidades?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plan-multicomunidades.md | 219 ++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 docs/plan-multicomunidades.md diff --git a/docs/plan-multicomunidades.md b/docs/plan-multicomunidades.md new file mode 100644 index 0000000..5f45d9a --- /dev/null +++ b/docs/plan-multicomunidades.md @@ -0,0 +1,219 @@ +# 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).