policy

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultSessionTTL            = 1 * time.Hour
	DefaultMaxRequestBodyBytes   = int64(1 << 20) // 1 MiB
	DefaultAuditDefaultLimit     = 100
	DefaultAuditMaxLimit         = 1000
	DefaultNotifyDispatchTimeout = 10 * time.Second
)

Defaults applied when the corresponding config key is unset. These preserve v0.4.0 behavior so a policy file written against v0.4.0 keeps its exact runtime semantics after upgrade.

View Source
const (
	OverrideModeMerge   = "merge"
	OverrideModeReplace = "replace"
)

Override-mode constants. We accept the literal strings in YAML; an unknown value falls back to the merge default with a warning at load time.

View Source
const DefaultPollInterval = 2 * time.Second

DefaultPollInterval is the fallback poll cadence used when fsnotify cannot be initialised (e.g. inotify exhaustion, unsupported filesystem). fsnotify is preferred because it reacts immediately to atomic-replace edits; polling is bounded-latency but imposes real wall-clock delay between `kubectl apply` and the Guard noticing the new policy.

View Source
const LocalTenantID = "local"

LocalTenantID is the single tenant identifier used by AgentGuard v0.5. Multi-tenant routing arrives in v0.6 (issue tracked in .audit/v05_decisions.md, "PolicyProvider interface design"); until then the proxy passes "local" on every Engine.Check call and FilePolicyProvider rejects every other tenant value with ErrTenantNotFound.

An empty tenantID is treated as a synonym for "local" so that engine-internal call paths and Engine.Check callers built before the signature change still resolve to the only configured policy.

Variables

View Source
var ErrTenantNotFound = errors.New("policy: tenant not found")

ErrTenantNotFound is returned by PolicyProvider.Get when the tenant has no associated policy. The proxy surfaces this as a synthetic DENY with Rule="deny:tenant:not_found" so the existing handleCheck flow does not need bespoke 404 handling. v0.6+ multi-tenant providers (database, etcd) will also map a missing tenant to ErrTenantNotFound.

Functions

This section is empty.

Types

type ActionRequest

type ActionRequest struct {
	SchemaVersion string            `json:"schema_version,omitempty"`
	Scope         string            `json:"scope"`
	Action        string            `json:"action,omitempty"`
	Command       string            `json:"command,omitempty"`
	Path          string            `json:"path,omitempty"`
	Domain        string            `json:"domain,omitempty"`
	URL           string            `json:"url,omitempty"`
	AgentID       string            `json:"agent_id,omitempty"`
	SessionID     string            `json:"session_id,omitempty"`
	EstCost       float64           `json:"est_cost,omitempty"`
	Meta          map[string]string `json:"meta,omitempty"`

	// ApprovalID, when set, instructs the central server's handleCheck
	// to look up the approval queue before running policy. If a matching
	// entry exists and is resolved, the server short-circuits with the
	// human's resolved decision rather than re-evaluating policy from
	// scratch. This is what makes the "approve once, model proceeds" UX
	// work: when a model retries a tool call after a human clicks
	// approve on the dashboard, the gateway propagates the original
	// approval_id (carried through MCP `_meta.dev.agentguard/approval_id`)
	// and the server honors the human's decision instead of producing a
	// fresh REQUIRE_APPROVAL entry.
	//
	// Empty / unset → server evaluates fresh (legacy behavior; back-
	// compat for SDK callers and any client that strips _meta).
	//
	// Wire-protocol note: the field is `omitempty` so existing v1
	// clients that never set it serialize byte-for-byte identically to
	// pre-A19b traffic. Engine.Check itself does NOT inspect this field —
	// the lookup short-circuit lives in pkg/proxy.handleCheck so the
	// approval queue (a server-side construct) is not dragged into the
	// pure policy package.
	ApprovalID string `json:"approval_id,omitempty"`
}

ActionRequest represents an agent's intended action.

SchemaVersion identifies the wire-format version. Clients may omit it (the proxy defaults missing values to "v1"); supplying any value other than "v1" is rejected with HTTP 400. The full schema is documented in pkg/proxy/schema/v1/schema.json.

type AgentCfg

