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 requestsdeny[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 functioncontains_api_key(text) { re_match("[a-z0-9_]{32,}", text)}How it works:
- If ALL conditions in a rule are true, the
denyrule fires - The
msgis the error message shown to user - Multiple
denyrules 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 matchre_match("^[0-9]+$", "123") # true
# String formattingmsg := sprintf("User %v exceeded limit", ["john"]) # "User john exceeded limit"
# Lowercaselower("HELLO") # "hello"
# Uppercaseupper("hello") # "HELLO"Array Functions
# Check membership"gpt-4" in ["gpt-4", "claude"] # true
# Get array lengthcount(["a", "b", "c"]) # 3
# Check if array has any items["a"] | length > 0 # trueData Functions
# Look up value in datadata.user_roles["john"] # Get John's role
# Check if key exists"john" in data.users # true/false
# Get all keyskeys := 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 ruledeny[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:
- Go to Governance → Policies → [Policy] → Test
- Enter test data:
{"type": "request","user_id": "user_123","model": "gpt-4","tokens": 5000,"text": "..."}
- See if policy triggers
- Adjust logic and retest
Common Mistakes
Mistake 1: Forgetting !=
# Wrong: This is assignment, not comparisoninput.type = "request"
# Correct: Use == for comparisoninput.type == "request"Mistake 2: Missing Data Lookups
# Wrong: May crash if user not in datadepartment := data.departments[input.user_id]
# Correct: Check firstuser_id := input.user_iduser_id in data.departmentsdepartment := 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 truedeny[msg] { input.model != "gpt-4" input.model != "claude" msg := "Model not approved"}