Skip to main content
Last updated on

Policies

Policies are stateless permission checks written in OPA Rego. Each policy evaluates a single input document at runtime and returns a governance decision. Policies evaluate each operation independently — they don't track prior actions or session history.

Create and manage policies under Agent → Authorize → Policies.

When to use policies

Policies give you fine-grained, field-level control over individual operations. Use them when the decision depends on properties of a single request — what tool is being called, what value a field contains, or what risk tier the agent belongs to. Where guardrails validate and transform content, policies answer a different question: "is this specific operation allowed right now?"

Create Policy

If an agent has no policy yet, the Policies sub-tab shows an empty state message and a Create Policy button. Use the Create Policy action to get started.

Policy Editor

When you create or edit a policy you provide:

  • A policy name (for operators/audit trails)
  • Rego source code

Policy Result Shape

Policies should return a single object (commonly named result) with:

  • decision: the policy outcome (example: CONTINUE, REQUIRE_APPROVAL)
  • reason: optional explanation for why the decision was produced

The platform uses this result to produce an authorization decision and to explain the outcome in audit trails.

Testing Policies

You can test Rego using the Rego Playground: https://play.openpolicyagent.org/

Recommendation: test the policy logic in OPA Playground first, then paste it into OpenBox Policy Editor.

Edit Policy

When a policy already exists, the Policies sub-tab shows:

  • A Rego editor for the policy source
  • A results area that shows the evaluated decision and reason

After changes, use the Save action to update the policy attached to the agent.

Runtime Enforcement

At runtime, policies are evaluated against a single input document (input).

Common input concepts:

  • Agent properties (identity, trust score/tier, risk tier)
  • Operation context (what kind of action is happening)
  • Activity spans (semantic types detected during execution)
  • Request/session context used to decide whether an operation should proceed

Your policy should be written defensively:

  • Prefer default result = ... so the policy always produces a decision
  • Avoid assumptions about optional fields being present

Policy Input Fields

Before diving into examples, here are the key fields available in the policy input document:

FieldSourceDescription
activity_typeAgent-definedThe type of Temporal activity being evaluated. Your agent defines its own activity types based on how you've structured your workflow. In the demo agent, agent_toolPlanner is the activity that calls the LLM and returns a structured tool call.
activity_output.toolAgent-definedThe name of the tool the agent is planning to call. Tools are custom functions that your agent registers. For example, CreateInvoice or CurrentPTO.
activity_output.argsAgent-definedThe arguments being passed to the tool. These are tool-specific and defined by your agent's tool schema.
event_typePlatformThe Temporal event type (e.g., ActivityCompleted). Provided by the platform.
risk_tierPlatformThe agent's assessed risk tier (1–4). Assigned in the platform under agent settings.
spansPlatformOperation-level classifications attached to activity execution. Each span has a semantic_type (e.g., database_select, file_read, llm_completion) that describes what kind of operation occurred.

Examples

Require approval for invoice creation

When every invoice must go through a human reviewer regardless of amount — a common requirement for newly deployed agents or regulated workflows.

Although behavioral rules can also enforce approvals, policies let you define more customized, field-level approval logic.

Substitute your own names

This example uses agent_toolPlanner (the demo agent's activity type for tool-call decisions) and CreateInvoice (a custom tool name from the demo's tool registry). Replace these with your own activity type and tool names.

package openbox

default result := {"decision": "CONTINUE", "reason": ""}

result := {"decision": "REQUIRE_APPROVAL", "reason": "Invoice creation requires human approval before proceeding"} if {
input.activity_type == "agent_toolPlanner"
input.activity_output.tool == "CreateInvoice"
}

Test input:

{
"activity_type": "agent_toolPlanner",
"event_type": "ActivityCompleted",
"activity_output": {
"tool": "CreateInvoice",
"next": "tool",
"args": {
"Amount": 1395.71,
"TripDetails": "Qantas flight from Bangkok to Melbourne",
"UserConfirmation": "User confirmed booking"
},
"response": "Let's proceed with creating an invoice for the Qantas flight."
}
}

