Files
agents_and_robots/dev/issues/0024-centralized-security-groups.md
T
egutierrez 0102afa06e chore: cerrar issue 0024b-security-loader
Mueve el issue a completed/ y actualiza el índice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 20:32:23 +00:00

12 KiB
Raw Blame History

0024 — Sistema centralizado de grupos y permisos

Objetivo

Reemplazar los controles de acceso por agente (security.roles, matrix.filters.allowed_users) con un sistema centralizado en una carpeta security/ donde se definen grupos de usuarios, grupos de agentes, y una política de permisos que los vincula. Esto elimina la necesidad de configurar permisos en cada agente individualmente.

Contexto

  • Actualmente cada agente tiene su propio bloque security.roles en config.yaml y matrix.filters.allowed_users en matrix.filters. Añadir un usuario a varios agentes requiere editar múltiples archivos.
  • El módulo pkg/acl/ existe y está completo: resuelve ACLs puras dado un mapa de roles. Lo reutilizamos como motor de evaluación.
  • La nueva capa pkg/security/ se apoya en pkg/acl/ para producir acl.ACL por agente a partir de la política centralizada.
  • La carpeta security/ en la raíz del proyecto contiene los YAML de grupos y permisos. El launcher los carga una vez y distribuye la ACL resuelta a cada agente.
  • Se elimina matrix.filters.allowed_users y security.roles del schema de config de agente una vez que todos los agentes usan la política centralizada.

Dependencias: ninguna (issue autocontenido en 3 fases).

Arquitectura

pkg/security/               NEW — tipos puros + resolución ACL
  groups.go                 NEW — UserGroup, AgentGroup
  policy.go                 NEW — Permission, AgentPolicy, SecurityPolicy
  resolver.go               NEW — ResolveACL(agentID, policy) → acl.ACL
  security_test.go          NEW — tests de resolución

security/                   NEW — configs centralizados (raíz del proyecto)
  user-groups.yaml          NEW — definición de grupos de usuarios
  agent-groups.yaml         NEW — definición de grupos de agentes
  permissions.yaml          NEW — políticas: qué grupos de usuarios tienen qué permisos en qué grupos de agentes

shell/security/             NEW — loader impuro
  loader.go                 NEW — carga los 3 YAML y construye SecurityPolicy
  loader_test.go            NEW — tests con YAML de ejemplo

cmd/launcher/main.go        MODIFIED — carga security/ al inicio, pasa acl.ACL resuelta a cada Agent
agents/runtime.go           MODIFIED — acepta acl.ACL pre-resuelta en lugar de RoleCfg
internal/config/schema.go   MODIFIED — marcar security.roles y matrix.filters.allowed_users como deprecated
agents/assistant-bot/config.yaml   MODIFIED — eliminar security.roles y allowed_users
agents/asistente-2/config.yaml     MODIFIED — eliminar security.roles y allowed_users
docs/security.md            MODIFIED — documentar nuevo sistema
CLAUDE.md                   MODIFIED — mencionar security/ en estructura

Patron pure core / impure shell

  • pkg/security/puro: tipos (UserGroup, AgentGroup, SecurityPolicy) y función ResolveACL(). Cero I/O.
  • shell/security/impuro: lee YAML del filesystem y construye SecurityPolicy.
  • cmd/launcher/impuro: llama al loader, resuelve ACL por agente, inyecta en Agent{}.
  • agents/runtime.gocomposición: recibe acl.ACL ya resuelta, la usa en shouldHandle() y en la evaluación de permisos.

Tareas

Fase 1: Pure core — pkg/security/

  • 1.1 Crear pkg/security/groups.go con tipos UserGroup{Name, Members []string} y AgentGroup{Name, Agents []string}
  • 1.2 Crear pkg/security/policy.go con tipos Permission{UserGroup, Actions []string}, AgentPolicy{AgentGroup, Permissions []Permission}, SecurityPolicy{UserGroups, AgentGroups, Policies}
  • 1.3 Crear pkg/security/resolver.go con ResolveACL(agentID string, p SecurityPolicy) acl.ACL: expande grupos de agentes que incluyan agentID o "*", expande grupos de usuarios a acl.Role list, construye acl.ACL vía acl.FromRoles()
  • 1.4 Soporte de wildcard: AgentGroup.Agents = ["*"] aplica a todos los agentes; UserGroup.Members = ["*"] aplica a todos los usuarios
  • 1.5 Crear pkg/security/security_test.go con casos: sin política (ACL vacía), agente en grupo, agente no en grupo, wildcard de agente, wildcard de usuario, múltiples políticas acumulativas