type AgentCfg struct {
	Extends      string    `yaml:"extends"`
	Override     []RuleSet `yaml:"override,omitempty"`
	OverrideMode string    `yaml:"override_mode,omitempty"`
}

AgentCfg defines per-agent policy overrides.

OverrideMode controls how a per-scope override RuleSet combines with its base counterpart. The default ("merge") inherits Deny and RequireApproval rules from base while letting the override's Allow list narrow the scope. Setting "replace" reproduces the v0.4.x behavior where the override fully supplants the base RuleSet for that scope.

Closes R2 E5 / T11 (audit finding "per-agent override silently widens scope by dropping base deny rules").

type AuditCfg added in v0.5.0

type AuditCfg struct {
	// DefaultLimit is the row count returned when the caller omits ?limit=.
	// 0 => DefaultAuditDefaultLimit.
	DefaultLimit int `yaml:"default_limit,omitempty"`
	// MaxLimit is the hard ceiling; ?limit= values above it are clamped.
	// 0 => DefaultAuditMaxLimit. Must be >= DefaultLimit if both are set.
	MaxLimit int `yaml:"max_limit,omitempty"`
}

AuditCfg tunes the /v1/audit query endpoint.

type CheckResult

type CheckResult struct {
	SchemaVersion string   `json:"schema_version,omitempty"`
	Decision      Decision `json:"decision"`
	Reason        string   `json:"reason"`
	Rule          string   `json:"matched_rule,omitempty"`
	ApprovalID    string   `json:"approval_id,omitempty"`
	ApprovalURL   string   `json:"approval_url,omitempty"`
}

CheckResult is the response returned after evaluating an action against policy.

SchemaVersion identifies the wire-format version. v0.5+ servers always emit "v1"; clients may use it to negotiate forward-compatibility. The `omitempty` tag preserves byte-for-byte compatibility with v0.4.x test fixtures that decode the response and assert on the JSON shape — the field is always populated by the proxy at response time. The full schema is documented in pkg/proxy/schema/v1/schema.json.

type Condition

type Condition struct {
	RequirePrior string `yaml:"require_prior,omitempty"`
	TimeWindow   string `yaml:"time_window,omitempty"`
}

Condition is a contextual constraint on a rule.

type CostLimits

type CostLimits struct {
	MaxPerAction   string `yaml:"max_per_action,omitempty"`
	MaxPerSession  string `yaml:"max_per_session,omitempty"`
	AlertThreshold string `yaml:"alert_threshold,omitempty"`
}

CostLimits defines cost guardrails for a scope.

type Decision

type Decision string

Decision represents the outcome of a policy check.

const (
	Allow           Decision = "ALLOW"
	Deny            Decision = "DENY"
	RequireApproval Decision = "REQUIRE_APPROVAL"
)

type Engine

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

Engine evaluates actions against a policy. The policy itself is owned by a PolicyProvider (file-backed, static, or — in v0.6 — database-backed), not by the Engine. Engine caches the latest *Policy for the local tenant via a Watch subscription so that Check() does not pay a provider lookup on every request.

Caching strategy: the cached pointer is refreshed whenever the provider fires its watch callback (for FilePolicyProvider, on every successful reload; for StaticPolicyProvider, on every UpdatePolicy call). All reads of the cache use Engine.mu so a concurrent reload cannot tear a pointer swap. See .audit/v05_decisions.md ("PolicyProvider interface design") for the rationale on choosing watch-cache over per-Check Get.

func NewEngine

func NewEngine(provider PolicyProvider) (*Engine, error)

NewEngine creates a policy engine that reads policies through the given provider. The engine immediately calls provider.Get(LocalTenantID) to populate its cached policy and registers a Watch callback so subsequent changes are picked up automatically.

A nil provider is rejected. A provider that returns ErrTenantNotFound for the local tenant on initial Get is also rejected — this catches configuration mistakes (an empty file provider, a database with no tenant row) at boot rather than at the first Check call. Operators who genuinely need a policy-less engine (e.g. tests that populate the provider after construction) should use NewStaticPolicyProvider with a non-nil placeholder *Policy and update it later.

func NewEngineFromPolicy added in v0.5.0

func NewEngineFromPolicy(pol *Policy) *Engine

