Skip to content

Policy Engine

The SafeAI policy engine evaluates every request against a set of YAML-defined rules. Policies use priority-based, first-match evaluation: the highest-priority matching rule determines the action. This gives you fine-grained, declarative control over what data flows where, without writing application code.

Auto-generate policies

Instead of writing policies by hand, use the intelligence layer to generate them automatically:

safeai intelligence auto-config --path . --apply
The AI analyzes your project and generates tailored policies. Use this guide when you need to customize or fine-tune the generated rules.

Quick Example

safeai.yaml
policies:
  - name: block-ssn-output
    boundary: output
    priority: 100
    condition:
      data_tags: [personal.pii.ssn]
    action: block
    reason: "SSN must never appear in agent output"
from safeai import SafeAI

ai = SafeAI.from_config("safeai.yaml")

result = ai.guard_output("SSN: 123-45-6789")
print(result.action)  # "block"
print(result.reason)  # "SSN must never appear in agent output"

Full Example

safeai.yaml
policies:
  # High priority — always block secrets
  - name: block-secrets
    boundary: input
    priority: 200
    condition:
      data_tags: [secret]
    action: block
    reason: "Secrets must not enter the pipeline"

  # Medium priority — redact PII on output
  - name: redact-pii-output
    boundary: output
    priority: 100
    condition:
      data_tags: [personal.pii]
    action: redact
    reason: "PII must be redacted before reaching users"

  # Low priority — require approval for financial data
  - name: approve-financial
    boundary: output
    priority: 50
    condition:
      data_tags: [personal.financial]
    action: require_approval
    reason: "Financial data requires human review"

  # Default allow
  - name: allow-all
    boundary: "*"
    priority: 1
    condition:
      data_tags: ["*"]
    action: allow
from safeai import SafeAI

ai = SafeAI.from_config("safeai.yaml")

# Triggers "block-secrets" (priority 200)
r1 = ai.scan_input("key=sk-ABCDEF1234567890")
assert r1.action == "block"

# Triggers "redact-pii-output" (priority 100)
r2 = ai.guard_output("Email: alice@example.com")
assert r2.action == "redact"

# Triggers "approve-financial" (priority 50)
r3 = ai.guard_output("Account balance: $52,340.00")
assert r3.action == "require_approval"

Policy Format

Each policy is a dictionary with the following fields:

Field Type Description
name str Unique identifier for the rule
boundary str input, output, tool_request, tool_response, *
priority int Higher number = evaluated first
condition dict Matching criteria (see below)
action str allow, block, redact, require_approval
reason str Human-readable explanation logged with every decision

Condition Fields

condition:
  data_tags: [personal.pii.email, secret.api_key]
  agent_id: "support-bot"
  tool_name: "send_email"

All specified condition fields must match for the rule to fire.

Tag Hierarchies

Tags are dot-separated and match hierarchically. A policy targeting a parent tag automatically matches all children:

personal           → matches personal.pii, personal.pii.ssn, personal.financial
personal.pii       → matches personal.pii.ssn, personal.pii.email
personal.financial → matches personal.financial.account_number
secret             → matches secret.api_key, secret.aws_key

Wildcard tags

Use "*" in data_tags to match any tag. This is useful for default allow/deny rules at the bottom of your policy list.

Actions

Action Behavior
allow Let the data through unchanged
block Reject the request; return an error to the caller
redact Mask the matched values and pass the rest through
require_approval Pause and queue for human review (see Approval Workflows)

Hot Reload

Reload policies without restarting your application:

# Reload if the file has changed since last load
ai.reload_policies()

# Force reload regardless of file modification time
ai.force_reload_policies()

Watch mode

In development, call ai.reload_policies() at the start of each request to pick up config changes instantly.

Configuration

safeai.yaml
policy_engine:
  evaluation: first_match   # first_match | all_match
  default_action: block     # action when no rule matches
  audit: true               # log every evaluation to audit trail

policies:
  - name: my-rule
    boundary: input
    priority: 100
    condition:
      data_tags: [secret]
    action: block
    reason: "Block all secrets on input"

See Also