Fase 2: Config files + Shell loader

  • 2.1 Crear security/user-groups.yaml con ejemplo: grupos admins y everyone (members: ["*"])
  • 2.2 Crear security/agent-groups.yaml con ejemplo: grupo assistants con los agentes actuales (assistant-bot, asistente-2), grupo all con agents: ["*"]
  • 2.3 Crear security/permissions.yaml con ejemplo: grupo all da acción "ask" a everyone; grupo all da "*" a admins
  • 2.4 Crear shell/security/loader.go con Load(dir string) (security.SecurityPolicy, error) que lee los 3 YAML del directorio y construye el struct. Si el directorio no existe, devuelve SecurityPolicy{} vacía (sin error: backward compat).
  • 2.5 Crear shell/security/loader_test.go con tests: dir vacío → policy vacía, YAMLs válidos → policy correcta, YAML malformado → error claro

Fase 3: Integración en launcher y runtime

  • 3.1 En cmd/launcher/main.go: llamar shell/security.Load("security/") al inicio; para cada agente llamar security.ResolveACL(cfg.Agent.ID, policy) y pasar la acl.ACL resultante a agents.New()
  • 3.2 En agents/runtime.go: añadir campo acl acl.ACL en Agent{}. Extender agents.New() para aceptar acl.ACL como parámetro adicional (o via Option). Usar a.acl.CanDo() en shouldHandle() y en evaluación de permisos de comandos/tools
  • 3.3 En shell/matrix/listener.go: eliminar el chequeo de AllowedUsers (líneas 285-301 aprox.); el control de acceso ahora está en runtime via acl.ACL
  • 3.4 En internal/config/schema.go: deprecar campos security.roles (añadir comentario // Deprecated: usar security/ centralizado) y matrix.filters.allowed_users (mismo comentario). No eliminar todavía — backward compat.
  • 3.5 Actualizar agents/assistant-bot/config.yaml y agents/asistente-2/config.yaml: eliminar bloques security.roles y matrix.filters.allowed_users (ahora gestionados centralmente)
  • 3.6 Actualizar security/permissions.yaml con los permisos reales de los agentes actuales (extraídos de sus configs antes de borrarlos)

Fase 4: Tests de integración

  • 4.1 go build -tags goolm ./... compila sin errores
  • 4.2 go test -tags goolm ./pkg/security/... pasa
  • 4.3 go test -tags goolm ./shell/security/... pasa
  • 4.4 go test -tags goolm ./... pasa completo (sin romper tests existentes de pkg/acl)

Fase 5: Cleanup y docs

  • 5.1 Actualizar docs/security.md — documentar el sistema de grupos, estructura de los 3 YAML, campos disponibles en cada uno, cómo se resuelven las ACLs
  • 5.2 Actualizar CLAUDE.md — añadir security/ en la sección de estructura del proyecto
  • 5.3 Añadir .gitignore entry si aplica (los YAML de security/ SÍ se commitean — son config, no secrets)
  • 5.4 Evaluar si eliminar definitivamente los campos deprecated del schema en este issue o dejarlo para un issue de limpieza posterior

Desglose multi-issue

Este issue se implementa en 3 sub-issues independientes.

Sub-issue Rama Alcance Estado
0024a-security-types issue/0024a-security-types pkg/security/ tipos puros + resolver + tests pendiente
0024b-security-loader issue/0024b-security-loader security/ YAML files + shell/security/ loader + tests pendiente
0024c-security-integration issue/0024c-security-integration Wiring en launcher+runtime, cleanup config schema, update agent configs, docs pendiente

Feature flag

Nombre: centralized-security-groups Se activa en el último sub-issue (0024c) una vez que todos los agentes usan la política centralizada y se han eliminado los controles per-agente.

Progreso por tarea

  • 1.1 UserGroup, AgentGroup types — 0024a
  • 1.2 Permission, AgentPolicy, SecurityPolicy types — 0024a
  • 1.3 ResolveACL() function — 0024a
  • 1.4 Wildcard support — 0024a
  • 1.5 Tests pkg/security/ — 0024a
  • 2.1 security/user-groups.yaml — 0024b
  • 2.2 security/agent-groups.yaml — 0024b
  • 2.3 security/permissions.yaml — 0024b
  • 2.4 shell/security/loader.go — 0024b
  • 2.5 Tests shell/security/ — 0024b
  • 3.1 Launcher wiring — 0024c
  • 3.2 Runtime ACL field + New() — 0024c
  • 3.3 Remove AllowedUsers from listener — 0024c
  • 3.4 Deprecar campos schema — 0024c
  • 3.5 Update agent configs — 0024c
  • 3.6 Populate permissions.yaml con datos reales — 0024c
  • 4.14.4 Tests completos — 0024c
  • 5.15.4 Cleanup y docs — 0024c

Ejemplo de uso

Estructura de archivos resultante:

security/
  user-groups.yaml
  agent-groups.yaml
  permissions.yaml

security/user-groups.yaml:

groups:
  admins:
    members:
      - "@alice:matrix-af2f3d.organic-machine.com"
      - "@bob:matrix-af2f3d.organic-machine.com"
  developers:
    members:
      - "@carol:matrix-af2f3d.organic-machine.com"
  everyone:
    members: ["*"]

security/agent-groups.yaml:

groups:
  assistants:
    agents:
      - assistant-bot
      - asistente-2
  all:
    agents: ["*"]

security/permissions.yaml:

policies:
  - agent_group: all
    permissions:
      - user_group: admins
        actions: ["*"]
      - user_group: developers
        actions: ["ask", "command:help", "command:ping", "tool:*"]
      - user_group: everyone
        actions: ["ask"]

Resultado: Al arrancar, el launcher lee security/, resuelve la ACL de cada agente, y se la inyecta. Los agentes ya no tienen security.roles ni allowed_users en su config individual. Para dar permisos a un nuevo usuario en todos los agentes, basta editar security/user-groups.yaml.

Decisiones de diseño

  • Reutilizar pkg/acl/ como motor: pkg/security/ no reemplaza pkg/acl/, lo usa. ResolveACL() produce acl.ACL que los agentes ya saben consumir. Mínimo cambio en runtime.
  • 3 YAML separados vs 1 solo archivo: separar grupos de usuarios, grupos de agentes, y permisos mantiene cada archivo enfocado. Los grupos son estables; los permisos cambian más frecuentemente.
  • Backward compat en schema: deprecar pero no eliminar security.roles y allowed_users en 0024c. Eliminarlos definitivamente sería un issue de limpieza posterior.
  • Loader devuelve policy vacía si no existe security/: no rompe agentes existentes si el directorio no existe. La ACL vacía equivale a "sin restricciones" (comportamiento actual).
  • ACL inyectada via parámetro en agents.New(): alternativa a Option{} para mantener la firma explícita. Más simple y sin abstracción innecesaria.

Prerequisitos

  • pkg/acl/ funcionando (completado en issue 0010)
  • Agentes compilando con -tags goolm (ya funciona)

Riesgos

  • Permisos actuales en config.yaml: antes de eliminar security.roles de los configs de agente, leer y migrar todos los roles a security/permissions.yaml. Si se olvida alguno, el agente queda sin restricciones o con más acceso del esperado. Mitigación: hacer la migración explícitamente en tarea 3.6 antes de borrar en 3.5.
  • Orden de carga en launcher: si el loader falla, los agentes arrancan sin ACL (acceso abierto). Mitigación: loguear WARNING claro en ese caso; considerar modo estricto (fail-fast) como opción de config futura.
  • acl.FromRoles() API: verificar que pkg/acl/ expone una función que acepte []acl.Role directamente (no solo map[string]RoleDef). Si no existe, añadirla en 0024a.