Test output:

{
"result": {
"decision": "REQUIRE_APPROVAL",
"reason": "Invoice creation requires human approval before proceeding"
}
}

Runtime result:

temporalio.exceptions.ApplicationError: ApprovalPending: Approval required for output: Invoice creation requires human approval before proceeding

Approval visibility in OpenBox platform:

  • Approvals (main sidebar)
  • Adapt → Approvals (agent page)

Require approval for high-value invoices only

When low-value operations can proceed automatically but high-value ones need human sign-off — balancing speed with risk control.

This variant keeps normal invoice creation automatic while routing high-value invoices to human approval. As with the previous example, replace agent_toolPlanner and CreateInvoice with your own activity type and tool names.

package openbox

default result := {"decision": "CONTINUE", "reason": ""}

result := {"decision": "REQUIRE_APPROVAL", "reason": "High-value invoice requires human approval before proceeding"} if {
input.activity_type == "agent_toolPlanner"
input.activity_output.tool == "CreateInvoice"
object.get(input.activity_output.args, "Amount", 0) >= 1000
}

Test input (approval expected):

{
"activity_type": "agent_toolPlanner",
"event_type": "ActivityCompleted",
"activity_output": {
"tool": "CreateInvoice",
"next": "tool",
"args": {
"Amount": 1395.71,
"TripDetails": "Qantas flight from Bangkok to Melbourne",
"UserConfirmation": "User confirmed booking"
},
"response": "Let's proceed with creating an invoice for the Qantas flight."
}
}

Test output:

{
"result": {
"decision": "REQUIRE_APPROVAL",
"reason": "High-value invoice requires human approval before proceeding"
}
}

Runtime result:

temporalio.exceptions.ApplicationError: ApprovalPending: Approval required for output: High-value invoice requires human approval before proceeding

Risk-tier-driven approvals

When different agents carry different risk profiles and you want to tighten or relax controls based on the agent's assessed risk tier.

This example uses spans — operation-level classifications the platform attaches to activity execution. Each span carries a semantic_type (e.g., database_select, file_read, llm_completion) that describes the kind of operation that occurred. The policy restricts different semantic types at each risk tier.

package org.openboxai.policy_564f9d9cc31b408c9947e04d64dbb7aa

tier2_restricted := {"internal"}
tier3_restricted := {"database_select", "file_read", "file_open"}
tier4_restricted := {"database_select", "file_read", "file_open", "llm_completion"}

default result = {"decision": "CONTINUE", "reason": null}

result := {"decision": "CONTINUE", "reason": null} if {
input.risk_tier == 1
}

result := {"decision": "REQUIRE_APPROVAL", "reason": "T2: internal tools blocked"} if {
input.risk_tier == 2
some span in input.spans
tier2_restricted[span.semantic_type]
}

result := {"decision": "CONTINUE", "reason": null} if {
input.risk_tier == 2
not has_restricted_span(tier2_restricted)
}

result := {"decision": "REQUIRE_APPROVAL", "reason": "T3: db/file blocked"} if {
input.risk_tier == 3
some span in input.spans
tier3_restricted[span.semantic_type]
}

result := {"decision": "CONTINUE", "reason": null} if {
input.risk_tier == 3
not has_restricted_span(tier3_restricted)
}

result := {"decision": "REQUIRE_APPROVAL", "reason": "T4: restricted"} if {
input.risk_tier == 4
some span in input.spans
tier4_restricted[span.semantic_type]
}

result := {"decision": "CONTINUE", "reason": null} if {
input.risk_tier == 4
not has_restricted_span(tier4_restricted)
}

has_restricted_span(restricted_set) if {
some span in input.spans
restricted_set[span.semantic_type]
}