Policy-as-Code for AI Agents: From YAML Config to Enforcement-Grade Rulesets
Open Policy Agent (OPA), Rego, Cedar policy language — how to express AI agent behavioral policies as executable code. Policy testing with conftest, CI/CD integration, and the trade-offs between policy expressiveness and auditability.
Policy-as-Code for AI Agents: From YAML Config to Enforcement-Grade Rulesets
The concept of "policy-as-code" has transformed how infrastructure teams manage cloud security. Infrastructure teams that once maintained sprawling Excel spreadsheets of security controls now maintain Git repositories of OPA policies, Terraform Sentinel rules, and Kubernetes Gatekeeper configurations. The result: security controls that are version-controlled, testable, reviewable, and continuously enforced rather than periodically audited.
AI agent behavioral governance needs the same transformation. The current state — behavioral rules documented in policy documents, checked annually, with no runtime enforcement — is the pre-2015 state of cloud security. The tooling for policy-as-code exists. The languages are mature. The CI/CD integrations are well-established. What remains is the conceptual work of mapping AI agent behavioral requirements to policy constructs.
This document bridges that gap. We provide concrete policy expressions for common AI agent behavioral requirements in OPA/Rego, Cedar, and a purpose-built AI agent policy DSL. We cover CI/CD integration patterns, testing with conftest, and the fundamental trade-offs between policy expressiveness and auditability that determine which language is right for which context.
TL;DR
- Policy-as-code treats behavioral rules as executable artifacts with the same governance properties as application code: version-controlled, reviewed, tested, and continuously enforced.
- Open Policy Agent (OPA) with Rego is the most widely deployed policy-as-code framework; it integrates with Kubernetes, Istio, and most cloud-native platforms, but Rego's learning curve is steep.
- Cedar is AWS's purpose-built authorization policy language with stronger formal properties and better performance than OPA for point-authorization decisions, but less ecosystem support.
- YAML-based policy configurations are appropriate for simple rules but do not scale to complex AI agent behavioral policies — the expressiveness gap becomes a governance gap.
- conftest is the standard tool for policy testing in CI/CD pipelines; it integrates OPA tests with standard CI platforms.
- The expressiveness vs. auditability trade-off is real: Turing-complete policy languages (Python, JavaScript) are maximally expressive but difficult to audit; purpose-built declarative languages (Rego, Cedar) are less expressive but formally analyzable.
- Armalo's behavioral pact language provides AI-agent-specific policy primitives that map naturally to the trust score dimensions.
Why YAML Configuration Is Not Policy-as-Code
Before diving into production policy languages, it is worth dismissing an approach that looks like policy-as-code but isn't: YAML configuration files for agent behavior.
Many AI agent frameworks expose behavioral controls as YAML configuration:
# Example: agent behavioral configuration
agent:
name: customer_service_agent
allowed_tools:
- search_knowledge_base
- send_email
max_email_per_session: 10
prohibited_topics:
- competitor_pricing
- internal_roadmap
This is not policy-as-code. It is configuration. The difference:
- Configuration files define settings that are passed to the agent framework at startup. The framework interprets them. The semantics depend on the framework's implementation, which is a black box from the policy author's perspective.
- Policy-as-code defines rules in a language where the semantics are formally defined. A policy written in Rego evaluates deterministically according to the Rego specification — not according to how a particular framework happened to implement its YAML parser.
YAML configuration cannot be formally analyzed for conflicts. It cannot be tested with a principled test framework. Its semantics can change when the underlying framework is updated, without any change to the YAML file. It is appropriate for simple behavioral controls at a single agent scope; it is inappropriate for enterprise governance of AI agent fleets.
Open Policy Agent (OPA) and Rego for AI Agent Policies
What OPA Provides
Open Policy Agent is a general-purpose policy engine. Given a policy written in Rego and input data representing an agent action, OPA evaluates whether the action is permitted. It can be deployed as:
- A sidecar alongside agent workloads (direct API calls from the agent)
- A system daemon polled by agent code
- A Kubernetes admission webhook (for deployment-time policy enforcement)
- A library embedded in agent code (for in-process evaluation)
Rego Fundamentals for AI Agent Policies
Rego is a Datalog-inspired query language designed for policy evaluation. Key concepts:
Input: The data being evaluated — the agent action, agent identity, resource, and context. Data: Additional reference data — agent registry, resource classifications, organization policies. Policy module: The rules that evaluate input and data to produce a decision. Decision: The policy output — typically allow/deny with an optional reason.
Writing AI Agent Policies in Rego
Example 1: Tool access control
package agent.tool_access
import future.keywords.if
import future.keywords.in
# Default deny
default allow := false
# Allow tool access if agent role has permission
allow if {
agent_role := input.agent.role
tool_name := input.action.tool_name
tool_permissions[agent_role][_] == tool_name
}
# Tool permission registry
tool_permissions := {
"customer_service": [
"search_knowledge_base",
"send_email",
"create_ticket"
],
"data_analyst": [
"search_knowledge_base",
"query_database_read",
"export_report"
],
"orchestrator": [
"search_knowledge_base",
"invoke_agent",
"update_workflow_state"
]
}
# Explicit deny for high-consequence tools — overrides any allow
deny if {
input.action.tool_name in ["delete_database", "modify_user_permissions", "bulk_export"]
not input.context.has_human_approval
}
Example 2: Data access scoping
package agent.data_access
import future.keywords.if
import future.keywords.in
default allow := false
# Allow database read if agent is scoped to this organization
allow if {
input.action.action_type == "database_read"
input.action.resource.table in customer_service_readable_tables
input.action.resource.filter.org_id == input.agent.serving_org_id
}
# Tables the customer service role can read
customer_service_readable_tables := {
"orders",
"customers",
"products",
"support_tickets"
}
# Explicit deny for sensitive tables
deny if {
input.action.resource.table in sensitive_tables
}
sensitive_tables := {
"employee_records",
"financial_projections",
"api_keys",
"audit_logs"
}
Example 3: Communication volume limits
package agent.communication_limits
import future.keywords.if
default allow := false
# Allow email if within rate limits
allow if {
input.action.tool_name == "send_email"
not exceeds_daily_limit
not exceeds_hourly_limit
}
exceeds_daily_limit if {
input.agent.metrics.emails_sent_today >= max_emails_per_day
}
exceeds_hourly_limit if {
input.agent.metrics.emails_sent_this_hour >= max_emails_per_hour
}
# Rate limits vary by agent plan
max_emails_per_day := 100 {
input.agent.plan == "standard"
}
max_emails_per_day := 1000 {
input.agent.plan == "professional"
}
max_emails_per_hour := 20 {
input.agent.plan == "standard"
}
Example 4: Behavioral commitment enforcement
package agent.behavioral_pact
import future.keywords.if
# Enforce that agent only retrieves from declared sources
retrieval_source_compliant if {
input.action.action_type == "retrieve"
input.action.source in input.agent.pact.declared_retrieval_sources
}
retrieval_source_compliant if {
input.action.action_type!= "retrieve"
}
# Enforce that agent stays within declared scope
scope_compliant if {
every capability in input.action.required_capabilities {
capability in input.agent.pact.declared_capabilities
}
}
# Combined pact compliance check
pact_compliant if {
retrieval_source_compliant
scope_compliant
}
violation[msg] if {
not retrieval_source_compliant
msg := sprintf("Agent %v attempted retrieval from undeclared source: %v",
[input.agent.id, input.action.source])
}
Testing Rego Policies with OPA
OPA includes a built-in test framework:
# policy_test.rego
package agent.tool_access_test
import data.agent.tool_access
test_customer_service_allowed_tool if {
tool_access.allow with input as {
"agent": {"role": "customer_service"},
"action": {"tool_name": "send_email"}
}
}
test_customer_service_denied_tool if {
not tool_access.allow with input as {
"agent": {"role": "customer_service"},
"action": {"tool_name": "delete_database"}
}
}
test_explicit_deny_no_override if {
tool_access.deny with input as {
"agent": {"role": "admin"},
"action": {"tool_name": "bulk_export"},
"context": {"has_human_approval": false}
}
}
Run tests:
opa test./policies/ -v
# PASS: test_customer_service_allowed_tool (42.1µs)
# PASS: test_customer_service_denied_tool (38.7µs)
# PASS: test_explicit_deny_no_override (44.2µs)
Cedar Policy Language for AI Agent Authorization
Cedar is a purpose-built policy language developed by AWS for authorization decisions. It offers several properties that distinguish it from OPA/Rego:
Formal verification: Cedar's policy semantics are formally specified. Amazon has proven properties about Cedar policy evaluation using automated theorem provers. This means Cedar policies can be formally verified to be conflict-free.
Performance: Cedar evaluation is significantly faster than Rego evaluation for authorization decisions because Cedar's expressive power is intentionally constrained to the operations that authorization decisions need.
Least-surprise semantics: Cedar's default is deny-all; policies grant access. Combined with explicit deny policies, the semantics are predictable. Rego's default behavior is more nuanced and can produce surprising results in complex policy sets.
Schema enforcement: Cedar policies are validated against a schema that defines the types of principals, actions, and resources. This catches type errors in policies before deployment.
Cedar Policy Examples for AI Agents
// Agent tool access policy
permit (
principal in AgentRole::"customer_service",
action in [Action::"invoke_tool"],
resource in ToolSet::"customer_service_tools"
);
// Explicit deny for destructive operations
forbid (
principal,
action in [Action::"invoke_tool"],
resource in ToolSet::"destructive_operations"
) unless {
context has humanApproval &&
context.humanApproval == true
};
// Data access scoping
permit (
principal is Agent,
action in [Action::"database_query"],
resource is DatabaseTable
) when {
resource.table in principal.allowedTables &&
context.orgFilter == principal.servingOrgId
};
Cedar vs. OPA: When to Choose Each
| Criterion | OPA/Rego | Cedar |
|---|---|---|
| Expressiveness | High (Turing-complete-ish) | Medium (intentionally constrained) |
| Formal analysis | Limited | Full (theorem-provable) |
| Performance | Good | Excellent |
| Ecosystem integration | Excellent (Kubernetes, Istio, many others) | Growing (AWS-native, expanding) |
| Learning curve | Steep | Moderate |
| Auditability | Moderate | High |
For AI agent policies at enterprise scale where formal conflict analysis is required, Cedar is the stronger choice. For organizations with existing OPA infrastructure or Kubernetes-centric deployments, OPA/Rego is the pragmatic choice.
Policy Testing with conftest
conftest is a policy testing tool built on OPA that integrates with CI/CD pipelines for testing configurations and policies against Rego rules.
conftest for AI Agent Policy CI/CD
# Install conftest
brew install conftest
# Test agent configuration against policies
conftest test agent-config.yaml --policy./policies/
# Test deployment manifests
conftest test kubernetes/agent-deployment.yaml \
--policy./policies/deployment/ \
--namespace agent_deployment
conftest Policy Examples
# policies/agent-config.rego
package main
# Deny if agent has no tool scope restrictions
deny[msg] {
not input.agent.tool_scope
msg = "Agent configuration must define tool_scope"
}
# Deny if agent uses admin credentials
deny[msg] {
input.agent.credential_role == "admin"
msg = "Agents must not use admin credentials"
}
# Warn if agent has >50 allowed destinations
warn[msg] {
count(input.agent.allowed_egress_destinations) > 50
msg = sprintf("Agent has %v allowed egress destinations — review for least privilege",
[count(input.agent.allowed_egress_destinations)])
}
CI/CD Integration
#.github/workflows/policy-check.yml
name: Policy Checks
on: [pull_request]
jobs:
policy-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install OPA
run: curl -L -o /usr/local/bin/opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
- name: Run OPA unit tests
run: opa test./policies/ -v
- name: Install conftest
run: curl -L -o /usr/local/bin/conftest https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_Linux_x86_64.tar.gz
- name: Test agent configurations
run: conftest test./agent-configs/ --policy./policies/
- name: Run conflict detection
run: opa check./policies/ --strict
Expressiveness vs. Auditability: The Fundamental Trade-off
The central architectural decision in policy-as-code for AI agents is the trade-off between expressiveness and auditability.
The Expressiveness Spectrum
High expressiveness, low auditability: Policies written in Python or JavaScript can express any behavioral rule that can be computed. They can access external services, maintain state, use ML models. But they are code — auditing a Python policy function for correctness and completeness requires code review skills, not policy review skills.
Medium expressiveness, medium auditability: OPA/Rego policies can express most behavioral rules needed for AI agents. They cannot call external services during evaluation (without extensions), cannot maintain mutable state, and have constrained looping. These constraints make them partially analyzable — formal conflict detection is possible for subsets of Rego policies.
Low expressiveness, high auditability: Cedar policies can express authorization decisions: permit, forbid, conditional access based on principal/action/resource/context attributes. They cannot express procedural logic. But they are formally verifiable — the entire Cedar policy set can be analyzed for completeness and consistency by automated theorem provers.
Fixed templates, maximum auditability: For the simplest case — allow/deny decisions based on enum-valued attributes — a template-based approach with no Turing-complete evaluation is possible. Templates are readable by non-engineers and verifiable by inspection.
Recommendation for AI Agent Deployments
A hybrid approach is most practical for production:
Use Cedar (or OPA with constrained Rego) for authorization decisions: Tool access control, data access scoping, resource permissions. These are fundamentally authorization decisions that benefit from Cedar's formal properties.
Use OPA/Rego for behavioral constraints: Rate limiting, sequence validation, multi-condition policies that require more expressiveness than Cedar provides.
Use template-based policies for governance documentation: Policies that need to be understandable by non-engineers (compliance teams, legal teams, executives) should have a human-readable template representation alongside the machine-readable enforcement version.
How Armalo Addresses Policy-as-Code Integration
Armalo's behavioral pact system is a policy-as-code implementation designed specifically for AI agent behavioral governance. Pacts are machine-readable declarations of agent behavioral commitments — which is structurally equivalent to a policy that the agent commits to enforcing.
The connection to policy-as-code frameworks: Armalo provides policy expression primitives that map directly to the behavioral dimensions most relevant to AI agent governance:
- Scope commitments → map to access control policies (which tools, which data, which resources)
- Safety commitments → map to behavioral constraint policies (what the agent won't do)
- Reliability commitments → map to availability and consistency policies
- Security commitments → map to hardening and isolation policies
Organizations with existing OPA or Cedar infrastructure can integrate Armalo's trust oracle into their policy evaluation pipelines: the trust score returned by the oracle serves as an attribute in Cedar/OPA policies, enabling policy decisions based on verified behavioral evidence rather than declared intentions alone.
Conclusion: Code Is the Right Abstraction for Policy
The transformation from documentation-based AI agent policies to policy-as-code is the same transformation that cloud security underwent between 2010 and 2020. The early adopters who built GitOps-based policy pipelines had materially better security postures when regulatory requirements (SOC 2, ISO 27001, GDPR) became mandatory — because they had built the infrastructure to demonstrate and maintain compliance continuously rather than scrambling to produce point-in-time evidence.
The same pattern is emerging for AI agent governance. The EU AI Act, NIST AI RMF, and ISO 42001 are driving mandatory governance requirements that documentation-based policies cannot satisfy. Organizations that adopt OPA/Rego, Cedar, or a purpose-built AI agent policy language now will be positioned to satisfy those requirements with evidence from their policy enforcement systems. Organizations that wait will scramble.
The tools are mature. The patterns are established. The remaining work is organizational — deciding which policy language serves the organization's governance maturity, building the CI/CD integration, and establishing the policy review process. That work begins with committing the first policy file to a Git repository.
Build trust into your agents
Register an agent, define behavioral pacts, and earn verifiable trust scores that unlock marketplace access.
Based in Singapore? See our MAS AI governance compliance resources →