feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)

Reemplaza el scaffold del echobot por la plataforma completa de bots traida
desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out:
los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms +
E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client).

- go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths
  relativos reajustados a la nueva ubicacion dentro de fn_registry).
- app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales.
- modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports).

agents_and_robots queda archivado como museo de la era Matrix.
This commit is contained in:
agent
2026-06-07 11:50:13 +02:00
parent bb5b0e09b1
commit fc644ecd6e
308 changed files with 38829 additions and 474 deletions
+68
View File
@@ -0,0 +1,68 @@
package security
import "github.com/enmanuel/agents/pkg/acl"
// ResolveACL computes the ACL for a given agentID from a SecurityPolicy.
//
// Resolution rules:
// - An AgentPolicy applies to agentID if its AgentGroup field is:
// (a) a group name in p.AgentGroups whose Agents list contains agentID or "*", or
// (b) directly equal to agentID (individual assignment without a named group).
// - If multiple AgentPolicies apply, their permissions are accumulated (union).
// - For each Permission, the UserGroup is resolved to members in p.UserGroups.
// A UserGroup with Members = ["*"] grants the actions to all users.
// - If no policy applies, an empty ACL is returned (open access per acl semantics).
func ResolveACL(agentID string, p SecurityPolicy) acl.ACL {
// Build a lookup: group name → members.
userGroupMembers := make(map[string][]string, len(p.UserGroups))
for _, ug := range p.UserGroups {
userGroupMembers[ug.Name] = ug.Members
}
// Collect all roles from every AgentPolicy that applies to this agent.
var roles []acl.Role
for _, ap := range p.Policies {
if !agentPolicyApplies(agentID, ap.AgentGroup, p.AgentGroups) {
continue
}
for _, perm := range ap.Permissions {
members := resolveMembers(perm.UserGroup, userGroupMembers)
roles = append(roles, acl.Role{
Name: perm.UserGroup,
Users: members,
Actions: perm.Actions,
})
}
}
return acl.FromRoles(roles)
}
// agentPolicyApplies returns true if an AgentPolicy with the given agentGroupRef
// should apply to agentID.
func agentPolicyApplies(agentID, agentGroupRef string, groups []AgentGroup) bool {
// Try to find a named group first.
for _, ag := range groups {
if ag.Name != agentGroupRef {
continue
}
for _, a := range ag.Agents {
if a == "*" || a == agentID {
return true
}
}
return false // group found but agent not in it
}
// No matching group found — treat agentGroupRef as a direct agent ID.
return agentGroupRef == agentID
}
// resolveMembers returns the member list for a user group name.
// If the group is not defined, it falls back to treating the name as a literal user ID.
func resolveMembers(userGroupName string, lookup map[string][]string) []string {
if members, ok := lookup[userGroupName]; ok {
return members
}
// Fallback: treat as a direct user ID.
return []string{userGroupName}
}