Documentation
¶
Overview ¶
Package agentcredential stores Agent credentials encrypted at rest.
A credential belongs to a team and is referenced by at most one Bot Agent instance via bot_agents.agent_credential_id (enforced by product flow, not schema). Attach replaces an Agent's credential atomically and revokes the replaced row once nothing references it; runtime resolution walks session → bot_agent → credential and decrypts only while preparing the Agent process configuration.
Index ¶
- Constants
- Variables
- func Compatible(agentRuntime, authKind string) bool
- func ProviderForAuthKind(kind string) string
- type Authorization
- type AuthorizationRequest
- type AuthorizationService
- func (s *AuthorizationService) Cancel(ctx context.Context, owner, id string) error
- func (s *AuthorizationService) Claim(ctx context.Context, owner, id, botID, agentID string) (PublicCredential, error)
- func (s *AuthorizationService) Create(ctx context.Context, owner string, req AuthorizationRequest) (Authorization, error)
- func (s *AuthorizationService) Exchange(ctx context.Context, owner, id, code string) (Authorization, error)
- func (s *AuthorizationService) Get(ctx context.Context, owner, id string, poll bool) (Authorization, error)
- type CreateRequest
- type PublicCredential
- type ResolvedCredential
- type Service
- func (s *Service) AttachToBotAgent(ctx context.Context, ownerUserID, botID, botAgentID string, req CreateRequest) (PublicCredential, error)
- func (s *Service) Configured() bool
- func (s *Service) DetachFromBotAgent(ctx context.Context, botID, botAgentID string) error
- func (s *Service) GetForBotAgent(ctx context.Context, botID, botAgentID string) (PublicCredential, error)
- func (s *Service) ResolveForBotAgent(ctx context.Context, botID, botAgentID string) (ResolvedCredential, error)
- func (s *Service) UpdateSecretCAS(ctx context.Context, credentialID string, expectedVersion int64, ...) (PublicCredential, error)
Constants ¶
const ( ProviderOpenAI = "openai" ProviderAnthropic = "anthropic" AuthKindOpenAIAPIKey = "openai_api_key" //nolint:gosec // Stable authentication-kind identifier. AuthKindOpenAICodexOAuth = "openai_codex_oauth" AuthKindAnthropicAPIKey = "anthropic_api_key" //nolint:gosec // Stable authentication-kind identifier. AuthKindClaudeCodeOAuth = "claude_code_oauth" )
Variables ¶
var ( ErrAuthorizationExpired = errors.New("authorization session expired or unavailable") ErrAuthorizationNotReady = errors.New("authorization is not complete") ErrAuthorizationLimit = errors.New("too many pending authorizations") ErrAuthorizationFailed = errors.New("authorization failed") ErrAuthorizationCodeInvalid = errors.New("authorization code is invalid") )
Functions ¶
func Compatible ¶
Compatible reports whether an auth kind can drive the Agent runtime.
func ProviderForAuthKind ¶
ProviderForAuthKind maps an auth kind to its provider so API callers only submit the kind.
Types ¶
type Authorization ¶
type Authorization struct {
ID string `json:"id" validate:"required"`
Runtime string `json:"runtime" validate:"required"`
AuthKind string `json:"auth_kind" validate:"required"`
Status string `json:"status" validate:"required" enums:"pending,ready,claimed"`
ExpiresAt time.Time `json:"expires_at" validate:"required"`
UserCode string `json:"user_code,omitempty"`
VerificationURL string `json:"verification_url,omitempty"`
AuthorizationURL string `json:"authorization_url,omitempty"`
IntervalSeconds int64 `json:"interval_seconds,omitempty"`
}
Authorization contains only the public handoff, never credentials or provider device IDs.
type AuthorizationRequest ¶
type AuthorizationService ¶
type AuthorizationService struct {
// contains filtered or unexported fields
}
AuthorizationService stages encrypted credentials independently of a Bot. Row locks serialize polling and one-time claims across server instances.
func NewAuthorizationService ¶
func NewAuthorizationService(credentials *Service, oauth *providers.Service) *AuthorizationService
func (*AuthorizationService) Cancel ¶
func (s *AuthorizationService) Cancel(ctx context.Context, owner, id string) error
func (*AuthorizationService) Claim ¶
func (s *AuthorizationService) Claim(ctx context.Context, owner, id, botID, agentID string) (PublicCredential, error)
Claim is idempotent for the same Agent and cannot attach a session to a second Agent. Binding, credential encryption, and erasing staged secrets commit together.
func (*AuthorizationService) Create ¶
func (s *AuthorizationService) Create(ctx context.Context, owner string, req AuthorizationRequest) (Authorization, error)
func (*AuthorizationService) Exchange ¶
func (s *AuthorizationService) Exchange(ctx context.Context, owner, id, code string) (Authorization, error)
Exchange completes a pending Claude authorization. Failed exchanges remain retryable; concurrent retries cannot exchange a one-time code twice.
func (*AuthorizationService) Get ¶
func (s *AuthorizationService) Get(ctx context.Context, owner, id string, poll bool) (Authorization, error)
type CreateRequest ¶
type CreateRequest struct {
Provider string `json:"provider"`
AuthKind string `json:"auth_kind"`
Label string `json:"label,omitempty"`
Secret map[string]string `json:"secret"`
AccountMetadata map[string]any `json:"account_metadata,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
CreateRequest carries a new secret into the store. Label may be empty; the service derives one from the auth kind when absent.
type PublicCredential ¶
type PublicCredential struct {
ID string `json:"id"`
OwnerUserID string `json:"owner_user_id"`
Provider string `json:"provider"`
AuthKind string `json:"auth_kind"`
Label string `json:"label"`
AccountMetadata map[string]any `json:"account_metadata,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
CredentialVersion int64 `json:"credential_version"`
Revoked bool `json:"revoked"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
PublicCredential is the redacted view of a stored credential. The secret never leaves the service; only labels and account metadata do.
type ResolvedCredential ¶
type ResolvedCredential struct {
PublicCredential
AgentRuntime string
Secret map[string]string
}
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func (*Service) AttachToBotAgent ¶
func (s *Service) AttachToBotAgent(ctx context.Context, ownerUserID, botID, botAgentID string, req CreateRequest) (PublicCredential, error)
AttachToBotAgent encrypts a new secret, points the Bot Agent instance at it, and revokes the replaced credential once no other instance references it. The instance's provider is read inside the transaction and gates auth-kind compatibility, so a wrong-profile attach can never link.
func (*Service) Configured ¶
func (*Service) DetachFromBotAgent ¶
DetachFromBotAgent disconnects the instance and revokes the credential once nothing references it. ErrNotFound covers both a missing Agent and an Agent that was never connected.
func (*Service) GetForBotAgent ¶
func (s *Service) GetForBotAgent(ctx context.Context, botID, botAgentID string) (PublicCredential, error)
GetForBotAgent returns the redacted credential currently attached to a Bot Agent instance, or ErrNotFound when the Agent is not connected.
func (*Service) ResolveForBotAgent ¶
func (s *Service) ResolveForBotAgent(ctx context.Context, botID, botAgentID string) (ResolvedCredential, error)
ResolveForBotAgent decrypts the credential attached to a Bot Agent instance.
func (*Service) UpdateSecretCAS ¶
func (s *Service) UpdateSecretCAS(ctx context.Context, credentialID string, expectedVersion int64, secret map[string]string, accountMetadata map[string]any, expiresAt *time.Time) (PublicCredential, error)
UpdateSecretCAS persists a rotated secret guarded by credential_version so a concurrent rotation (or revoke) loses cleanly instead of overwriting.