NewEngineFromPolicy is a convenience constructor that wraps pol in a StaticPolicyProvider and hands the provider to NewEngine. Library embedders who manage policy lifecycle out-of-band, plus the engine's own test suite, use this to avoid the FilePolicyProvider boilerplate.

The returned engine owns the StaticPolicyProvider — calling Engine.Close stops the watch subscription but does not close the provider; callers that need full teardown should construct the provider explicitly.

func (*Engine) Check

func (e *Engine) Check(req ActionRequest, tenantID string) CheckResult

Check evaluates an action request against the active policy for the given tenant. Order: deny rules -> require_approval rules -> allow rules -> default deny. Per-agent overrides are applied when AgentID matches a key in policy.Agents.

tenantID selects which policy to evaluate against. v0.5 only supports the local tenant ("" and "local" are accepted; everything else returns a synthetic DENY with Rule="deny:tenant:not_found"). The proxy passes "local" today; multi-tenant URL routing arrives with worker A7 in this phase.

Bad-tenant is surfaced as a CheckResult Deny rather than an error because the existing handleCheck flow already routes Deny through the audit + notify + response path; introducing a separate error channel would force every caller to add bespoke fallback logic for a case the CheckResult schema already covers. Future providers that need to distinguish "tenant unknown" from "tenant denied" can do so via Rule="deny:tenant:not_found" (the canonical sentinel). See .audit/v05_decisions.md ("PolicyProvider interface design") for the rationale.

For cost-scoped requests that are ALLOWed, the session accumulator is incremented atomically under the same write lock as the decision, so concurrent checks on the same session_id cannot collectively exceed the configured max_per_session limit (TOCTOU fix).

func (*Engine) Close added in v0.5.0

func (e *Engine) Close() error

Close releases the engine's Watch subscription. Safe to call multiple times (the underlying stop function uses sync.Once). Engines that outlive their server should call Close to avoid leaking the callback registration on the provider.

func (*Engine) LastPolicyLoadAt added in v0.5.0

func (e *Engine) LastPolicyLoadAt() time.Time

LastPolicyLoadAt returns the wall-clock time of the most recent successful policy load. Returns the zero time if no policy has been loaded yet (which should not occur at runtime: NewEngine refuses to construct without a successful initial Get and stamps the timestamp before returning).

func (*Engine) Policy

func (e *Engine) Policy() *Policy

Policy returns the currently active policy for the local tenant (thread-safe). Returns nil only if the provider's most recent Get surfaced ErrTenantNotFound for the local tenant; in v0.5 that should not happen at runtime because NewEngine refuses to construct without a policy.

func (*Engine) PolicyForTenant added in v0.5.0

func (e *Engine) PolicyForTenant(tenantID string) (*Policy, error)

PolicyForTenant returns the policy for tenantID (or the cached local policy when tenantID is "" or LocalTenantID). Forwards to the underlying provider for any other tenantID so multi-tenant providers (v0.6+) can resolve tenants the engine has not subscribed to. Returns ErrTenantNotFound when the tenant is unknown.

Used by /v1/health and the /v1/t/{tenant}/health endpoint to validate the requested tenant before composing the response. Health is the only caller in v0.5; the hot path (Engine.Check) goes through the existing dispatch logic and remains unchanged.

func (*Engine) RateLimitConfig

func (e *Engine) RateLimitConfig(scope, agentID string) *RateLimitCfg

RateLimitConfig returns the rate limit config for a given scope, considering per-agent overrides. Returns nil if no rate limit is configured.

func (*Engine) RecordCost added in v0.5.0

func (e *Engine) RecordCost(sessionID string, cost float64)

RecordCost adds the cost of a completed action to the session accumulator. Called by the proxy after a check returns ALLOW for a cost-scoped request.

Note: callers should prefer CheckAndReserve in Check() when possible, which updates the accumulator atomically with the Allow decision. RecordCost is retained for backfill or out-of-band accounting.

func (*Engine) RefundCost added in v0.5.0

func (e *Engine) RefundCost(sessionID string, cost float64)

RefundCost subtracts from the session accumulator. Used if a reserved cost is rolled back (e.g. a downstream action failed after the policy allowed it).

func (*Engine) SessionCost added in v0.5.0

