access

package
v0.60.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package access is the policy-enforcement point for Agent resources.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidScope reports an admin-supplied scope that is neither system nor
	// restricted. It is a validation error (400), never an authorization denial.
	ErrInvalidScope = errors.New("agent scope must be 'system' or 'restricted'")
	// ErrUserNotFound reports an assignment target that does not exist.
	ErrUserNotFound = errors.New("assignment target user not found")
)

Management-specific typed errors. Authorization/availability reuse the Service sentinels (ErrForbidden / ErrNotFound / ErrUnavailable) so the transport maps every Agent error through one vocabulary.

View Source
var (
	ErrForbidden   = errors.New("agent access forbidden")
	ErrNotFound    = errors.New("agent not found")
	ErrUnavailable = errors.New("agent authorization unavailable")
)

Functions

func GroupAgentAuthority

func GroupAgentAuthority(groupID, agentID string) (authz.Authority, error)

GroupAgentAuthority mints an exact group/agent authority for one group turn. A triggering user is intentionally absent from this capability, so a group turn can never reach user-private access.

func SystemAgentAuthority

func SystemAgentAuthority(component string) (authz.Authority, error)

SystemAgentAuthority mints the named maintenance authority used for an agent invocation. System execution is a named component; it never inherits an admin or user identity.

func WorkerAgentAuthority

func WorkerAgentAuthority(ownerID, agentID string) (authz.Authority, error)

WorkerAgentAuthority reconstructs the sole authority shape permitted for a persisted user-owned worker: its durable owner and exact executor agent.

Types

type Access

type Access struct {
	// contains filtered or unexported fields
}

Access captures one validated authority and caches its assignment relation for the duration of a multi-step use case.

func (*Access) CanCreate

func (a *Access) CanCreate() error

func (*Access) CanList

func (a *Access) CanList() error

func (*Access) CanUse

func (a *Access) CanUse(ctx context.Context, agentID string) (bool, error)

func (*Access) Delete

func (a *Access) Delete(ctx context.Context, agentID string) (config.Agent, error)

func (*Access) ListReadable

func (a *Access) ListReadable(ctx context.Context, includeDisabled bool) ([]config.Agent, error)

ListReadable applies both the collection list decision and a read decision to every candidate. SQL may narrow candidates for performance, but never decides visibility.

func (*Access) Manage

func (a *Access) Manage(ctx context.Context, agentID string) (config.Agent, error)

func (*Access) Read

func (a *Access) Read(ctx context.Context, agentID string) (config.Agent, error)

func (*Access) Use

func (a *Access) Use(ctx context.Context, agentID string) (config.Agent, error)

func (*Access) UseDedicated

func (a *Access) UseDedicated(ctx context.Context, agentID, channelID string) (config.Agent, error)

UseDedicated requires both the authority's exact held channel binding and a current DB binding from that channel to this exact agent. Either mismatch fails closed; channel ID by itself is never an agent-use capability.

type ActivityReader

type ActivityReader interface {
	ListAgentLastActive(ctx context.Context, userID string) (map[string]time.Time, error)
}

ActivityReader is the conversation-activity read model: the last-active time per agent for one user's own conversations.

type AgentReloader

type AgentReloader interface {
	SyncAgent(ctx context.Context, agentID string) error
}

AgentReloader refreshes an agent's runtime state after a durable change. agent.PoolManager.SyncAgent satisfies it. Reload is eventually-consistent, so Management treats a failure as best-effort (see Create/Update/Delete).

type AgentStore

type AgentStore interface {
	GetAgent(ctx context.Context, id string) (config.Agent, error)
	ListAgents(ctx context.Context) ([]config.Agent, error)
}

AgentStore is deliberately narrow, but includes list because collection filtering belongs at the PEP rather than in transports or SQL.

type AgentWriter

type AgentWriter interface {
	GetAgent(ctx context.Context, id string) (config.Agent, error)
	CreateAgent(ctx context.Context, a config.Agent) error
	UpdateAgent(ctx context.Context, a config.Agent) error
	DeleteAgent(ctx context.Context, id string) error
}

AgentWriter persists agent rows. config.Store satisfies it; the interface stays narrow so Management cannot reach unrelated aggregate config.

type AssignmentStore

type AssignmentStore interface {
	ListUserAgentIDs(ctx context.Context, userID string) ([]string, error)
}

type AssignmentWriter

type AssignmentWriter interface {
	AssignAgent(ctx context.Context, userID, agentID string) error
	RemoveAgent(ctx context.Context, userID, agentID string) error
	ListAgentUserIDs(ctx context.Context, agentID string) ([]string, error)
}

AssignmentWriter mutates and lists the user<->agent assignment relation. auth.AuthStore satisfies it.

type Management

type Management struct {
	// contains filtered or unexported fields
}

Management owns the Agent write use cases: create/update/delete, admin user assignment, and the conversation-activity read model. Every method authorizes through the Agent PEP (Service) before any durable read or write, then performs the persistence, cross-write compensation, and best-effort runtime reload the HTTP transport used to orchestrate itself. Splitting these off the read-only Service keeps the PEP's decision surface narrow while giving the Agent domain a single owner for its mutating invariants (unique ID, creator auto-assignment atomicity, scope rules).

