Policy Packs
AAF ships built-in OPA/Rego policy packs for common tools, and you can add custom packs for your own integrations.
How Policy Evaluation Works
Policies are written in Rego and evaluated by OPA (Open Policy Agent). Each action request is evaluated against the loaded policies, which return:
- allow: action can proceed immediately
- require_approval: action needs human approval before execution
- deny: implicit when neither
allownorrequire_approvalis satisfied — packs declaredefault allow := false
Each pack also returns a risk_level (low, medium, high, critical) and a human-readable reason for the audit trail.
Policy Structure
Built-in packs follow this structure (Rego v0 syntax with `if` keywords):
package aaf.policy.<tool_name>
import future.keywords.if
import future.keywords.in
# Default decision is deny
default allow := false
default require_approval := false
default risk_level := "critical"
default reason := "Action denied by policy"
# Allow rule
allow if {
# conditions
}
# Require approval rule
require_approval if {
# conditions
}
risk_level := "low" if { allow }
reason := "..." if { allow }Input Format
Each policy receives an input object describing the action:
{
"tool": "http_proxy",
"operation": "http_get",
"params": {
"url": "https://api.example.com/data",
"headers": { "Authorization": "Bearer ..." }
},
"context": {
"org_id": "org-123",
"agent_id": "agent-456",
"org_allowlist": ["api.example.com", "internal.corp.com"]
}
}Built-in Policy Packs
HTTP Proxy Policy
Located at packages/policy/src/policies/http-proxy.rego (package aaf.policy.http_proxy). Enforces:
- GET/HEAD requests to allowlisted hosts: allow with low risk
- Requests with sensitive headers (e.g. Authorization): require approval, medium risk
- POST/PUT/DELETE/PATCH to allowlisted hosts: require approval, high risk
- Any request to a non-allowlisted host: deny (critical risk)
Jira Policy
Located at packages/policy/src/policies/jira.rego (package aaf.policy.jira). Enforces:
jira_get_issue: allow with low riskjira_add_comment: require approval, medium riskjira_transition_issue: require approval, high risk
Creating a Custom Policy Pack
Step 1: Create the Rego file
cat > packages/policy/src/policies/my-tool.rego << 'EOF'
package aaf.policy.my_tool
import future.keywords.if
default allow := false
default require_approval := false
default risk_level := "critical"
default reason := "Action denied by policy"
# Your rules here
EOFStep 2: Register it in the policy index
Add a PolicyDefinition entry to POLICIES in packages/policy/src/policies/index.ts:
export const POLICIES: PolicyDefinition[] = [
// ... existing entries
{
path: 'aaf/policy/my_tool',
name: 'My Tool Policy',
description: 'Enforces access control for my_tool operations',
regoFile: 'my-tool.rego',
},
];Step 3: Write tests
Add a test under packages/policy/src/__tests__/ exercising the pack through the OPA client, then run pnpm --filter @aaf/policy test.
Org-Specific Allowlists
Host allowlists are stored per-organization and passed to policies as input.context.org_allowlist.
Managing Allowlists via API
# Read the current allowlist
curl https://api.agentactionfirewall.com/admin/settings/allowlist \
-H "Authorization: Bearer $SUPABASE_JWT"
# Update it (full replace)
curl -X PUT https://api.agentactionfirewall.com/admin/settings/allowlist \
-H "Authorization: Bearer $SUPABASE_JWT" \
-H "Content-Type: application/json" \
-d '{"domains": ["api.example.com", "*.internal.corp.com"]}'Via Dashboard
- Navigate to Settings in the sidebar
- Open the Allowlist tab
- Add or remove allowed hosts — changes take effect immediately
Troubleshooting
Policy Not Loading
# Verify policy syntax
opa check packages/policy/src/policies/my-tool.rego
# Evaluate locally against a sample input
opa eval -i input.json -d packages/policy/src/policies/ "data.aaf.policy"Unexpected Decisions
# Full evaluation trace
opa eval -i input.json -d packages/policy/src/policies/ \
--explain full "data.aaf.policy.http_proxy"