func (e *Engine) SessionCost(sessionID string) float64

SessionCost returns the accumulated cost for a session (for testing).

func (*Engine) SessionCostCount added in v0.5.0

func (e *Engine) SessionCostCount() int

SessionCostCount returns the number of tracked session cost entries (useful for metrics and tests).

func (*Engine) SetHistoryQuerier added in v0.5.0

func (e *Engine) SetHistoryQuerier(h HistoryQuerier)

SetHistoryQuerier sets the history querier for conditional rule evaluation.

func (*Engine) SweepSessionCosts added in v0.5.0

func (e *Engine) SweepSessionCosts(maxAge time.Duration) int

SweepSessionCosts removes session-cost entries whose last update was more than maxAge ago. Returns the number of entries evicted. A non-positive maxAge is treated as "disabled" and returns 0 without scanning.

Called by a periodic goroutine in the proxy; exposed here so engine-only tests and out-of-band callers can trigger a sweep directly.

type FilePolicyProvider added in v0.5.0

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

FilePolicyProvider serves a single policy loaded from a YAML file. The only valid tenantID is "local" (or the empty string, treated as a synonym). Multi-file/multi-tenant arrives in v0.6.

Hot-reload is delegated to FileWatcher (pkg/policy/watcher.go), which prefers fsnotify and falls back to ModTime polling. On a successful reload the provider swaps its cached *Policy and fans the new pointer out to every Watch() callback registered with this provider.

func NewFilePolicyProvider added in v0.5.0

func NewFilePolicyProvider(path string) (*FilePolicyProvider, error)

NewFilePolicyProvider loads the policy at path, starts a FileWatcher, and returns a ready provider. A failed initial load surfaces as an error so operators see a missing or malformed file at boot rather than at the first Check call.

func (*FilePolicyProvider) Close added in v0.5.0

func (p *FilePolicyProvider) Close() error

Close stops the watcher and clears registered callbacks. Idempotent.

func (*FilePolicyProvider) Get added in v0.5.0

func (p *FilePolicyProvider) Get(tenantID string) (*Policy, error)

Get returns the current policy for tenantID. Only "" and "local" are valid in v0.5; every other tenantID returns ErrTenantNotFound.

func (*FilePolicyProvider) Validate added in v0.5.0

func (p *FilePolicyProvider) Validate(policyBytes []byte) error

Validate parses+validates raw YAML bytes without committing them. Used by `agentguard validate` (after this refactor lands; v0.5 main.go still calls LoadFromFile directly because it needs the parsed Policy back to print rule counts). v0.6+ admin endpoints call this before writing a new policy to disk or DB.

func (*FilePolicyProvider) Watch added in v0.5.0

func (p *FilePolicyProvider) Watch(tenantID string, cb func(*Policy)) (func(), error)

Watch registers a callback for policy changes on tenantID. Returns a stop function the caller invokes to unregister; calling stop more than once is safe.

type FileWatcher

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

FileWatcher watches a policy file for changes and triggers a callback. It prefers event-driven fsnotify; if fsnotify fails to initialise the watcher falls back to modtime polling at DefaultPollInterval.

Atomic-replace awareness: editors and `mv` replace a file by unlinking and renaming, which invalidates an inotify watch on the path itself. The watcher therefore watches the *parent directory* and filters events by basename, so `atomic replace` reliably triggers a reload.

func WatchFile

func WatchFile(path string, callback func(*Policy)) (*FileWatcher, error)

WatchFile starts watching a policy file for changes. An initial Stat failure returns an error up-front so callers can surface misconfiguration (missing file) at boot; transient errors inside the watch goroutine are logged and do not tear the watcher down.

func (*FileWatcher) Close

func (w *FileWatcher) Close()

Close stops the file watcher. Safe to call multiple times.

type HistoryEntry added in v0.5.0

type HistoryEntry struct {
	Action   string
	Command  string
	Decision Decision
	EstCost  float64
}

HistoryEntry is a minimal record of a past action, used for conditional rule evaluation.

type HistoryQuerier added in v0.5.0

type HistoryQuerier interface {
	RecentActions(agentID string, scope string, since time.Time) ([]HistoryEntry, error)
}

