service

package
v1.0.0-beta.1 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2026 License: AGPL-3.0 Imports: 31 Imported by: 0

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

View Source
const DefaultPolicyName = "Default RBAC Policy"

DefaultPolicyName is the name used to identify the default policy.

View Source
const DevDefaultPolicyName = "dev-allow-all"

DevDefaultPolicyName is the name used by the dev-mode default policy.

Variables

View Source
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.

View Source
var ErrDefaultPolicyDelete = errors.New("cannot delete the default policy")

ErrDefaultPolicyDelete is returned when attempting to delete the default policy.

View Source
var ErrDefaultRuleReadOnly = errors.New("default blocklist rules cannot be modified")

ErrDefaultRuleReadOnly is returned when attempting to modify or delete a default blocklist rule.

View Source
var ErrPolicyNotFound = errors.New("policy not found")

ErrPolicyNotFound is returned when a policy is not found.

Functions

func DefaultPolicy

func DefaultPolicy() *policy.Policy

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

func GenerateHelpText(decision policy.Decision) string

GenerateHelpText creates a human-readable help text from a policy decision.

func GenerateHelpURL

func GenerateHelpURL(ruleID string) string

GenerateHelpURL creates a URL pointing to the rule in the Admin UI.

func SeedDefaultPolicy

func SeedDefaultPolicy(ctx context.Context, store policy.PolicyStore, logger *slog.Logger) error

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

type ClientFactory func(u *upstream.Upstream) (outbound.MCPClient, error)

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

type CreateIdentityInput struct {
	Name  string   `json:"name"`
	Roles []string `json:"roles"`
}

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

type GenerateKeyInput struct {
	IdentityID string `json:"identity_id"`
	Name       string `json:"name"`
}

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

CreateIdentity creates a new identity and persists it to state.json.

func (*IdentityService) DeleteIdentity

func (s *IdentityService) DeleteIdentity(_ context.Context, id string) ([]string, error)

DeleteIdentity removes an identity and all its API keys.

func (*IdentityService) GenerateKey

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

func (s *IdentityService) RevokeKey(_ context.Context, keyID string) (string, error)

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

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

Get returns a single outbound rule by ID. Returns action.ErrOutboundRuleNotFound if not found.

func (*OutboundAdminService) List

List returns all outbound rules from the store.

func (*OutboundAdminService) LoadFromState

func (s *OutboundAdminService) LoadFromState(ctx context.Context, appState *state.AppState) error

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

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

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) List

List returns all policies from the store.

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

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

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

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

func (p *ProxyService) Run(ctx context.Context, clientIn io.Reader, clientOut io.Writer) error

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).

func (*ResultCache) Put

func (c *ResultCache) Put(key uint64, decision policy.Decision)

Put stores a decision in the cache. If at capacity, the least recently used entry is evicted.

func (*ResultCache) Size

func (c *ResultCache) Size() int

Size returns current cache size.

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.

func (*StatsService) Reset

func (s *StatsService) Reset()

Reset sets all counters to zero.

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

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

func (s *ToolDiscoveryService) RefreshUpstream(ctx context.Context, upstreamID string) (int, error)

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

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) List

List returns all configured upstreams from the in-memory store.

func (*UpstreamService) LoadFromState

func (s *UpstreamService) LoadFromState(ctx context.Context, appState *state.AppState) error

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.

func (*UpstreamService) Update

Update validates and updates an existing upstream, persisting the change. Checks name uniqueness excluding the upstream being updated.

Jump to

Keyboard shortcuts

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