Policy Engine
Agent Action Firewall uses Open Policy Agent (OPA) as its policy engine, giving you powerful, flexible control over what your agents can do.
How Policy Evaluation Works
- Agent submits an action request
- OPA evaluates all applicable policies against the action
- The most restrictive matching decision is returned
- The decision determines if the action proceeds
Policy Structure
Policies are written in Rego and must return a decision:
package aaf.policy
# Default to allow if no rules match
default decision = "allow"
# Deny requests to private networks
decision = "deny" {
input.action.tool == "http_proxy"
is_private_network(input.action.params.url)
}
# Require approval for financial operations
decision = "require_approval" {
input.action.tool == "http_proxy"
contains(input.action.params.url, "stripe.com")
input.action.params.body.amount > 10000
}
Input Schema
Your policies receive an input object with:
{
"action": {
"id": "action-123",
"tool": "http",
"operation": "POST",
"params": {
"url": "https://api.stripe.com/v1/charges",
"headers": { ... },
"body": { ... }
},
"context": {
"user_id": "user-456",
"session_id": "session-789",
"purpose": "Process payment"
}
},
"agent": {
"id": "agent-001",
"name": "Payment Agent",
"risk_score": 0.2
},
"org": {
"id": "org-uuid",
"plan": "pro"
},
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"ip_address": "203.0.113.42"
}
}
Policy Precedence
Decisions follow this precedence (most restrictive wins):
- deny - Action is blocked
- require_approval - Action needs human approval
- allow - Action can proceed
If multiple policies match, the most restrictive decision applies.
Built-in Functions
AAF provides helper functions for common patterns:
Network Security
# Check if URL targets a private network
is_private_ip(url) {
parts := split(url, "/")
host := parts[2]
startswith(host, "10.")
}
is_private_ip(url) {
parts := split(url, "/")
host := parts[2]
startswith(host, "192.168.")
}
is_private_ip(url) {
parts := split(url, "/")
host := parts[2]
startswith(host, "172.16.")
}
Data Classification
# Check if payload contains sensitive data
has_pii(data) {
regex.match(`\d{3}-\d{2}-\d{4}`, data) # SSN pattern
}
has_credit_card(data) {
regex.match(`\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}`, data)
}
Time-based Rules
# Deny actions outside business hours
decision = "deny" {
current_hour := time.clock(time.now_ns())[0]
current_hour < 9
}
decision = "deny" {
current_hour := time.clock(time.now_ns())[0]
current_hour > 17
}
Policy Organization
Policy Packs
Group related policies into packs:
- Security Pack: SSRF protection, network isolation
- Compliance Pack: PII detection, audit requirements
- Financial Pack: Transaction limits, approval thresholds
Policy Versioning
Policies are versioned and changes are tracked:
# View policy history
GET /api/v1/policies/{id}/versions
# Rollback to previous version
POST /api/v1/policies/{id}/rollback
{
"version": 3
}
Testing Policies
Dry Run Mode
Test policies without executing actions:
const result = await firewall.dryRun({
tool: 'http_proxy',
operation: 'POST',
params: {
url: 'https://api.stripe.com/v1/charges',
body: { amount: 50000 }
}
});
console.log(result.decision); // "require_approval"
console.log(result.matchedPolicies); // ["financial-limits"]
Policy Playground
The dashboard includes a playground for testing policies:
- Navigate to Policies → Playground
- Enter a sample action
- See which policies match and why
Performance
OPA is highly optimized for policy evaluation:
- Sub-millisecond evaluation for typical policies
- Caching of compiled policies
- Parallel evaluation of independent rules
For high-throughput scenarios, consider:
- Running OPA as a sidecar (Kubernetes)
- Enabling decision caching (Redis)
- Simplifying complex regex patterns
Next Steps
- Writing Rego — Learn the Rego policy language
- Visual Workflow Editor — Build policies without code
- Policy Templates — Start from a pre-built template