HistoryQuerier provides access to recent action history for conditional rules. Implemented by the audit logger (via an adapter to avoid circular imports).

type NotificationCfg

type NotificationCfg struct {
	ApprovalRequired []NotifyTarget `yaml:"approval_required,omitempty"`
	OnDeny           []NotifyTarget `yaml:"on_deny,omitempty"`
	Redaction        RedactionCfg   `yaml:"redaction,omitempty"`
	// DispatchTimeout is the default per-notification HTTP timeout applied
	// to webhook and Slack targets that do not set their own `timeout`.
	// Parsed as a Go duration string. Empty => DefaultNotifyDispatchTimeout.
	DispatchTimeout string `yaml:"dispatch_timeout,omitempty"`
}

NotificationCfg defines where to send alerts.

type NotifyTarget

type NotifyTarget struct {
	Type  string `yaml:"type"` // "webhook", "slack", "console", "log"
	URL   string `yaml:"url,omitempty"`
	Level string `yaml:"level,omitempty"`
	// Timeout optionally overrides NotificationCfg.DispatchTimeout for this
	// specific target. Only webhook and slack notifiers honor it; console
	// and log targets are synchronous and local. Empty string => inherit
	// the notifications.dispatch_timeout value (which itself defaults to
	// DefaultNotifyDispatchTimeout when unset).
	Timeout string `yaml:"timeout,omitempty"`
}

NotifyTarget is a notification destination.

func (NotifyTarget) ResolvedTimeout added in v0.5.0

func (t NotifyTarget) ResolvedTimeout(fallback time.Duration) time.Duration

ResolvedTimeout returns the effective per-target HTTP timeout, falling back to the supplied dispatch-level default when the target did not set its own. Invalid durations (which LoadFromFile already rejects, but callers that construct NotifyTarget directly in tests can bypass) fall through to the default.

type Policy

type Policy struct {
	Version       string              `yaml:"version"`
	Name          string              `yaml:"name"`
	Description   string              `yaml:"description"`
	Rules         []RuleSet           `yaml:"rules"`
	Agents        map[string]AgentCfg `yaml:"agents,omitempty"`
	Notifications NotificationCfg     `yaml:"notifications,omitempty"`
	// Proxy carries server-side tunables (session TTL, request body limit,
	// audit query bounds). All subfields are optional; unset values fall
	// back to the Default* constants below, which match v0.4.0 behavior.
	Proxy ProxyCfg `yaml:"proxy,omitempty"`

	// ToolScopeMap maps MCP tool names to existing policy scopes for the
	// MCP Gateway's dual-check pattern. Patterns are glob-matched (same
	// matcher as rule patterns); the gateway's --policy-mode strict
	// (default) fires a second Engine.Check against the mapped scope per
	// tool call so existing filesystem/network/shell rules apply to MCP
	// traffic without duplication. See docs/MCP_GATEWAY.md § 4.4 and
	// docs/POLICY_REFERENCE.md#mcp_tool-scope.
	//
	// Order matters: first match wins. Operators put more-specific
	// patterns before broader ones.
	//
	// Example:
	//
	//   tool_scope_map:
	//     - pattern: "fs:read_file"
	//       scope: filesystem
	//     - pattern: "fs:*"
	//       scope: filesystem
	//     - pattern: "github:*"
	//       scope: network
	//     - pattern: "*:execute_*"
	//       scope: shell
	ToolScopeMap []ToolScopeMapping `yaml:"tool_scope_map,omitempty" json:"tool_scope_map,omitempty"`
}

Policy is the top-level policy document.

func LoadFromFile

func LoadFromFile(path string) (*Policy, error)

LoadFromFile reads and parses a policy YAML file. The file is read, parsed, and validated by the same code path that PolicyProvider.Validate invokes for raw bytes — only the os.ReadFile step is unique to this function. Validation errors include the YAML path so operators can find the failing field without grepping.

func (*Policy) AuditDefaultLimit added in v0.5.0

func (p *Policy) AuditDefaultLimit() int

AuditDefaultLimit returns the configured default audit page size, or DefaultAuditDefaultLimit when unset.

func (*Policy) AuditMaxLimit added in v0.5.0

func (p *Policy) AuditMaxLimit() int

