Documentation
¶
Overview ¶
Package service provides business logic services for SentinelGate.
Package service contains application services.
Package service contains application services.
Package service contains the core proxy service implementation.
Package service contains application services.
Package service provides application-level services for SentinelGate.
Index ¶
- Constants
- Variables
- func DefaultPolicy() *policy.Policy
- func GenerateHelpText(decision policy.Decision) string
- func GenerateHelpURL(ruleID string) string
- func SeedDefaultPolicy(ctx context.Context, store policy.PolicyStore, logger *slog.Logger) error
- type AgentInfo
- type AgentRegistry
- type AuditOption
- func WithAdaptiveFlushThreshold(percent int) AuditOption
- func WithBatchSize(size int) AuditOption
- func WithChannelSize(size int) AuditOption
- func WithFlushInterval(interval time.Duration) AuditOption
- func WithSendTimeout(timeout time.Duration) AuditOption
- func WithWarningThreshold(percent int) AuditOption
- type AuditService
- type ClientFactory
- type CompiledRule
- type CompiledRulesSnapshot
- type CreateIdentityInput
- type DestinationRequest
- type DriftReport
- type GenerateKeyInput
- type GenerateKeyResult
- type IdentityService
- func (s *IdentityService) CreateIdentity(_ context.Context, input CreateIdentityInput) (*state.IdentityEntry, error)
- func (s *IdentityService) DeleteIdentity(_ context.Context, id string) ([]string, error)
- func (s *IdentityService) GenerateKey(_ context.Context, input GenerateKeyInput) (*GenerateKeyResult, error)
- func (s *IdentityService) GetIdentity(_ context.Context, id string) (*state.IdentityEntry, error)
- func (s *IdentityService) Init() error
- func (s *IdentityService) ListAllKeys(_ context.Context) ([]state.APIKeyEntry, error)
- func (s *IdentityService) ListIdentities(_ context.Context) ([]state.IdentityEntry, error)
- func (s *IdentityService) ListKeys(_ context.Context, identityID string) ([]state.APIKeyEntry, error)
- func (s *IdentityService) RevokeKey(_ context.Context, keyID string) (string, error)
- func (s *IdentityService) UpdateIdentity(_ context.Context, id string, input UpdateIdentityInput) (*state.IdentityEntry, error)
- func (s *IdentityService) VerifyKey(_ context.Context, cleartextKey string) (*state.APIKeyEntry, error)
- type OutboundAdminService
- func (s *OutboundAdminService) AddInterceptor(interceptor *action.OutboundInterceptor)
- func (s *OutboundAdminService) Create(ctx context.Context, rule *action.OutboundRule) (*action.OutboundRule, error)
- func (s *OutboundAdminService) Delete(ctx context.Context, id string) error
- func (s *OutboundAdminService) Get(ctx context.Context, id string) (*action.OutboundRule, error)
- func (s *OutboundAdminService) List(ctx context.Context) ([]action.OutboundRule, error)
- func (s *OutboundAdminService) LoadFromState(ctx context.Context, appState *state.AppState) error
- func (s *OutboundAdminService) ReloadRules(ctx context.Context)
- func (s *OutboundAdminService) Stats(ctx context.Context) (*OutboundStats, error)
- func (s *OutboundAdminService) TestRule(_ context.Context, rule action.OutboundRule, testDomain string, testIP string, ...) (bool, *action.OutboundRule)
- func (s *OutboundAdminService) Update(ctx context.Context, id string, rule *action.OutboundRule) (*action.OutboundRule, error)
- type OutboundStats
- type PolicyAdminService
- func (s *PolicyAdminService) Create(ctx context.Context, p *policy.Policy) (*policy.Policy, error)
- func (s *PolicyAdminService) Delete(ctx context.Context, id string) error
- func (s *PolicyAdminService) Get(ctx context.Context, id string) (*policy.Policy, error)
- func (s *PolicyAdminService) List(ctx context.Context) ([]policy.Policy, error)
- func (s *PolicyAdminService) LoadPoliciesFromState(ctx context.Context, appState *state.AppState) error
- func (s *PolicyAdminService) Update(ctx context.Context, id string, p *policy.Policy) (*policy.Policy, error)
- type PolicyEvaluateRequest
- type PolicyEvaluateResponse
- type PolicyEvaluation
- type PolicyEvaluationService
- type PolicyService
- type PolicyServiceOption
- type ProxyService
- type ResultCache
- type RuleIndex
- type Stats
- type StatsService
- func (s *StatsService) GetStats() Stats
- func (s *StatsService) RecordAllow()
- func (s *StatsService) RecordDeny()
- func (s *StatsService) RecordError()
- func (s *StatsService) RecordFramework(framework string)
- func (s *StatsService) RecordProtocol(protocol string)
- func (s *StatsService) RecordRateLimited()
- func (s *StatsService) Reset()
- type ToolBaselineEntry
- type ToolDiscoveryService
- func (s *ToolDiscoveryService) Cache() *upstream.ToolCache
- func (s *ToolDiscoveryService) DiscoverAll(ctx context.Context) error
- func (s *ToolDiscoveryService) DiscoverFromUpstream(ctx context.Context, upstreamID string) (int, error)
- func (s *ToolDiscoveryService) RefreshUpstream(ctx context.Context, upstreamID string) (int, error)
- func (s *ToolDiscoveryService) StartPeriodicRetry(ctx context.Context)
- func (s *ToolDiscoveryService) Stop()
- type ToolSecurityService
- func (s *ToolSecurityService) CaptureBaseline(_ context.Context) (int, error)
- func (s *ToolSecurityService) DetectDrift(_ context.Context) ([]DriftReport, error)
- func (s *ToolSecurityService) GetBaseline() map[string]ToolBaselineEntry
- func (s *ToolSecurityService) GetQuarantinedTools() []string
- func (s *ToolSecurityService) IsQuarantined(toolName string) bool
- func (s *ToolSecurityService) LoadFromState(appState *state.AppState)
- func (s *ToolSecurityService) Quarantine(toolName string) error
- func (s *ToolSecurityService) Unquarantine(toolName string) error
- type UpdateIdentityInput
- type UpstreamLister
- type UpstreamManager
- func (m *UpstreamManager) AllConnected() bool
- func (m *UpstreamManager) Close() error
- func (m *UpstreamManager) GetConnection(upstreamID string) (io.WriteCloser, io.ReadCloser, error)
- func (m *UpstreamManager) Init()
- func (m *UpstreamManager) Restart(ctx context.Context, upstreamID string) error
- func (m *UpstreamManager) SetBackoffBase(d time.Duration)
- func (m *UpstreamManager) Start(ctx context.Context, upstreamID string) error
- func (m *UpstreamManager) StartAll(ctx context.Context) error
- func (m *UpstreamManager) Status(upstreamID string) (upstream.ConnectionStatus, string)
- func (m *UpstreamManager) StatusAll() map[string]upstream.ConnectionStatus
- func (m *UpstreamManager) Stop(upstreamID string) error
- type UpstreamService
- func (s *UpstreamService) Add(ctx context.Context, u *upstream.Upstream) (*upstream.Upstream, error)
- func (s *UpstreamService) Delete(ctx context.Context, id string) error
- func (s *UpstreamService) Get(ctx context.Context, id string) (*upstream.Upstream, error)
- func (s *UpstreamService) List(ctx context.Context) ([]upstream.Upstream, error)
- func (s *UpstreamService) LoadFromState(ctx context.Context, appState *state.AppState) error
- func (s *UpstreamService) SetEnabled(ctx context.Context, id string, enabled bool) (*upstream.Upstream, error)
- func (s *UpstreamService) Update(ctx context.Context, id string, u *upstream.Upstream) (*upstream.Upstream, error)
Constants ¶
const DefaultPolicyName = "Default RBAC Policy"
DefaultPolicyName is the name used to identify the default policy.
const DevDefaultPolicyName = "dev-allow-all"
DevDefaultPolicyName is the name used by the dev-mode default policy.
Variables ¶
var ( ErrIdentityNotFound = errors.New("identity not found") ErrAPIKeyNotFound = errors.New("api key not found") ErrDuplicateName = errors.New("identity name already exists") ErrReadOnly = errors.New("cannot modify read-only resource") )
IdentityService errors.
var ErrDefaultPolicyDelete = errors.New("cannot delete the default policy")
ErrDefaultPolicyDelete is returned when attempting to delete the default policy.
var ErrDefaultRuleReadOnly = errors.New("default blocklist rules cannot be modified")
ErrDefaultRuleReadOnly is returned when attempting to modify or delete a default blocklist rule.
var ErrPolicyNotFound = errors.New("policy not found")
ErrPolicyNotFound is returned when a policy is not found.
Functions ¶
func DefaultPolicy ¶
DefaultPolicy returns a policy with the built-in RBAC rules. All roles are plain strings with no implicit privileges. Note: Rule IDs are left empty so they get auto-generated UUIDs on insert. The Name field is used to identify rule purpose.
func GenerateHelpText ¶
GenerateHelpText creates a human-readable help text from a policy decision.
func GenerateHelpURL ¶
GenerateHelpURL creates a URL pointing to the rule in the Admin UI.
func SeedDefaultPolicy ¶
SeedDefaultPolicy seeds the default policy if no policies exist in the store. This ensures the proxy has rules to evaluate on first boot. Returns nil if policies already exist (idempotent).
Types ¶
type AgentInfo ¶
type AgentInfo struct {
ID string `json:"id"`
Command string `json:"command"`
Args []string `json:"args"`
Framework string `json:"framework,omitempty"`
FailMode string `json:"fail_mode"`
StartedAt time.Time `json:"started_at"`
Status string `json:"status"` // "running", "stopped"
PID int `json:"pid,omitempty"`
}
AgentInfo represents a running agent process tracked by SentinelGate.
type AgentRegistry ¶
type AgentRegistry struct {
// contains filtered or unexported fields
}
AgentRegistry tracks running agent processes in memory. It is safe for concurrent use.
func NewAgentRegistry ¶
func NewAgentRegistry() *AgentRegistry
NewAgentRegistry creates a new empty AgentRegistry.
func (*AgentRegistry) Get ¶
func (r *AgentRegistry) Get(id string) (*AgentInfo, bool)
Get returns a single agent by ID. Returns nil, false if not found.
func (*AgentRegistry) List ¶
func (r *AgentRegistry) List() []AgentInfo
List returns all agents sorted by StartedAt descending (newest first).
func (*AgentRegistry) Register ¶
func (r *AgentRegistry) Register(info AgentInfo)
Register adds or updates an agent in the registry.
func (*AgentRegistry) SetStatus ¶
func (r *AgentRegistry) SetStatus(id, status string)
SetStatus updates the status of an agent by ID. Does nothing if the agent is not found.
func (*AgentRegistry) Unregister ¶
func (r *AgentRegistry) Unregister(id string)
Unregister removes an agent from the registry.
type AuditOption ¶
type AuditOption func(*AuditService)
AuditOption configures AuditService.
func WithAdaptiveFlushThreshold ¶
func WithAdaptiveFlushThreshold(percent int) AuditOption
WithAdaptiveFlushThreshold sets the channel depth % that triggers faster flushing. When channel depth exceeds this %, flush interval is reduced to 1/4 normal. Default is 80%. Set to 0 to disable adaptive flushing.
func WithBatchSize ¶
func WithBatchSize(size int) AuditOption
WithBatchSize sets the number of records to batch before writing.
func WithChannelSize ¶
func WithChannelSize(size int) AuditOption
WithChannelSize sets the size of the audit channel buffer.
func WithFlushInterval ¶
func WithFlushInterval(interval time.Duration) AuditOption
WithFlushInterval sets the interval to flush pending records.
func WithSendTimeout ¶
func WithSendTimeout(timeout time.Duration) AuditOption
WithSendTimeout sets the backpressure timeout. 0 = drop immediately (no blocking), >0 = block up to this duration before dropping.
func WithWarningThreshold ¶
func WithWarningThreshold(percent int) AuditOption
WithWarningThreshold sets the channel depth warning percentage (0-100). A warning is logged when channel depth exceeds this percentage of capacity.
type AuditService ¶
type AuditService struct {
// contains filtered or unexported fields
}
AuditService provides async audit logging with a buffered channel and background worker. Tool calls are logged without blocking the proxy hot path.
func NewAuditService ¶
func NewAuditService(store audit.AuditStore, logger *slog.Logger, opts ...AuditOption) *AuditService
NewAuditService creates a new AuditService with the given store and options.
func (*AuditService) ChannelCapacity ¶
func (s *AuditService) ChannelCapacity() int
ChannelCapacity returns channel buffer size (for percentage calculation).
func (*AuditService) ChannelDepth ¶
func (s *AuditService) ChannelDepth() int
ChannelDepth returns current channel usage (for monitoring).
func (*AuditService) DroppedRecords ¶
func (s *AuditService) DroppedRecords() int64
DroppedRecords returns total dropped records (for metrics/alerting).
func (*AuditService) Record ¶
func (s *AuditService) Record(record audit.AuditRecord)
Record sends an audit record to the background worker. Applies backpressure: attempts fast non-blocking send, then blocks up to sendTimeout. If timeout expires, record is dropped and counted.
func (*AuditService) Start ¶
func (s *AuditService) Start(ctx context.Context)
Start begins the background worker that batches and writes audit records.
func (*AuditService) Stop ¶
func (s *AuditService) Stop()
Stop signals the worker to stop and waits for it to finish. Pending records are flushed before returning.
type ClientFactory ¶
ClientFactory creates an MCPClient from an upstream configuration. The default factory creates StdioClient for stdio type and HTTPClient for http type.
type CompiledRule ¶
type CompiledRule struct {
ID string
Name string // Human-readable rule name
Priority int
ToolMatch string // Glob pattern for tool name matching
Program cel.Program // Pre-compiled CEL program
Action policy.Action
ApprovalTimeout time.Duration // How long to wait for approval (0 = default 5m)
TimeoutAction policy.Action // What to do when approval times out (deny/allow)
}
CompiledRule represents a pre-compiled policy rule ready for evaluation.
type CompiledRulesSnapshot ¶
type CompiledRulesSnapshot struct {
Rules []CompiledRule // All rules sorted by priority (kept for compatibility)
Index *RuleIndex // Index for fast lookup
}
CompiledRulesSnapshot is the immutable snapshot stored in atomic.Value.
type CreateIdentityInput ¶
CreateIdentityInput holds the input for creating an identity.
type DestinationRequest ¶
type DestinationRequest struct {
URL string `json:"url,omitempty"`
Domain string `json:"domain,omitempty"`
IP string `json:"ip,omitempty"`
Port int `json:"port,omitempty"`
Scheme string `json:"scheme,omitempty"`
Path string `json:"path,omitempty"`
Command string `json:"command,omitempty"`
}
DestinationRequest represents destination details for an evaluation request.
type DriftReport ¶
type DriftReport struct {
ToolName string `json:"tool_name"`
DriftType string `json:"drift_type"` // "added", "removed", "changed"
Baseline interface{} `json:"baseline,omitempty"`
Current interface{} `json:"current,omitempty"`
}
DriftReport describes a difference between the baseline and current tool set.
type GenerateKeyInput ¶
GenerateKeyInput holds the input for generating an API key.
type GenerateKeyResult ¶
type GenerateKeyResult struct {
KeyEntry state.APIKeyEntry `json:"key_entry"`
CleartextKey string `json:"cleartext_key"`
}
GenerateKeyResult holds the result of key generation. The CleartextKey is returned exactly once and never stored.
type IdentityService ¶
type IdentityService struct {
// contains filtered or unexported fields
}
IdentityService provides CRUD operations on identities and API keys with Argon2id key hashing and persistence to state.json.
func NewIdentityService ¶
func NewIdentityService(stateStore *state.FileStateStore, logger *slog.Logger) *IdentityService
NewIdentityService creates a new IdentityService.
func (*IdentityService) CreateIdentity ¶
func (s *IdentityService) CreateIdentity(_ context.Context, input CreateIdentityInput) (*state.IdentityEntry, error)
CreateIdentity creates a new identity and persists it to state.json.
func (*IdentityService) DeleteIdentity ¶
DeleteIdentity removes an identity and all its API keys.
func (*IdentityService) GenerateKey ¶
func (s *IdentityService) GenerateKey(_ context.Context, input GenerateKeyInput) (*GenerateKeyResult, error)
GenerateKey creates a new API key for the given identity. The cleartext key is returned exactly once in GenerateKeyResult and never stored. Only the Argon2id hash is persisted.
func (*IdentityService) GetIdentity ¶
func (s *IdentityService) GetIdentity(_ context.Context, id string) (*state.IdentityEntry, error)
GetIdentity returns a single identity by ID.
func (*IdentityService) Init ¶
func (s *IdentityService) Init() error
Init loads identities and API keys from state.json into memory. Must be called once after construction, before serving requests.
func (*IdentityService) ListAllKeys ¶
func (s *IdentityService) ListAllKeys(_ context.Context) ([]state.APIKeyEntry, error)
ListAllKeys returns all API keys across all identities.
func (*IdentityService) ListIdentities ¶
func (s *IdentityService) ListIdentities(_ context.Context) ([]state.IdentityEntry, error)
ListIdentities returns all identities.
func (*IdentityService) ListKeys ¶
func (s *IdentityService) ListKeys(_ context.Context, identityID string) ([]state.APIKeyEntry, error)
ListKeys returns all API keys for a given identity.
func (*IdentityService) RevokeKey ¶
RevokeKey marks an API key as revoked. It does not delete it. Returns the key hash of the revoked key so callers can sync in-memory stores.
func (*IdentityService) UpdateIdentity ¶
func (s *IdentityService) UpdateIdentity(_ context.Context, id string, input UpdateIdentityInput) (*state.IdentityEntry, error)
UpdateIdentity updates an existing identity and persists the change.
func (*IdentityService) VerifyKey ¶
func (s *IdentityService) VerifyKey(_ context.Context, cleartextKey string) (*state.APIKeyEntry, error)
VerifyKey checks if a cleartext key matches any non-revoked API key. Returns the matching key entry or ErrAPIKeyNotFound.
type OutboundAdminService ¶
type OutboundAdminService struct {
// contains filtered or unexported fields
}
OutboundAdminService provides CRUD operations on outbound rules with validation, default rule protection, state.json persistence, and live interceptor reload across all registered interceptors.
func NewOutboundAdminService ¶
func NewOutboundAdminService( store action.OutboundRuleStore, stateStore *state.FileStateStore, logger *slog.Logger, interceptors ...*action.OutboundInterceptor, ) *OutboundAdminService
NewOutboundAdminService creates a new OutboundAdminService. It accepts zero or more OutboundInterceptor instances that will all receive rule reload notifications when rules are created/updated/deleted.
func (*OutboundAdminService) AddInterceptor ¶
func (s *OutboundAdminService) AddInterceptor(interceptor *action.OutboundInterceptor)
AddInterceptor registers an additional interceptor for rule reload notifications. This is used when an interceptor is created after the OutboundAdminService (e.g., the HTTP gateway outbound interceptor created inside a conditional block).
func (*OutboundAdminService) Create ¶
func (s *OutboundAdminService) Create(ctx context.Context, rule *action.OutboundRule) (*action.OutboundRule, error)
Create validates, generates an ID, persists, and live-reloads a new outbound rule.
func (*OutboundAdminService) Delete ¶
func (s *OutboundAdminService) Delete(ctx context.Context, id string) error
Delete removes an outbound rule by ID. Default blocklist rules cannot be deleted.
func (*OutboundAdminService) Get ¶
func (s *OutboundAdminService) Get(ctx context.Context, id string) (*action.OutboundRule, error)
Get returns a single outbound rule by ID. Returns action.ErrOutboundRuleNotFound if not found.
func (*OutboundAdminService) List ¶
func (s *OutboundAdminService) List(ctx context.Context) ([]action.OutboundRule, error)
List returns all outbound rules from the store.
func (*OutboundAdminService) LoadFromState ¶
LoadFromState loads persisted outbound rules from AppState into the in-memory store. If no persisted rules exist, loads the default blocklist rules with ReadOnly flag. After loading, it reloads the interceptor with the loaded rules.
func (*OutboundAdminService) ReloadRules ¶
func (s *OutboundAdminService) ReloadRules(ctx context.Context)
ReloadRules triggers an explicit rule reload across all registered interceptors. Used when a new interceptor is registered after initial boot.
func (*OutboundAdminService) Stats ¶
func (s *OutboundAdminService) Stats(ctx context.Context) (*OutboundStats, error)
Stats returns aggregate statistics about outbound rules.
func (*OutboundAdminService) TestRule ¶
func (s *OutboundAdminService) TestRule(_ context.Context, rule action.OutboundRule, testDomain string, testIP string, testPort int) (bool, *action.OutboundRule)
TestRule evaluates whether the given destination would be matched by the given rule. Returns (true, rule) if the destination matches, (false, nil) otherwise.
func (*OutboundAdminService) Update ¶
func (s *OutboundAdminService) Update(ctx context.Context, id string, rule *action.OutboundRule) (*action.OutboundRule, error)
Update validates and updates an existing outbound rule. Default blocklist rules only allow toggling the Enabled field.
type OutboundStats ¶
type OutboundStats struct {
TotalRules int `json:"total_rules"`
EnabledRules int `json:"enabled_rules"`
BlocklistRules int `json:"blocklist_rules"`
AllowlistRules int `json:"allowlist_rules"`
DefaultRules int `json:"default_rules"`
CustomRules int `json:"custom_rules"`
}
OutboundStats provides aggregate statistics about outbound rules.
type PolicyAdminService ¶
type PolicyAdminService struct {
// contains filtered or unexported fields
}
PolicyAdminService provides CRUD operations on policies with validation, default policy protection, and persistence to state.json. After every mutation it calls PolicyService.Reload() to hot-reload CEL rules.
func NewPolicyAdminService ¶
func NewPolicyAdminService( store policy.PolicyStore, stateStore *state.FileStateStore, policyService *PolicyService, logger *slog.Logger, ) *PolicyAdminService
NewPolicyAdminService creates a new PolicyAdminService.
func (*PolicyAdminService) Create ¶
Create creates a new policy with the given configuration. Generates UUID for the policy and each rule, sets timestamps, persists to state.json, and triggers a hot-reload.
func (*PolicyAdminService) Delete ¶
func (s *PolicyAdminService) Delete(ctx context.Context, id string) error
Delete removes a policy by ID. The default policy cannot be deleted. Returns ErrDefaultPolicyDelete if attempting to delete the default policy. Returns ErrPolicyNotFound if the policy does not exist.
func (*PolicyAdminService) Get ¶
Get returns a single policy by ID with its rules. Returns ErrPolicyNotFound if the policy does not exist.
func (*PolicyAdminService) LoadPoliciesFromState ¶
func (s *PolicyAdminService) LoadPoliciesFromState(ctx context.Context, appState *state.AppState) error
LoadPoliciesFromState loads policy entries from state.json into the in-memory policy store. Entries are grouped by policy name (extracted from the "PolicyName: RuleName" format). Policies already present in the store (e.g. seeded from YAML config) are skipped to avoid duplicates. After loading, it triggers a PolicyService.Reload() to compile the rules.
func (*PolicyAdminService) Update ¶
func (s *PolicyAdminService) Update(ctx context.Context, id string, p *policy.Policy) (*policy.Policy, error)
Update updates an existing policy. Preserves immutable fields (ID, CreatedAt), updates timestamp, persists, and triggers reload. Returns ErrPolicyNotFound if the policy does not exist.
type PolicyEvaluateRequest ¶
type PolicyEvaluateRequest struct {
ActionType string `json:"action_type"`
ActionName string `json:"action_name"`
Protocol string `json:"protocol"`
Framework string `json:"framework,omitempty"`
Gateway string `json:"gateway,omitempty"`
Arguments map[string]interface{} `json:"arguments,omitempty"`
IdentityName string `json:"identity_name"`
IdentityRoles []string `json:"identity_roles"`
Destination *DestinationRequest `json:"destination,omitempty"`
}
PolicyEvaluateRequest represents a policy evaluation request from the API. It accepts a CanonicalAction representation and identity information.
type PolicyEvaluateResponse ¶
type PolicyEvaluateResponse struct {
Decision string `json:"decision"`
RuleID string `json:"rule_id,omitempty"`
RuleName string `json:"rule_name,omitempty"`
Reason string `json:"reason"`
HelpURL string `json:"help_url,omitempty"`
HelpText string `json:"help_text,omitempty"`
RequestID string `json:"request_id"`
LatencyMs int64 `json:"latency_ms"`
}
PolicyEvaluateResponse represents the structured result of a policy evaluation.
type PolicyEvaluation ¶
type PolicyEvaluation struct {
RequestID string `json:"request_id"`
ActionType string `json:"action_type"`
ActionName string `json:"action_name"`
Protocol string `json:"protocol"`
Gateway string `json:"gateway"`
Framework string `json:"framework,omitempty"`
Decision string `json:"decision"`
RuleID string `json:"rule_id,omitempty"`
LatencyMs int64 `json:"latency_ms"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
PolicyEvaluation represents a stored evaluation record.
type PolicyEvaluationService ¶
type PolicyEvaluationService struct {
// contains filtered or unexported fields
}
PolicyEvaluationService handles policy evaluation requests from the API. It wraps the core PolicyService, adds evaluation tracking (latency, protocol), and generates helpful deny messages.
func NewPolicyEvaluationService ¶
func NewPolicyEvaluationService( engine policy.PolicyEngine, store policy.PolicyStore, stateStore *state.FileStateStore, logger *slog.Logger, ) *PolicyEvaluationService
NewPolicyEvaluationService creates a new PolicyEvaluationService.
func (*PolicyEvaluationService) Evaluate ¶
func (s *PolicyEvaluationService) Evaluate(ctx context.Context, req PolicyEvaluateRequest) (*PolicyEvaluateResponse, error)
Evaluate processes a policy evaluation request. It converts the request to an EvaluationContext, evaluates it, and returns a structured response with helpful deny information.
func (*PolicyEvaluationService) GetEvaluationStatus ¶
func (s *PolicyEvaluationService) GetEvaluationStatus(requestID string) *PolicyEvaluation
GetEvaluationStatus returns the status of a policy evaluation by request ID. Returns nil if the evaluation is not found.
type PolicyService ¶
type PolicyService struct {
// contains filtered or unexported fields
}
PolicyService implements policy.PolicyEngine with CEL-based rule evaluation. Rules are compiled at load time and evaluated in priority order (highest first). Supports hot-reload via Reload() method for runtime policy updates. Uses atomic.Value for lock-free reads on the hot path.
func NewPolicyService ¶
func NewPolicyService(ctx context.Context, store policy.PolicyStore, logger *slog.Logger, opts ...PolicyServiceOption) (*PolicyService, error)
NewPolicyService creates a new PolicyService that loads and compiles rules from the store. The ctx parameter is used for the initial policy loading and can be cancelled to abort startup.
func (*PolicyService) Evaluate ¶
func (s *PolicyService) Evaluate(ctx context.Context, evalCtx policy.EvaluationContext) (policy.Decision, error)
Evaluate evaluates a tool call against loaded policies. Returns Decision with Allowed=true/false and reason. Rules are evaluated in priority order, first matching rule wins. Default deny if no rules match. Uses lock-free atomic.Value read for high performance on the hot path. Results are cached by tool name, roles, arguments, identity, action type, and protocol.
func (*PolicyService) Reload ¶
func (s *PolicyService) Reload(ctx context.Context) error
Reload reloads and recompiles all policies from the store. This method is thread-safe and can be called concurrently with Evaluate. Only enabled policies are included in the compiled ruleset. Uses atomic.Value.Store for lock-free publish to readers.
func (*PolicyService) ValidateRules ¶
func (s *PolicyService) ValidateRules(rules []policy.Rule) error
ValidateRules checks that all CEL conditions in the given rules are valid. This should be called before persisting policies to prevent invalid CEL from poisoning the policy store. Returns an error describing the first invalid rule.
type PolicyServiceOption ¶
type PolicyServiceOption func(*PolicyService)
PolicyServiceOption configures PolicyService.
func WithCacheSize ¶
func WithCacheSize(size int) PolicyServiceOption
WithCacheSize sets the maximum number of cached decisions.
type ProxyService ¶
type ProxyService struct {
// contains filtered or unexported fields
}
ProxyService orchestrates bidirectional message proxying between the client and the upstream MCP server.
func NewProxyService ¶
func NewProxyService(client outbound.MCPClient, interceptor proxy.MessageInterceptor, logger *slog.Logger) *ProxyService
NewProxyService creates a new proxy service with the given dependencies.
func (*ProxyService) Run ¶
Run starts the bidirectional proxy between client and upstream server. It blocks until the context is cancelled or an error occurs. clientIn is where we read messages from (typically os.Stdin). clientOut is where we write messages to (typically os.Stdout).
When client is nil (multi-upstream mode), the interceptor chain handles all routing via the UpstreamRouter. Messages are processed through the interceptor and responses are written back to clientOut without needing an upstream pipe.
type ResultCache ¶
type ResultCache struct {
// contains filtered or unexported fields
}
ResultCache provides bounded LRU caching for CEL evaluation results. Thread-safe with Mutex (both Get and Put mutate LRU order).
func NewResultCache ¶
func NewResultCache(maxSize int) *ResultCache
NewResultCache creates a new LRU cache with the given max size.
func (*ResultCache) Clear ¶
func (c *ResultCache) Clear()
Clear empties the cache. Called on policy reload.
func (*ResultCache) Get ¶
func (c *ResultCache) Get(key uint64) (policy.Decision, bool)
Get retrieves a cached decision. Returns (decision, true) on hit, (zero, false) on miss. On hit, the entry is promoted to the head (most recently used).
type RuleIndex ¶
type RuleIndex struct {
Exact map[string][]CompiledRule // "read_file" -> rules for exact match
Wildcard []CompiledRule // "*" or glob patterns, evaluated in priority order
}
RuleIndex provides O(1) lookup for exact tool matches.
type Stats ¶
type Stats struct {
Allowed int64 `json:"allowed"`
Denied int64 `json:"denied"`
RateLimited int64 `json:"rate_limited"`
Errors int64 `json:"errors"`
ProtocolCounts map[string]int64 `json:"protocol_counts"`
FrameworkCounts map[string]int64 `json:"framework_counts"`
}
Stats holds a snapshot of all counters at a point in time.
type StatsService ¶
type StatsService struct {
// contains filtered or unexported fields
}
StatsService tracks runtime statistics using lock-free atomic counters. All counter operations are safe for concurrent access from multiple goroutines.
func NewStatsService ¶
func NewStatsService() *StatsService
NewStatsService creates a new StatsService with all counters initialized to zero.
func (*StatsService) GetStats ¶
func (s *StatsService) GetStats() Stats
GetStats returns a snapshot of all counters. The snapshot is consistent per-counter but not atomically across all counters.
func (*StatsService) RecordAllow ¶
func (s *StatsService) RecordAllow()
RecordAllow increments the allowed counter.
func (*StatsService) RecordDeny ¶
func (s *StatsService) RecordDeny()
RecordDeny increments the denied counter.
func (*StatsService) RecordError ¶
func (s *StatsService) RecordError()
RecordError increments the error counter.
func (*StatsService) RecordFramework ¶
func (s *StatsService) RecordFramework(framework string)
RecordFramework increments the counter for the given framework. Empty strings are skipped.
func (*StatsService) RecordProtocol ¶
func (s *StatsService) RecordProtocol(protocol string)
RecordProtocol increments the counter for the given protocol.
func (*StatsService) RecordRateLimited ¶
func (s *StatsService) RecordRateLimited()
RecordRateLimited increments the rate-limited counter.
type ToolBaselineEntry ¶
type ToolBaselineEntry struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema interface{} `json:"input_schema"`
CapturedAt time.Time `json:"captured_at"`
}
ToolBaselineEntry stores a snapshot of a tool's schema at baseline capture time.
type ToolDiscoveryService ¶
type ToolDiscoveryService struct {
// contains filtered or unexported fields
}
ToolDiscoveryService discovers tools from connected upstream MCP servers and maintains a shared ToolCache for routing and tools/list aggregation.
func NewToolDiscoveryService ¶
func NewToolDiscoveryService( upstreamService UpstreamLister, cache *upstream.ToolCache, clientFactory ClientFactory, logger *slog.Logger, ) *ToolDiscoveryService
NewToolDiscoveryService creates a new ToolDiscoveryService.
func (*ToolDiscoveryService) Cache ¶
func (s *ToolDiscoveryService) Cache() *upstream.ToolCache
Cache returns the shared tool cache.
func (*ToolDiscoveryService) DiscoverAll ¶
func (s *ToolDiscoveryService) DiscoverAll(ctx context.Context) error
DiscoverAll discovers tools from all enabled upstreams.
func (*ToolDiscoveryService) DiscoverFromUpstream ¶
func (s *ToolDiscoveryService) DiscoverFromUpstream(ctx context.Context, upstreamID string) (int, error)
DiscoverFromUpstream discovers tools from a single upstream by ID. It creates a temporary MCP client, sends a tools/list request, parses the response, checks for conflicts, and stores non-conflicting tools in the cache. Returns the number of non-conflicting tools stored.
func (*ToolDiscoveryService) RefreshUpstream ¶
RefreshUpstream re-discovers tools from an upstream, replacing the cached tools. This is the same as DiscoverFromUpstream but logs as a refresh operation.
func (*ToolDiscoveryService) StartPeriodicRetry ¶
func (s *ToolDiscoveryService) StartPeriodicRetry(ctx context.Context)
StartPeriodicRetry starts a background goroutine that periodically retries discovery for upstreams with 0 cached tools.
func (*ToolDiscoveryService) Stop ¶
func (s *ToolDiscoveryService) Stop()
Stop cancels the discovery service context and stops periodic retry. Safe to call multiple times.
type ToolSecurityService ¶
type ToolSecurityService struct {
// contains filtered or unexported fields
}
ToolSecurityService manages tool baseline capture, drift detection, and quarantine.
func NewToolSecurityService ¶
func NewToolSecurityService(toolCache *upstream.ToolCache, stateStore *state.FileStateStore, logger *slog.Logger) *ToolSecurityService
NewToolSecurityService creates a new ToolSecurityService.
func (*ToolSecurityService) CaptureBaseline ¶
func (s *ToolSecurityService) CaptureBaseline(_ context.Context) (int, error)
CaptureBaseline snapshots all current tools from the ToolCache as the baseline.
func (*ToolSecurityService) DetectDrift ¶
func (s *ToolSecurityService) DetectDrift(_ context.Context) ([]DriftReport, error)
DetectDrift compares the current ToolCache tools against the stored baseline.
func (*ToolSecurityService) GetBaseline ¶
func (s *ToolSecurityService) GetBaseline() map[string]ToolBaselineEntry
GetBaseline returns the current baseline entries.
func (*ToolSecurityService) GetQuarantinedTools ¶
func (s *ToolSecurityService) GetQuarantinedTools() []string
GetQuarantinedTools returns the list of quarantined tool names.
func (*ToolSecurityService) IsQuarantined ¶
func (s *ToolSecurityService) IsQuarantined(toolName string) bool
IsQuarantined returns true if the tool is quarantined. Thread-safe for hot-path use.
func (*ToolSecurityService) LoadFromState ¶
func (s *ToolSecurityService) LoadFromState(appState *state.AppState)
LoadFromState restores baseline and quarantine state from a previously loaded AppState.
func (*ToolSecurityService) Quarantine ¶
func (s *ToolSecurityService) Quarantine(toolName string) error
Quarantine marks a tool as quarantined and persists the change.
func (*ToolSecurityService) Unquarantine ¶
func (s *ToolSecurityService) Unquarantine(toolName string) error
Unquarantine removes quarantine from a tool and persists the change.
type UpdateIdentityInput ¶
type UpdateIdentityInput struct {
Name *string `json:"name,omitempty"`
Roles []string `json:"roles,omitempty"`
}
UpdateIdentityInput holds the input for updating an identity.
type UpstreamLister ¶
type UpstreamLister interface {
List(ctx context.Context) ([]upstream.Upstream, error)
Get(ctx context.Context, id string) (*upstream.Upstream, error)
}
UpstreamLister provides a list of configured upstreams for discovery.
type UpstreamManager ¶
type UpstreamManager struct {
// contains filtered or unexported fields
}
UpstreamManager handles lifecycle management of multiple MCP server connections. It provides start, stop, restart, health monitoring, and exponential backoff retry.
func NewUpstreamManager ¶
func NewUpstreamManager(upstreamService *UpstreamService, clientFactory ClientFactory, logger *slog.Logger) *UpstreamManager
NewUpstreamManager creates a new UpstreamManager.
func NewUpstreamManagerUnstarted ¶
func NewUpstreamManagerUnstarted(upstreamService *UpstreamService, clientFactory ClientFactory, logger *slog.Logger) *UpstreamManager
NewUpstreamManagerUnstarted creates a new UpstreamManager without signaling background goroutines to start. The caller MUST call Init() after configuring fields. This is intended for tests that need to override timing parameters.
func (*UpstreamManager) AllConnected ¶
func (m *UpstreamManager) AllConnected() bool
AllConnected returns true if at least one upstream is connected. Returns false when all upstreams are disconnected (for 503 status check).
func (*UpstreamManager) Close ¶
func (m *UpstreamManager) Close() error
Close stops all upstreams and cancels the manager context.
func (*UpstreamManager) GetConnection ¶
func (m *UpstreamManager) GetConnection(upstreamID string) (io.WriteCloser, io.ReadCloser, error)
GetConnection returns the stdin/stdout for a connected upstream.
func (*UpstreamManager) Init ¶
func (m *UpstreamManager) Init()
Init signals background goroutines that configuration is ready to be read. This is called automatically by NewUpstreamManager. Tests that need to override configuration fields (e.g. stabilityCheckInterval) should use NewUpstreamManagerUnstarted to create the manager, set fields, then call Init().
func (*UpstreamManager) Restart ¶
func (m *UpstreamManager) Restart(ctx context.Context, upstreamID string) error
Restart stops and then starts an upstream.
func (*UpstreamManager) SetBackoffBase ¶
func (m *UpstreamManager) SetBackoffBase(d time.Duration)
SetBackoffBase sets the base backoff duration (exported for integration tests).
func (*UpstreamManager) Start ¶
func (m *UpstreamManager) Start(ctx context.Context, upstreamID string) error
Start starts an individual upstream by ID. If the connection fails, it schedules a retry with exponential backoff.
func (*UpstreamManager) StartAll ¶
func (m *UpstreamManager) StartAll(ctx context.Context) error
StartAll starts all enabled upstreams from the upstream service.
func (*UpstreamManager) Status ¶
func (m *UpstreamManager) Status(upstreamID string) (upstream.ConnectionStatus, string)
Status returns the current status and last error for an upstream.
func (*UpstreamManager) StatusAll ¶
func (m *UpstreamManager) StatusAll() map[string]upstream.ConnectionStatus
StatusAll returns the status of all managed upstreams.
func (*UpstreamManager) Stop ¶
func (m *UpstreamManager) Stop(upstreamID string) error
Stop stops an individual upstream by ID.
type UpstreamService ¶
type UpstreamService struct {
// contains filtered or unexported fields
}
UpstreamService provides CRUD operations on upstream configurations with validation and persistence to state.json.
func NewUpstreamService ¶
func NewUpstreamService(store upstream.UpstreamStore, stateStore *state.FileStateStore, logger *slog.Logger) *UpstreamService
NewUpstreamService creates a new UpstreamService.
func (*UpstreamService) Add ¶
func (s *UpstreamService) Add(ctx context.Context, u *upstream.Upstream) (*upstream.Upstream, error)
Add validates and creates a new upstream, persisting the change to state.json. Generates a UUID, sets timestamps, checks name uniqueness, and validates configuration.
func (*UpstreamService) Delete ¶
func (s *UpstreamService) Delete(ctx context.Context, id string) error
Delete removes an upstream by ID and persists the change. Returns upstream.ErrUpstreamNotFound if the upstream does not exist.
func (*UpstreamService) Get ¶
Get returns a single upstream by ID. Returns upstream.ErrUpstreamNotFound if the upstream does not exist.
func (*UpstreamService) LoadFromState ¶
LoadFromState populates the in-memory store from the given AppState. Called at boot to restore persisted upstream configuration. The ctx parameter enables cancellation during startup.
func (*UpstreamService) SetEnabled ¶
func (s *UpstreamService) SetEnabled(ctx context.Context, id string, enabled bool) (*upstream.Upstream, error)
SetEnabled toggles the enabled flag on an upstream and persists the change. Returns the updated upstream.