go-crypt/trust/trust.go
Snider ee58e790a0
Some checks failed
Security Scan / security (push) Failing after 11s
Test / test (push) Failing after 29s
feat: modernise to Go 1.26 iterators and stdlib helpers
Add iter.Seq iterators for trust registry (ListSeq), audit log
(EventsSeq, QuerySeq), and approval store (PendingSeq). Use
slices.DeleteFunc in session store, slices.SortFunc in testcmd,
range-over-int in benchmarks.

Co-Authored-By: Gemini <noreply@google.com>
Co-Authored-By: Virgil <virgil@lethean.io>
2026-02-23 05:47:13 +00:00

179 lines
4.6 KiB
Go

// Package trust implements an agent trust model with tiered access control.
//
// Agents are assigned trust tiers that determine their capabilities:
//
// - Tier 3 (Full Trust): Internal agents with full access (e.g., Athena, Virgil, Charon)
// - Tier 2 (Verified): Partner agents with scoped access (e.g., Clotho, Hypnos)
// - Tier 1 (Untrusted): External/community agents with minimal access
//
// The package provides a Registry for managing agent identities and a PolicyEngine
// for evaluating capability requests against trust policies.
package trust
import (
"fmt"
"iter"
"sync"
"time"
)
// Tier represents an agent's trust level in the system.
type Tier int
const (
// TierUntrusted is for external/community agents with minimal access.
TierUntrusted Tier = 1
// TierVerified is for partner agents with scoped access.
TierVerified Tier = 2
// TierFull is for internal agents with full access.
TierFull Tier = 3
)
// String returns the human-readable name of the tier.
func (t Tier) String() string {
switch t {
case TierUntrusted:
return "untrusted"
case TierVerified:
return "verified"
case TierFull:
return "full"
default:
return fmt.Sprintf("unknown(%d)", int(t))
}
}
// Valid returns true if the tier is a recognised trust level.
func (t Tier) Valid() bool {
return t >= TierUntrusted && t <= TierFull
}
// Capability represents a specific action an agent can perform.
type Capability string
const (
CapPushRepo Capability = "repo.push"
CapMergePR Capability = "pr.merge"
CapCreatePR Capability = "pr.create"
CapCreateIssue Capability = "issue.create"
CapCommentIssue Capability = "issue.comment"
CapReadSecrets Capability = "secrets.read"
CapRunPrivileged Capability = "cmd.privileged"
CapAccessWorkspace Capability = "workspace.access"
CapModifyFlows Capability = "flows.modify"
)
// Agent represents an agent identity in the trust system.
type Agent struct {
// Name is the unique identifier for the agent (e.g., "Athena", "Clotho").
Name string
// Tier is the agent's trust level.
Tier Tier
// ScopedRepos limits repo access for Tier 2 agents. Empty means no repo access.
// Tier 3 agents ignore this field (they have access to all repos).
ScopedRepos []string
// RateLimit is the maximum requests per minute. 0 means unlimited.
RateLimit int
// TokenExpiresAt is when the agent's token expires.
TokenExpiresAt time.Time
// CreatedAt is when the agent was registered.
CreatedAt time.Time
}
// Registry manages agent identities and their trust tiers.
type Registry struct {
mu sync.RWMutex
agents map[string]*Agent
}
// NewRegistry creates an empty agent registry.
func NewRegistry() *Registry {
return &Registry{
agents: make(map[string]*Agent),
}
}
// Register adds or updates an agent in the registry.
// Returns an error if the agent name is empty or the tier is invalid.
func (r *Registry) Register(agent Agent) error {
if agent.Name == "" {
return fmt.Errorf("trust.Register: agent name is required")
}
if !agent.Tier.Valid() {
return fmt.Errorf("trust.Register: invalid tier %d for agent %q", agent.Tier, agent.Name)
}
if agent.CreatedAt.IsZero() {
agent.CreatedAt = time.Now()
}
if agent.RateLimit == 0 {
agent.RateLimit = defaultRateLimit(agent.Tier)
}
r.mu.Lock()
defer r.mu.Unlock()
r.agents[agent.Name] = &agent
return nil
}
// Get returns the agent with the given name, or nil if not found.
func (r *Registry) Get(name string) *Agent {
r.mu.RLock()
defer r.mu.RUnlock()
return r.agents[name]
}
// Remove deletes an agent from the registry.
func (r *Registry) Remove(name string) bool {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.agents[name]; !ok {
return false
}
delete(r.agents, name)
return true
}
// List returns all registered agents. The returned slice is a snapshot.
func (r *Registry) List() []Agent {
r.mu.RLock()
defer r.mu.RUnlock()
out := make([]Agent, 0, len(r.agents))
for _, a := range r.agents {
out = append(out, *a)
}
return out
}
// ListSeq returns an iterator over all registered agents.
func (r *Registry) ListSeq() iter.Seq[Agent] {
return func(yield func(Agent) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
for _, a := range r.agents {
if !yield(*a) {
return
}
}
}
}
// Len returns the number of registered agents.
func (r *Registry) Len() int {
r.mu.RLock()
defer r.mu.RUnlock()
return len(r.agents)
}
// defaultRateLimit returns the default rate limit for a given tier.
func defaultRateLimit(t Tier) int {
switch t {
case TierUntrusted:
return 10
case TierVerified:
return 60
case TierFull:
return 0 // unlimited
default:
return 10
}
}