AuditMaxLimit returns the configured hard ceiling on audit page size, or DefaultAuditMaxLimit when unset.

func (*Policy) MapToolScope added in v0.5.0

func (p *Policy) MapToolScope(toolName string) (string, bool)

MapToolScope returns the existing-scope rule that applies to the given namespaced MCP tool name (`<ns>:<tool>`), per the policy's tool_scope_map. Returns ("", false) if no entry matches. First match wins; operators control resolution order via the YAML list order.

Used by the MCP Gateway in --policy-mode strict to fire a second Engine.Check against the mapped scope per tool call. Single-call hot path; safe to invoke under any lock state because it does not touch engine state.

func (*Policy) MaxRequestBodyBytes added in v0.5.0

func (p *Policy) MaxRequestBodyBytes() int64

MaxRequestBodyBytes returns the configured request body cap, or DefaultMaxRequestBodyBytes when unset.

func (*Policy) NotifyDispatchTimeout added in v0.5.0

func (p *Policy) NotifyDispatchTimeout() time.Duration

NotifyDispatchTimeout returns the global dispatch HTTP timeout or DefaultNotifyDispatchTimeout when unset.

func (*Policy) RuleCount

func (p *Policy) RuleCount() int

RuleCount returns the total number of individual rules.

func (*Policy) ScopeCount

func (p *Policy) ScopeCount() int

ScopeCount returns the number of unique scopes.

func (*Policy) SessionTTL added in v0.5.0

func (p *Policy) SessionTTL() time.Duration

SessionTTL returns the configured session TTL or DefaultSessionTTL when unset. Guarantees a positive duration.

type PolicyProvider added in v0.5.0

type PolicyProvider interface {
	// Get returns the current policy for tenantID. The returned *Policy is
	// a snapshot — callers must not mutate it (the engine treats it as
	// read-only and the file provider holds the same pointer). Returns
	// ErrTenantNotFound when no policy exists for the tenant.
	Get(tenantID string) (*Policy, error)

	// Watch registers a callback fired whenever the policy for tenantID
	// changes. Multiple Watch calls coexist. The returned stop function
	// unregisters the callback; calling it twice is safe. The callback is
	// invoked from a watcher goroutine without holding the provider's
	// internal lock, so callers may take their own locks freely.
	Watch(tenantID string, callback func(*Policy)) (stop func(), err error)

	// Validate parses+validates a policy without committing it. Used by
	// `agentguard validate` and (in v0.6+) a /v1/validate endpoint. The
	// error message is intended for display to operators; it includes the
	// YAML path of any failing field.
	Validate(policyBytes []byte) error

	// Close releases provider resources (file watchers, DB connections).
	// Safe to call multiple times via sync.Once.
	Close() error
}

PolicyProvider abstracts policy retrieval. Engine reads policies through this interface instead of loading from disk directly. Future v0.6 implementations (database, etcd, S3) implement the same interface without engine changes. Closes audit finding R2 P1 (partial; full closure ships with the database-backed provider in v0.6).

type ProxyCfg added in v0.5.0

type ProxyCfg struct {
	Session SessionCfg `yaml:"session,omitempty"`
	Request RequestCfg `yaml:"request,omitempty"`
	Audit   AuditCfg   `yaml:"audit,omitempty"`
}

ProxyCfg groups server-side tunables under the `proxy:` YAML key. Each subsection is independently optional.

type RateLimitCfg

type RateLimitCfg struct {
	MaxRequests int    `yaml:"max_requests"`
	Window      string `yaml:"window"`
}

RateLimitCfg defines rate limiting parameters.

type RedactionCfg added in v0.5.0

type RedactionCfg struct {
	ExtraPatterns []string `yaml:"extra_patterns,omitempty"`
}

RedactionCfg tunes the secret-redactor used by the notify dispatcher.

ExtraPatterns are appended to the built-in DefaultRedactor list. Operators use this to mask org-specific secret formats (e.g. internal API key prefixes) without patching the binary. Patterns are Go regexp syntax (RE2); invalid patterns are rejected at policy load.

type RequestCfg added in v0.5.0

type RequestCfg struct {
	// MaxBodyBytes caps POST /v1/check body size. 0 => DefaultMaxRequestBodyBytes.
	// Requests larger than this receive 413 and increment
	// agentguard_request_rejected_total{reason="body_too_large"}.
	MaxBodyBytes int64 `yaml:"max_body_bytes,omitempty"`
}

RequestCfg tunes incoming-request acceptance limits.

type Rule

type Rule struct {
	Action     string      `yaml:"action,omitempty"`
	Pattern    string      `yaml:"pattern,omitempty"`
	Paths      []string    `yaml:"paths,omitempty"`
	Domain     string      `yaml:"domain,omitempty"`
	Message    string      `yaml:"message,omitempty"`
	Conditions []Condition `yaml:"conditions,omitempty"`
}

Rule is an individual policy rule.

type RuleSet

type RuleSet struct {
	Scope           string        `yaml:"scope"`
	Allow           []Rule        `yaml:"allow,omitempty"`
	Deny            []Rule        `yaml:"deny,omitempty"`
	RequireApproval []Rule        `yaml:"require_approval,omitempty"`
	RateLimit       *RateLimitCfg `yaml:"rate_limit,omitempty"`
	Limits          *CostLimits   `yaml:"limits,omitempty"`
}

RuleSet groups rules by scope.

type SessionCfg added in v0.5.0

type SessionCfg struct {
	// TTL is how long a /auth/login session stays valid. Parsed as a Go
	// duration string ("1h", "30m"). Empty string => DefaultSessionTTL.
	TTL string `yaml:"ttl,omitempty"`
}

SessionCfg tunes the dashboard session store.

type StaticPolicyProvider added in v0.5.0

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

StaticPolicyProvider serves a fixed *Policy without watching any file or database. Primary use case: tests that construct a Policy struct directly and want to drive Engine.Check without the disk dependency. Validate parses raw YAML and runs the standard checks but does NOT rebind the static policy — Validate is a pure function.

StaticPolicyProvider also serves as the v0.6 escape hatch for users who embed AgentGuard as a library and prefer to manage policy lifecycle themselves; UpdatePolicy lets them swap the served policy at runtime.

func NewStaticPolicyProvider added in v0.5.0

func NewStaticPolicyProvider(pol *Policy) *StaticPolicyProvider

NewStaticPolicyProvider returns a provider that serves pol on every Get("local") / Get("") call. pol may be nil; callers can populate it later via UpdatePolicy.

func (*StaticPolicyProvider) Close added in v0.5.0

func (p *StaticPolicyProvider) Close() error

Close clears callbacks. Idempotent.

func (*StaticPolicyProvider) Get added in v0.5.0

func (p *StaticPolicyProvider) Get(tenantID string) (*Policy, error)

Get returns the static policy. Tenants other than "" and "local" produce ErrTenantNotFound. A nil policy also produces ErrTenantNotFound — callers must populate one via UpdatePolicy before first use.

func (*StaticPolicyProvider) UpdatePolicy added in v0.5.0

func (p *StaticPolicyProvider) UpdatePolicy(pol *Policy)

UpdatePolicy swaps the served policy and notifies all watchers. This mirrors the runtime-mutation path that FilePolicyProvider exposes implicitly via file edits, so library embedders can drive hot-reloads from any source.

func (*StaticPolicyProvider) Validate added in v0.5.0

func (p *StaticPolicyProvider) Validate(policyBytes []byte) error

Validate parses+validates raw YAML bytes. Same semantics as FilePolicyProvider.Validate.

func (*StaticPolicyProvider) Watch added in v0.5.0

func (p *StaticPolicyProvider) Watch(tenantID string, cb func(*Policy)) (func(), error)

Watch registers a callback. Callbacks fire on every UpdatePolicy call.

type ToolScopeMapping added in v0.5.0

type ToolScopeMapping struct {
	Pattern string `yaml:"pattern" json:"pattern"`
	Scope   string `yaml:"scope" json:"scope"`
}

ToolScopeMapping is one entry in Policy.ToolScopeMap. The list form (rather than an inline map) is chosen so YAML iteration order is deterministic — the gateway's first-match-wins resolution depends on it. Operators write a few extra lines per entry; the gateway guarantees the same scope for the same tool name on every host regardless of YAML library quirks.

Jump to

Keyboard shortcuts

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