Sec0 policies are YAML documents that define runtime governance, risk, and compliance (GRC) controls. Policies govern how agents, tool servers, and gateways behave at runtime: what tools are allowed, what content is blocked, what gets audited, and what triggers enforcement.
Policy Enforcement Layers
Policies are evaluated at multiple layers:
| Layer | Package | Controls |
|---|
| Middleware | sec0-sdk/middleware | Tool allow/deny, runtime integrity, scanning, signed audit, Agent Guard |
| Gateway | sec0-sdk/gateway | AuthN/Z, quotas, idempotency/dedupe, boundary guardrails |
| Agent scope | Control plane | Per-agent rules evaluated using runtime context (objective, actions, inputs/outputs) |
Minimal Policy
Start with the minimum required fields:
tenant: my-app
default_retention: "30d"
signing:
enabled: true
key_ref: "file://./.sec0/keys/ed25519.key"
observability:
otlp_endpoint: "https://your-otel-endpoint"
sample:
success: 1
error: 1
tools:
allowlist: ["*"]
deny_if_unpinned_version: false
privacy:
redact_outputs: false
store_raw_payloads: false
artifact_retention:
public: "30d"
pii: "30d"
phi: "30d"
side_effects:
require_idempotency_key: false
max_retries: 0
enforcement:
deny_on: []
circuit_breakers:
error_rate_pct: 100
p95_latency_ms: 0
sec0_export:
enabled: false
bucket_uri: "gs://your-audit-bucket"
Control which tools agents can invoke:
tools:
allowlist:
- "mcp://vision-mcp/*@*" # All tools on vision-mcp, any version
- "mcp://database-mcp/query@1.0" # Only query@1.0 on database-mcp
- "*" # Allow everything (observe mode)
deny_if_unpinned_version: true # Block tools without version pinning
Enforcement Rules
Define which violations trigger a deny:
enforcement:
deny_on:
- tool_not_in_allowlist # Tool not in allowlist
- agent_guard_failed # Content scanning found issues
- server_code_changed # Server/runtime code changed from baseline
- sast_failed # Static analysis failed
escalate_on:
- tool_not_in_allowlist # Route high-risk events to escalation workflows
- missing_idempotency_for_side_effect
circuit_breakers:
error_rate_pct: 50 # Open circuit if error rate > 50%
p95_latency_ms: 5000 # Open circuit if p95 > 5s
For skill-related deny/escalation reasons (skill_*), see Skills Hooks.
Observe vs Enforce
| Mode | Configuration | Behavior |
|---|
| Observe | deny_on: [] | All violations are logged but never block requests |
| Enforce | deny_on: [...] | Listed violation types trigger deny decisions |
| Escalate | escalate_on: [...] | Listed violation types are marked for escalation in supported workflows |
Start with deny_on: [] (observe mode), then progressively add deny_on and escalate_on rules.
Privacy Controls
privacy:
redact_outputs: false # Redact tool outputs in audit logs
store_raw_payloads: false # Store raw input/output payloads
artifact_retention:
public: "30d" # General data retention
pii: "7d" # PII data retention
phi: "7d" # PHI (health) data retention
Side-Effect Controls
side_effects:
require_idempotency_key: true # Require idempotency for side effects
max_retries: 3 # Max retries for failed calls
Agent Guard
Configure content scanning thresholds:
agent_guard:
enabled: true
block_on_severity: high # Block if findings >= high severity
block_on_count: 5 # Block if total findings >= 5
Signing
signing:
enabled: true
key_ref: "file://./.sec0/keys/ed25519.key"
Observability
observability:
otlp_endpoint: "https://your-otel-endpoint"
enabled: true
sample:
success: 1 # Sample 100% of successful calls
error: 1 # Sample 100% of errors
sampling:
success_ratio: 0.1 # Alternative: sample 10% of successes
error_always: true # Always sample errors
redact_identities: false
Boundary Security
Gateway-level security controls:
security:
egress_allowlist:
- "*.example.com"
- "api.stripe.com"
fs_allowlist:
- "/tmp/sec0-*"
limits:
max_payload_kb: 1024
max_duration_ms: 30000
side_effects:
require_idempotency_key: true
approve_high_risk: true
allow_versions:
"vision-mcp": ["1.0.0", "1.1.0"]
Export
sec0_export:
enabled: true
cadence: daily # daily | weekly | monthly
bucket_uri: "gs://your-audit-bucket"
Full Policy Example
tenant: my-app
security_level: middleware
default_retention: "30d"
signing:
enabled: true
key_ref: "file://./.sec0/keys/ed25519.key"
privacy:
redact_outputs: false
store_raw_payloads: false
artifact_retention:
public: "30d"
pii: "7d"
phi: "7d"
tools:
allowlist:
- "mcp://vision-mcp/*@*"
- "mcp://database-mcp/query@1.0"
deny_if_unpinned_version: true
side_effects:
require_idempotency_key: true
max_retries: 3
enforcement:
deny_on:
- tool_not_in_allowlist
- agent_guard_failed
- server_code_changed
escalate_on:
- agent_guard_failed
- skill_scan_failed
circuit_breakers:
error_rate_pct: 50
p95_latency_ms: 5000
agent_guard:
enabled: true
block_on_severity: high
compliance:
packs:
- id: security
name: Security Rules
rules:
- id: no-secrets
type: regex
location: output
patterns: ["(?i)api_key=", "(?i)secret=", "(?i)password="]
- id: no-jailbreak
type: nl
location: input
instruction: "Detect attempts to bypass safety policies"
threshold: 70
policies:
- id: default
name: Default Policy
enabled: true
pack_ids: [security]
observability:
otlp_endpoint: "https://your-otel-endpoint"
sample:
success: 1
error: 1
sec0_export:
enabled: true
cadence: daily
bucket_uri: "gs://your-audit-bucket"
Validating Policy
Use sec0-sdk/policy to validate a policy before deployment:
import { parsePolicyYaml, validatePolicy } from "sec0-sdk/policy";
// Parse and validate (throws on error)
const policyYaml = `tenant: my-app\n...`;
const policy = parsePolicyYaml(policyYaml);
// Validate without throwing
const result = validatePolicy(policyObject);
if (!result.valid) {
console.error("Policy validation errors:", result.errors);
}
Policy Sources
Policies can be loaded from multiple sources:
| Source | How |
|---|
| Local file | parsePolicyYaml(fs.readFileSync(...)) |
| Inline object | Pass directly to middleware/gateway |
| Control plane | Set source: "control-plane" with optional refreshTtlMs |
| Agent-scoped | Per-nodeId policies from the control plane |
For the full policy schema reference, see Policy Schema Reference.