Skip to content

Policy-as-Code (Rego)

Rego Basics

Rego is a policy language by Open Policy Agent (OPA). It’s ideal for complex business logic that simple rules can’t express.

Basic Syntax

package policies.example
# Define a rule that blocks certain requests
deny[msg] {
# Condition: check if this applies
input.type == "request"
# Check: does the request meet the violation condition?
contains_api_key(input.text)
# Action: generate error message
msg := "API keys cannot be sent to AI"
}
# Helper function
contains_api_key(text) {
re_match("[a-z0-9_]{32,}", text)
}

How it works:

  1. If ALL conditions in a rule are true, the deny rule fires
  2. The msg is the error message shown to user
  3. Multiple deny rules can exist (any one firing blocks the request)

Common Patterns

Pattern 1: Check Field Value

deny[msg] {
input.model == "gpt-3.5-turbo" # Check field
msg := "Only GPT-4 or Claude allowed"
}

Pattern 2: Check Against Data

deny[msg] {
user := input.user_id
department := data.user_departments[user] # Look up user's department
department == "finance"
msg := "Finance users cannot use this model"
}

Pattern 3: Numeric Comparison

deny[msg] {
input.tokens_requested > 50000
msg := "Token limit exceeded"
}

Pattern 4: List Membership

deny[msg] {
approved_models := ["gpt-4", "claude-3"]
not input.model in approved_models
msg := sprintf("Model %v not approved", [input.model])
}

Pattern 5: Regex Matching

deny[msg] {
re_match("(?i)password|secret|key", input.text)
msg := "Cannot send secrets to AI"
}

Pattern 6: Arithmetic

deny[msg] {
monthly_used := data.monthly_tokens[input.user_id]
monthly_limit := 100000
monthly_used + input.tokens > monthly_limit
msg := "Monthly token limit would be exceeded"
}

Built-In Functions

String Functions

# Contains substring
"hello world" contains "hello" # true
# Regex match
re_match("^[0-9]+$", "123") # true
# String formatting
msg := sprintf("User %v exceeded limit", ["john"]) # "User john exceeded limit"
# Lowercase
lower("HELLO") # "hello"
# Uppercase
upper("hello") # "HELLO"

Array Functions

# Check membership
"gpt-4" in ["gpt-4", "claude"] # true
# Get array length
count(["a", "b", "c"]) # 3
# Check if array has any items
["a"] | length > 0 # true

Data Functions

# Look up value in data
data.user_roles["john"] # Get John's role
# Check if key exists
"john" in data.users # true/false
# Get all keys
keys := object.keys(data.users) # [all user IDs]

Real-World Examples

Example 1: Token Budget Per User

package policies.token_budget
deny[msg] {
user_id := input.user_id
daily_budget := 50000
tokens_used_today := data.daily_usage[user_id]
tokens_used_today + input.tokens > daily_budget
remaining := daily_budget - tokens_used_today
msg := sprintf(
"Daily token limit exceeded. Used: %v, Remaining: %v",
[tokens_used_today, remaining]
)
}

Example 2: Department-Based Approval

package policies.department_approval
deny[msg] {
user_id := input.user_id
user_department := data.departments[user_id]
model := input.model
# Check if this department can use this model
not can_department_use_model(user_department, model)
msg := sprintf(
"Department %v cannot use model %v",
[user_department, model]
)
}
can_department_use_model(dept, model) {
allowed := data.department_model_permissions[dept]
model in allowed
}

Example 3: Time-Based Restrictions

package policies.business_hours
deny[msg] {
input.type == "request"
input.user_type == "external"
# Get current hour (0-23)
now := time.now_ns()
hour := floor(now / 3600000000000) % 24
# Block external users outside 9-5 (9am to 5pm)
hour < 9
hour >= 17
msg := "External users can only access AI between 9 AM and 5 PM"
}

Example 4: Risk-Based Rules

package policies.high_risk
deny[msg] {
input.type == "request"
is_sensitive_topic(input.text)
user_risk_score := data.user_risk[input.user_id]
# Allow only low-risk users
user_risk_score > 0.7
msg := "Your account needs verification before using sensitive topics"
}
is_sensitive_topic(text) {
sensitive_keywords := ["medical", "financial", "legal", "personal"]
keyword := sensitive_keywords[_]
re_match(sprintf("(?i)%v", [keyword]), text)
}

Debugging Rego

Use print() to debug policies:

deny[msg] {
user_id := input.user_id
print(sprintf("Checking user: %v", [user_id])) # Debug output
department := data.user_departments[user_id]
print(sprintf("User department: %v", [department]))
department == "finance"
msg := "Finance users blocked"
}

When you test the policy, debug output appears in test results.

Complex Logic

If/Then/Else Pattern

check_approval[result] {
user_role := data.users[input.user_id].role
# If admin, approve
user_role == "admin"
result := "approved"
}
check_approval[result] {
# Else if high risk, need manual approval
data.high_risk_users[input.user_id]
result := "needs_manual_approval"
}
check_approval[result] {
# Else auto approve
result := "auto_approved"
}
# Use in deny rule
deny[msg] {
approval := check_approval[_]
approval == "needs_manual_approval"
msg := "Request requires manual approval"
}

Loops with foreach

violations[violation] {
# Check each department has at least 1 token budget
dept := data.departments[_]
budget := data.dept_budgets[dept]
budget == 0
violation := sprintf("Department %v has no token budget", [dept])
}

Testing Rego Policies

Always test before deployment:

  1. Go to GovernancePolicies → [Policy] → Test
  2. Enter test data:
    {
    "type": "request",
    "user_id": "user_123",
    "model": "gpt-4",
    "tokens": 5000,
    "text": "..."
    }
  3. See if policy triggers
  4. Adjust logic and retest

Common Mistakes

Mistake 1: Forgetting !=

# Wrong: This is assignment, not comparison
input.type = "request"
# Correct: Use == for comparison
input.type == "request"

Mistake 2: Missing Data Lookups

# Wrong: May crash if user not in data
department := data.departments[input.user_id]
# Correct: Check first
user_id := input.user_id
user_id in data.departments
department := data.departments[user_id]

Mistake 3: Logic Issues

# Wrong: This allows everything (always at least one condition true)
deny[msg] {
(input.model == "gpt-4") OR (input.model == "claude")
msg := "Models blocked"
}
# Correct: All conditions must be true
deny[msg] {
input.model != "gpt-4"
input.model != "claude"
msg := "Model not approved"
}