func NewManagement

func NewManagement(pep *Service, agents AgentWriter, assign AssignmentWriter, reloader AgentReloader, users UserDirectory, activity ActivityReader, log *slog.Logger) *Management

NewManagement builds the Agent management service over the Agent PEP and its write/reload/lookup ports. log defaults to slog.Default() when nil.

func (*Management) AssignUser

func (m *Management) AssignUser(ctx context.Context, authority authz.Authority, agentID, userID string) (UserRef, error)

AssignUser assigns a user to an agent. Admin-only. It verifies the agent then the user exists (preserving the historical 404 order) before writing.

func (*Management) Create

func (m *Management) Create(ctx context.Context, authority authz.Authority, candidate config.Agent) (config.Agent, error)

Create authorizes and persists a new agent, enforcing the scope rules, generating a unique ID, and — for a restricted agent a non-admin creates — auto-assigning the creator. The create+auto-assign pair is atomic from the caller's view: a failed assignment compensates by deleting the just-created agent, so a restricted agent is never left invisible to its creator. The caller supplies a fully transport-validated candidate whose ID is the base slug; Management owns the scope decision, uniqueness, workspace, and creator.

func (*Management) Delete

func (m *Management) Delete(ctx context.Context, authority authz.Authority, agentID string) error

Delete authorizes (Delete) and removes an agent. Reload is best-effort.

func (*Management) ListAgentLastActive

func (m *Management) ListAgentLastActive(ctx context.Context, userID string) (map[string]time.Time, error)

ListAgentLastActive returns the last-active time per agent for the given user's own conversations. It carries no additional authorization: the caller has already been list-authorized for the agents it enriches, and the read is scoped to that same user's activity.

func (*Management) ListAssignedUsers

func (m *Management) ListAssignedUsers(ctx context.Context, authority authz.Authority, agentID string) ([]UserRef, error)

ListAssignedUsers returns the account references for the users assigned to an agent. Admin-only: the gate runs before any durable read.

func (*Management) RemoveUser

func (m *Management) RemoveUser(ctx context.Context, authority authz.Authority, agentID, userID string) error

RemoveUser unassigns a user from an agent. Admin-only.

func (*Management) Update

func (m *Management) Update(ctx context.Context, authority authz.Authority, candidate config.Agent) (config.Agent, error)

Update authorizes (Manage) and persists an agent edit. Non-admins cannot change scope; admins default an empty scope to system and reject any other value. The caller supplies a transport-validated candidate; reload is best-effort.

type Service

type Service struct {
	// contains filtered or unexported fields
}

func NewService

func NewService(agents AgentStore, assign AssignmentStore) *Service

func (*Service) Authorize

func (s *Service) Authorize(ctx context.Context, authority authz.Authority, agentID string, action authz.Action) error

Authorize is the narrow cross-domain Agent port. It reads the durable Agent and applies the same direct Agent rules as the public access methods; a dedicated-channel binding is intentionally not inferred here.

func (*Service) AuthorizeViaChannelBinding

func (s *Service) AuthorizeViaChannelBinding(ctx context.Context, authority authz.Authority, agentID string) error

AuthorizeViaChannelBinding authorizes read/execute of agentID for a trusted dedicated-channel Authority: the exact held channel binding whose CURRENT persisted channel→agent binding names this exact agent. It is the Agent PEP's sole interpretation of a dedicated channel, so cross-domain callers (the session flow's Agent gate) never re-derive channel bindings themselves. A held binding alone is never authority for an arbitrary agent; every mismatch fails closed.

func (*Service) Begin

func (s *Service) Begin(_ context.Context, authority authz.Authority) (*Access, error)

func (*Service) CanCreate

func (s *Service) CanCreate(ctx context.Context, authority authz.Authority) error

func (*Service) CanList

func (s *Service) CanList(ctx context.Context, authority authz.Authority) error

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, authority authz.Authority, agentID string) (config.Agent, error)

func (*Service) ListReadable

func (s *Service) ListReadable(ctx context.Context, authority authz.Authority, includeDisabled bool) ([]config.Agent, error)

func (*Service) Manage

func (s *Service) Manage(ctx context.Context, authority authz.Authority, agentID string) (config.Agent, error)

func (*Service) Read

func (s *Service) Read(ctx context.Context, authority authz.Authority, agentID string) (config.Agent, error)

func (*Service) Use

func (s *Service) Use(ctx context.Context, authority authz.Authority, agentID string) (config.Agent, error)

type UserDirectory

type UserDirectory interface {
	LookupUser(ctx context.Context, userID string) (UserRef, error)
	LookupUsers(ctx context.Context, userIDs []string) ([]UserRef, error)
}

UserDirectory resolves assignment-target account display data. The composition root backs it with the account user store; LookupUsers skips IDs that no longer resolve so a stale assignment link never breaks the admin listing.

type UserRef

type UserRef struct {
	ID    string
	Email string
}

UserRef is the account display data an assignment view needs. It is a domain value, not an account row, so Management never depends on the Account boundary's internal shapes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL