Documentation
¶
Overview ¶
Package engine provides the layered safety execution engine.
The engine runs content through six layers modeled on human safety cognition, in this order, and then the tenant's policies:
Layer 1: INSTINCTS injection, jailbreak, exfiltration and manipulation patterns Layer 2: AWARENESS PII, secret, topic and custom pattern detectors Layer 3: BOUNDARIES topic, data, output and tool-call (action) limits Layer 4: VALUES safety and toxicity lexicons, custom terms and patterns Layer 5: JUDGMENT risk over the findings so far, plus the optional model judge Layer 6: REFLEXES trigger and action rules then: POLICIES the tenant's org-level and assigned app-level rules
Every scan runs inside a tenant and evaluates one safety profile of that tenant, compiled from the store and cached (see Config.ProfileCacheTTL), then the tenant's policies. With Config.EnableShortCircuit a block ends the scan after the layer that found it.
Index ¶
- Constants
- Variables
- func ActionGlob(entry string) (glob string, risk bool)
- type Capabilities
- type Engine
- func (e *Engine) Capabilities() Capabilities
- func (e *Engine) Config() shield.Config
- func (e *Engine) Health(ctx context.Context) error
- func (e *Engine) InvalidateAllProfiles()
- func (e *Engine) InvalidateProfiles(tenantID string)
- func (e *Engine) ProfileGeneration(tenantID string) uint64
- func (e *Engine) ScanInput(ctx context.Context, input *scan.Input) (*scan.Result, error)
- func (e *Engine) ScanOutput(ctx context.Context, input *scan.Input) (*scan.Result, error)
- func (e *Engine) ScanToolCall(ctx context.Context, in *scan.ToolCallInput) (*scan.Result, error)
- func (e *Engine) Stop(ctx context.Context) error
- func (e *Engine) Store() store.Store
- type Option
Constants ¶
const ( ScansCompletedMetric = "shield_scans_completed_total" ScanDurationMetric = "shield_scan_duration_seconds" )
The metrics the engine records when WithMetrics is set. A Forge app scrapes them with a forge_ prefix: forge_shield_scans_completed_total and forge_shield_scan_duration_seconds. Both carry direction (input, output, tool_call) and decision (allow, block, flag, redact, require_approval).
const MaxArgumentDepth = 32
MaxArgumentDepth bounds how deeply the JSON arguments of a tool call may nest objects and arrays. Deeper arguments are shield.ErrInvalidInput. Tool arguments are flat in practice; the bound keeps a hostile body from costing a deep walk.
const MaxProfilePatterns = 256
MaxProfilePatterns bounds the regexes and globs operators write into one compiled profile: instinct, awareness and values patterns, reflex patterns, targets and globs. Each regex without a literal prefix costs a pass over the text, so the bound keeps one tenant's scans from costing seconds.
const MaxRateThreshold = 100000
MaxRateThreshold is the largest on_rate threshold: more scans than this within a window are never counted. Thresholds are whole numbers.
Variables ¶
var ScanDurationBuckets = []float64{0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5}
ScanDurationBuckets are the histogram bounds for ScanDurationMetric, in seconds. They span the layer budgets, from a 5ms boundary check to a 500ms judgment, with headroom for storing the result.
Functions ¶
func ActionGlob ¶
ActionGlob splits an action-scope boundary entry into its path.Match glob and what it matches: the risk class for an entry with a risk: prefix (in any case), the tool name otherwise. The entry and the glob after the prefix are trimmed.
Types ¶
type Capabilities ¶
type Capabilities struct {
Persistence bool `json:"persistence"`
Evaluation bool `json:"evaluation"`
ToolCalls bool `json:"tool_calls"`
ModelJudge bool `json:"model_judge"`
ReportGeneration bool `json:"report_generation"`
ConfigWrite bool `json:"config_write"`
// Layers lists the layers that cannot run. It is empty, never nil.
Layers []string `json:"unavailable_layers"`
// Supported names what the engine runs, sorted, under the keys
// instinct_strategies, awareness_detectors, judgment_assessors,
// reflex_triggers and values_lexicons (the principles with a built-in
// detector). unavailable_judgment_assessors names the assessors a
// judgment may list that the engine skips: model, unless
// Config.ModelJudge is on and a judge is registered.
Supported map[string][]string `json:"supported"`
}
Capabilities describes implemented services, independently of configured rows.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the core safety execution engine.
func (*Engine) Capabilities ¶
func (e *Engine) Capabilities() Capabilities
Capabilities reports what this engine can do.
func (*Engine) InvalidateAllProfiles ¶
func (e *Engine) InvalidateAllProfiles()
InvalidateAllProfiles drops every tenant's cached profiles, so each tenant's next scan loads its profile and policies again. Call it after changing an app-level policy outside the admin service, which calls it for you: every tenant the policy is assigned to compiles it in.
func (*Engine) InvalidateProfiles ¶
InvalidateProfiles drops every cached profile of tenantID, so the next scan for that tenant loads its profile and primitives again. Call it after changing a tenant's safety rows outside the admin service, which calls it for you.
func (*Engine) ProfileGeneration ¶
ProfileGeneration returns a number that changes whenever InvalidateProfiles(tenantID) or InvalidateAllProfiles runs. A host that caches scan verdicts keys them on it, so a profile change made through the admin service (or followed by an invalidation) is never answered from a stale verdict.
func (*Engine) ScanOutput ¶
ScanOutput runs a safety scan on output content (agent→user).
func (*Engine) ScanToolCall ¶
ScanToolCall evaluates a proposed tool call. The decision is allow, block or require_approval. It is advisory and can only restrict: the host enforces its own risk table first and combines with scan.Stricter, so nothing here can turn a host refusal into an allow.
Every object key, whatever its value, and every string and number value of the arguments is a unit, scanned on its own and unchanged, the way ScanInput would see that text, with Direction tool_call. Keys come sorted, each before what it holds. A finding from a unit lists the JSON path of each unit in its paths details (a.b.0, key:a.b for a key, $ for a top-level value); a key that is not a short identifier, that a layer found something in, or that a built-in secret or personal data detector matches whatever the profile runs, shows as #N, its place among its sorted siblings. The judge's lines name units the same way. Topic and data boundaries match the units joined by newlines. Action boundaries match the tool name, or with a risk: prefix the risk class. on_context reflexes see tool, risk_class (unknown when empty) and every Metadata key. The result never carries text, and the stored scan keeps the tool, the risk class and the metadata but no argument.
Besides the errors of ScanInput, a nil input, a blank Tool or one holding a control or invisible rune, and arguments that are not one JSON value or nest deeper than MaxArgumentDepth are shield.ErrInvalidInput. Arguments, tool, risk class and metadata over Config.MaxScanBytes are shield.ErrInputTooLarge.
type Option ¶
type Option func(*Engine)
Option configures the Engine.
func WithConfig ¶
WithConfig sets the engine configuration.
func WithJudge ¶
WithJudge registers the model judge. It is called only when Config.ModelJudge is true, and it can only raise a judgment's risk.
func WithLogger ¶
WithLogger sets a custom logger. The plugin registry reports hook errors on it too.
func WithMetrics ¶
WithMetrics records scan and plugin metrics on m: ScansCompletedMetric, ScanDurationMetric and plugin.HooksFailedMetric. In a Forge app pass app.Metrics(); the Shield Forge extension does this for you. A nil m, the default, records nothing.