- Actualiza docs/security.md: nueva sección "Sistema de grupos centralizados" con estructura de los 3 YAML, acciones disponibles, campos deprecados - Actualiza .claude/CLAUDE.md: añade security/ en la estructura del proyecto - Mueve 0024 y 0024c a dev/issues/completed/ - Actualiza dev/issues/README.md: marca 0024, 0024a, 0024b, 0024c como completado Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 KiB
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.rolesenconfig.yamlymatrix.filters.allowed_usersenmatrix.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 enpkg/acl/para produciracl.ACLpor 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_usersysecurity.rolesdel 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ónResolveACL(). Cero I/O.shell/security/— impuro: lee YAML del filesystem y construyeSecurityPolicy.cmd/launcher/— impuro: llama al loader, resuelve ACL por agente, inyecta enAgent{}.agents/runtime.go— composición: recibeacl.ACLya resuelta, la usa enshouldHandle()y en la evaluación de permisos.
Tareas
Fase 1: Pure core — pkg/security/
- 1.1 Crear
pkg/security/groups.gocon tiposUserGroup{Name, Members []string}yAgentGroup{Name, Agents []string} - 1.2 Crear
pkg/security/policy.gocon tiposPermission{UserGroup, Actions []string},AgentPolicy{AgentGroup, Permissions []Permission},SecurityPolicy{UserGroups, AgentGroups, Policies} - 1.3 Crear
pkg/security/resolver.goconResolveACL(agentID string, p SecurityPolicy) acl.ACL: expande grupos de agentes que incluyanagentIDo"*", expande grupos de usuarios aacl.Rolelist, construyeacl.ACLvíaacl.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.gocon 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.yamlcon ejemplo: gruposadminsyeveryone(members:["*"]) - 2.2 Crear
security/agent-groups.yamlcon ejemplo: grupoassistantscon los agentes actuales (assistant-bot,asistente-2), grupoallconagents: ["*"] - 2.3 Crear
security/permissions.yamlcon ejemplo: grupoallda acción"ask"aeveryone; grupoallda"*"aadmins - 2.4 Crear
shell/security/loader.goconLoad(dir string) (security.SecurityPolicy, error)que lee los 3 YAML del directorio y construye el struct. Si el directorio no existe, devuelveSecurityPolicy{}vacía (sin error: backward compat). - 2.5 Crear
shell/security/loader_test.gocon 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: llamarshell/security.Load("security/")al inicio; para cada agente llamarsecurity.ResolveACL(cfg.Agent.ID, policy)y pasar laacl.ACLresultante aagents.New() - 3.2 En
agents/runtime.go: añadir campoacl acl.ACLenAgent{}. Extenderagents.New()para aceptaracl.ACLcomo parámetro adicional (o viaOption). Usara.acl.CanDo()enshouldHandle()y en evaluación de permisos de comandos/tools - 3.3 En
shell/matrix/listener.go: eliminar el chequeo deAllowedUsers(líneas 285-301 aprox.); el control de acceso ahora está en runtime viaacl.ACL - 3.4 En
internal/config/schema.go: deprecar campossecurity.roles(añadir comentario// Deprecated: usar security/ centralizado) ymatrix.filters.allowed_users(mismo comentario). No eliminar todavía — backward compat. - 3.5 Actualizar
agents/assistant-bot/config.yamlyagents/asistente-2/config.yaml: eliminar bloquessecurity.rolesymatrix.filters.allowed_users(ahora gestionados centralmente) - 3.6 Actualizar
security/permissions.yamlcon 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ñadirsecurity/en la sección de estructura del proyecto - 5.3 Añadir
.gitignoreentry si aplica (los YAML desecurity/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.1–4.4 Tests completos — 0024c
- 5.1–5.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 reemplazapkg/acl/, lo usa.ResolveACL()produceacl.ACLque 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.rolesyallowed_usersen 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.rolesde los configs de agente, leer y migrar todos los roles asecurity/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.Roledirectamente (no solomap[string]RoleDef). Si no existe, añadirla en 0024a.