Documentation
¶
Index ¶
- func APIKeyAuth(config AuthConfig) gin.HandlerFunc
- func APIKeyUnaryInterceptor(apiKey string) grpc.UnaryServerInterceptor
- func AdminTokenAuth(adminToken string) gin.HandlerFunc
- func ConnectorCapabilityCheck(capName string, capabilities map[string]config.ConnectorCapability) gin.HandlerFunc
- func ConnectorTokenAuth(connectorToken string) gin.HandlerFunc
- func DIDAuthMiddleware(didService DIDWebServiceInterface, config DIDAuthConfig) gin.HandlerFunc
- func GetCallerAgentID(c *gin.Context) string
- func GetTargetAgent(c *gin.Context) *types.AgentNode
- func GetTargetDID(c *gin.Context) string
- func GetVerifiedCallerDID(c *gin.Context) string
- func GinLogger() gin.HandlerFunc
- func IsDIDAuthSkipped(c *gin.Context) bool
- func MemoryEventFilter(c *gin.Context) func(context.Context, *types.MemoryChangeEvent) bool
- func MemoryEventScopeFilter(c *gin.Context) func(context.Context, *types.MemoryChangeEvent) bool
- func MemoryEventSnapshotFilter(c *gin.Context) func(context.Context, *types.MemoryChangeEvent) bool
- func MemoryPermissionMiddleware(policyService AccessPolicyServiceInterface, ...) gin.HandlerFunc
- func PermissionCheckMiddleware(policyService AccessPolicyServiceInterface, ...) gin.HandlerFunc
- func PrivilegedAccess(config AuthConfig) gin.HandlerFunc
- func RateLimit(store *RateLimiterStore) gin.HandlerFunc
- func RequireMemoryScope(c *gin.Context, scope, scopeID string) bool
- func WriteMemoryAccessDenial(c *gin.Context, denial *MemoryAccessDenial)
- type AccessPolicyServiceInterface
- type AgentResolverInterface
- type AuthConfig
- type ContextKey
- type DIDAuthConfig
- type DIDResolverInterface
- type DIDWebServiceInterface
- type MemoryAccessDenial
- type MemoryAgentLister
- type MemoryExecutionReader
- type MemoryPermissionConfig
- type MemoryPermissionResult
- type PermissionCheckResult
- type PermissionConfig
- type RateLimitConfig
- type RateLimiterStore
- type RateLimitsConfig
- type TagVCVerifierInterface
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func APIKeyAuth ¶
func APIKeyAuth(config AuthConfig) gin.HandlerFunc
APIKeyAuth enforces API key authentication via header or bearer token.
func APIKeyUnaryInterceptor ¶
func APIKeyUnaryInterceptor(apiKey string) grpc.UnaryServerInterceptor
APIKeyUnaryInterceptor enforces API key authentication on gRPC calls.
func AdminTokenAuth ¶
func AdminTokenAuth(adminToken string) gin.HandlerFunc
AdminTokenAuth enforces a separate admin token for admin routes. If adminToken is empty, the middleware is a no-op (falls back to global API key auth). Admin tokens must be sent via the X-Admin-Token header only (not Bearer) to avoid collision with the API key Bearer token namespace.
func ConnectorCapabilityCheck ¶
func ConnectorCapabilityCheck(capName string, capabilities map[string]config.ConnectorCapability) gin.HandlerFunc
ConnectorCapabilityCheck enforces that a specific capability is enabled for the connector token and respects read_only mode by rejecting write HTTP methods. This is the CP-side security boundary — even if the connector is compromised, requests for disabled or read-only capabilities are rejected here.
func ConnectorTokenAuth ¶
func ConnectorTokenAuth(connectorToken string) gin.HandlerFunc
ConnectorTokenAuth enforces the connector token for connector routes. It validates the X-Connector-Token header and injects audit correlation metadata (X-Command-ID, X-Command-Source) into the gin context.
func DIDAuthMiddleware ¶
func DIDAuthMiddleware(didService DIDWebServiceInterface, config DIDAuthConfig) gin.HandlerFunc
DIDAuthMiddleware creates a gin middleware that verifies DID-based authentication.
The middleware extracts X-Caller-DID, X-DID-Signature, and X-DID-Timestamp headers from incoming requests. If a caller DID is present, it verifies the signature against the caller's DID document public key.
Authentication flow:
- If no X-Caller-DID header is present, the request proceeds without DID auth
- If X-Caller-DID is present, X-DID-Signature and X-DID-Timestamp are required
- The timestamp must be within the configured time window (default: 5 minutes)
- The signature is verified against: timestamp + ":" + SHA256(body)
- Replay protection rejects signatures seen within the timestamp window
- On successful verification, the verified DID is stored in the gin context
This middleware should be applied AFTER API key authentication and BEFORE routes that need to know the caller's identity.
func GetCallerAgentID ¶
GetCallerAgentID extracts the caller agent ID from the gin context.
func GetTargetAgent ¶
GetTargetAgent extracts the resolved target agent from the gin context.
func GetTargetDID ¶
GetTargetDID extracts the target DID from the gin context.
func GetVerifiedCallerDID ¶
GetVerifiedCallerDID extracts the verified caller DID from the gin context. Returns empty string if no verified DID is present.
func GinLogger ¶
func GinLogger() gin.HandlerFunc
GinLogger replaces gin's default stdout logger. It emits one structured log line per request through the control-plane's zerolog logger, at a severity derived from the response status (see requestLogEvent). This keeps the default output free of the duplicated, overly verbose [GIN] lines that gin.Default() writes to stdout, without hiding failing requests.
func IsDIDAuthSkipped ¶
IsDIDAuthSkipped returns true if DID authentication was skipped for this request.
func MemoryEventFilter ¶
MemoryEventFilter limits wildcard streams to owned or participated scopes. Only positive participation decisions are cached, bounded per connection. A nil filter means authorization is not installed and all events are allowed.
func MemoryEventScopeFilter ¶
MemoryEventScopeFilter rechecks explicit stream selectors before each event. Explicit selectors retain read/bearer semantics, without participation checks.
func MemoryEventSnapshotFilter ¶
MemoryEventSnapshotFilter caches participation decisions, including denials, only for this history request. Its lookups are bounded by distinct scopes in the snapshot, so it needs neither a token bucket nor cache expiration.
func MemoryPermissionMiddleware ¶
func MemoryPermissionMiddleware( policyService AccessPolicyServiceInterface, agentResolver AgentResolverInterface, didResolver DIDResolverInterface, config MemoryPermissionConfig, ) gin.HandlerFunc
MemoryPermissionMiddleware resolves identity and evaluates memory tag policies. Handlers authorize the actual resolved scope before accessing storage. This middleware should be applied AFTER DIDAuthMiddleware.
func PermissionCheckMiddleware ¶
func PermissionCheckMiddleware( policyService AccessPolicyServiceInterface, tagVCVerifier TagVCVerifierInterface, agentResolver AgentResolverInterface, didResolver DIDResolverInterface, config PermissionConfig, ) gin.HandlerFunc
PermissionCheckMiddleware creates a middleware that checks permissions before allowing requests to protected agents.
This middleware should be applied AFTER DIDAuthMiddleware so that the verified caller DID is available in the context.
The middleware:
- Extracts the verified caller DID from context (set by DIDAuthMiddleware)
- Resolves the target agent from the request path
- Evaluates access policies based on caller/target tags
- If a policy denies access, returns 403 Forbidden
- If no policy matches and DefaultDeny is false, allows the request (backward compat for untagged agents). When DefaultDeny is true, returns HTTP 403 with an opaque error. The unmatched (caller_tags, target_tags, function) tuple is logged at DEBUG in both modes; it is not included in the response body to avoid leaking tag taxonomy to denied callers.
func PrivilegedAccess ¶
func PrivilegedAccess(config AuthConfig) gin.HandlerFunc
PrivilegedAccess guards the control-plane endpoints that can execute arbitrary code (package install/update/uninstall) or read and write credentials (the encrypted secret store and agent .env files).
These routes would otherwise inherit the global posture, where an empty API key disables authentication entirely. Combined with the wildcard bind in Start(), that leaves anything on the same network able to make the control plane clone and build an arbitrary repository, or overwrite the credentials an installed agent runs with.
The rule:
API key configured -> the key is required, like any other protected route.
No API key -> only loopback callers may use these endpoints, so a
local CLI or desktop app keeps working untouched
while remote callers are told to configure a key.
The peer address is read from Request.RemoteAddr rather than c.ClientIP(): gin trusts every proxy by default, so ClientIP() honours a caller-supplied X-Forwarded-For header and a remote host could simply claim to be 127.0.0.1.
The corollary is that a reverse proxy sharing a host with the control plane makes every forwarded request look local, and the loopback rule then protects nothing. Such a deployment has to configure an API key; the docs say so.
func RateLimit ¶
func RateLimit(store *RateLimiterStore) gin.HandlerFunc
RateLimit returns a Gin middleware that enforces per-key rate limiting. The key is derived from the API key (if authenticated) or the client IP. When the limit is exceeded, the middleware responds with HTTP 429 and a Retry-After header.
func RequireMemoryScope ¶
RequireMemoryScope writes a 403 and aborts on denial; handlers must return when false.
func WriteMemoryAccessDenial ¶
func WriteMemoryAccessDenial(c *gin.Context, denial *MemoryAccessDenial)
WriteMemoryAccessDenial returns a denial before any storage or stream access.
Types ¶
type AccessPolicyServiceInterface ¶
type AccessPolicyServiceInterface interface {
EvaluateAccess(callerTags, targetTags []string, functionName string, inputParams map[string]any) *types.PolicyEvaluationResult
}
AccessPolicyServiceInterface defines the methods required for tag-based policy evaluation.
type AgentResolverInterface ¶
type AgentResolverInterface interface {
GetAgent(ctx context.Context, agentID string) (*types.AgentNode, error)
}
AgentResolverInterface provides methods for resolving agent information.
type AuthConfig ¶
type AuthConfig struct {
APIKey string
SkipPaths []string
SkipPrefixes []string
QueryAPIKeyAllowedPaths []string
}
AuthConfig mirrors server configuration for HTTP authentication.
type ContextKey ¶
type ContextKey string
ContextKey is the type for context keys used by this middleware.
const ( // VerifiedCallerDIDKey is the context key for the verified caller DID. VerifiedCallerDIDKey ContextKey = "verified_caller_did" // DIDAuthSkippedKey is set when DID auth was skipped (no DID claimed). DIDAuthSkippedKey ContextKey = "did_auth_skipped" )
const ( // CallerAgentIDKey is the context key for storing the resolved caller agent ID. CallerAgentIDKey ContextKey = "caller_agent_id" // MemoryPermissionResultKey is the context key for storing memory permission check results. MemoryPermissionResultKey ContextKey = "memory_permission_result" )
const ( // PermissionCheckResultKey is the context key for storing permission check results. PermissionCheckResultKey ContextKey = "permission_check_result" // TargetAgentKey is the context key for storing the resolved target agent. TargetAgentKey ContextKey = "target_agent" // TargetDIDKey is the context key for storing the target agent's DID. TargetDIDKey ContextKey = "target_did" )
type DIDAuthConfig ¶
type DIDAuthConfig struct {
// Enabled determines if DID authentication is active
Enabled bool
// TimestampWindowSeconds is the allowed time drift for signature timestamps (default: 300)
TimestampWindowSeconds int64
// SkipPaths are paths that bypass DID authentication
SkipPaths []string
}
DIDAuthConfig holds configuration for DID authentication middleware.
type DIDResolverInterface ¶
type DIDResolverInterface interface {
GenerateDIDWeb(agentID string) string
// ResolveAgentIDByDID looks up the agent ID associated with a DID.
// Returns empty string if the DID cannot be resolved.
ResolveAgentIDByDID(ctx context.Context, did string) string
}
DIDResolverInterface provides methods for resolving agent DIDs.
type DIDWebServiceInterface ¶
type DIDWebServiceInterface interface {
VerifyDIDOwnership(ctx context.Context, did string, message []byte, signature []byte) (bool, error)
}
DIDWebServiceInterface defines the methods required for DID verification. This interface allows the middleware to work with any DID service implementation.
type MemoryAccessDenial ¶
MemoryAccessDenial describes an authorization failure without writing a response. Hierarchical reads and wildcard events can skip denied scopes quietly.
func AuthorizeMemoryScope ¶
func AuthorizeMemoryScope(c *gin.Context, scope, scopeID string) *MemoryAccessDenial
AuthorizeMemoryScope checks the scope actually selected by a handler. Absence of middleware intentionally preserves the legacy behavior when auth is off.
type MemoryAgentLister ¶
type MemoryAgentLister interface {
ListAgents(context.Context, types.AgentFilters) ([]*types.AgentNode, error)
}
MemoryAgentLister supplies all registered nodes, including inactive/offline nodes. It is optional on AgentResolverInterface; without it actor checks fail closed.
type MemoryExecutionReader ¶
type MemoryExecutionReader interface {
QueryExecutionRecords(context.Context, types.ExecutionFilter) ([]*types.Execution, error)
}
MemoryExecutionReader is the minimal storage contract for event participation.
type MemoryPermissionConfig ¶
type MemoryPermissionConfig struct {
// Enabled determines if memory permission checking is active
Enabled bool
// EnforceScopeOwnership determines if scope ownership validation is enforced
EnforceScopeOwnership bool
// ExecutionReader supplies participation checks for wildcard memory events.
ExecutionReader MemoryExecutionReader
}
MemoryPermissionConfig holds configuration for memory permission checking.
type MemoryPermissionResult ¶
MemoryPermissionResult contains the result of a memory permission check.
func GetMemoryPermissionResult ¶
func GetMemoryPermissionResult(c *gin.Context) *MemoryPermissionResult
GetMemoryPermissionResult extracts the memory permission result from the gin context.
type PermissionCheckResult ¶
PermissionCheckResult contains the result of a permission check.
func GetPermissionCheckResult ¶
func GetPermissionCheckResult(c *gin.Context) *PermissionCheckResult
GetPermissionCheckResult extracts the permission check result from the gin context.
type PermissionConfig ¶
type PermissionConfig struct {
// Enabled determines if permission checking is active
Enabled bool
// DefaultDeny returns 403 instead of allowing fall-through when no
// access policy matches. Default false preserves backward compat.
DefaultDeny bool
}
PermissionConfig holds configuration for permission checking.
type RateLimitConfig ¶
type RateLimitConfig struct {
// RequestsPerSecond is the sustained rate of allowed requests per key.
RequestsPerSecond float64 `yaml:"requests_per_second" mapstructure:"requests_per_second"`
// BurstSize is the maximum burst of requests allowed above the sustained rate.
BurstSize int `yaml:"burst_size" mapstructure:"burst_size"`
// Enabled controls whether rate limiting is active. Defaults to true when
// any rate limit config is present.
Enabled *bool `yaml:"enabled,omitempty" mapstructure:"enabled"`
}
RateLimitConfig defines per-endpoint rate limiting parameters.
func (RateLimitConfig) IsEnabled ¶
func (c RateLimitConfig) IsEnabled() bool
IsEnabled returns whether this rate limit config is active.
type RateLimiterStore ¶
type RateLimiterStore struct {
// contains filtered or unexported fields
}
RateLimiterStore manages per-key rate limiters with idle eviction.
func NewRateLimiterStore ¶
func NewRateLimiterStore(rps float64, burst int, ttl time.Duration) *RateLimiterStore
NewRateLimiterStore creates a store for per-key rate limiters. Keys idle for longer than ttl are evicted by a background goroutine.
func (*RateLimiterStore) Allow ¶
func (s *RateLimiterStore) Allow(key string) bool
Allow checks whether the given key is within its rate limit.
func (*RateLimiterStore) Len ¶
func (s *RateLimiterStore) Len() int
Len returns the number of tracked keys (for testing/metrics).
func (*RateLimiterStore) Stop ¶
func (s *RateLimiterStore) Stop()
Stop halts the background eviction goroutine.
type RateLimitsConfig ¶
type RateLimitsConfig struct {
Execute RateLimitConfig `yaml:"execute" mapstructure:"execute"`
Discovery RateLimitConfig `yaml:"discovery" mapstructure:"discovery"`
BulkStatus RateLimitConfig `yaml:"bulk_status" mapstructure:"bulk_status"`
Global RateLimitConfig `yaml:"global" mapstructure:"global"`
}
RateLimitsConfig holds rate limit settings for different endpoint groups.
func DefaultRateLimitConfig ¶
func DefaultRateLimitConfig() RateLimitsConfig
DefaultRateLimitConfig returns sensible defaults for production use. Execute: 50 req/s burst 100 (per key) — generous for normal agent traffic. Discovery: 20 req/s burst 40 — read-heavy but cacheable. BulkStatus: 30 req/s burst 60 — moderate fan-out. Global: 200 req/s burst 400 — per-IP fallback for unauthenticated paths.
type TagVCVerifierInterface ¶
type TagVCVerifierInterface interface {
VerifyAgentTagVC(ctx context.Context, agentID string) (*types.AgentTagVCDocument, error)
}
TagVCVerifierInterface defines the methods required for verifying Agent Tag VCs.