Permission policies
YAML and Rego permission policy schema, environment variable, evaluation rules, and Rego input interface for Cua Driver.
Cua Driver evaluates every same-process SDK and daemon tool call against the configured policy stack at the native registry boundary. The runtime loads policies once at process startup; transport adapters may repeat an early policy check as defense in depth. SDK, CLI, MCP, and raw-socket calls cannot bypass the native enforcement point. This page describes the user and managed policy formats. Permission modes select the default autonomy model, while policies can only narrow its capability ceiling.
Environment variable#
| Variable | Type | Default |
|---|---|---|
CUA_DRIVER_POLICY_FILE | path to a .yaml, .yml, .rego file, or a directory | not set (policy disabled) |
CUA_DRIVER_MANAGED_POLICY_FILE | path to an administrator-owned policy with the same formats | not set |
When the variable is absent the policy engine is disabled and every call is allowed.
When either variable is explicitly set, a missing, unreadable, empty, or invalid path prevents the daemon from binding its action endpoint. An unset variable still means that layer is absent.
When both layers are present, a call must pass both. The user policy can narrow the managed policy but cannot widen it. cua-driver status reports a SHA-256 content hash for each loaded layer without exposing policy contents.
YAML policy format#
Top-level structure#
allow: # what to permit (default: nothing allowed)
tools: [] # tool names allowed unconditionally
rules: [] # rules with argument constraints
deny: # hard overrides (checked first)
tools: [] # tool names always blockedBoth allow and deny are optional and default to empty lists. A policy with neither section blocks every call.
deny.tools is checked before allow.tools and allow.rules. A tool in deny.tools is rejected even if it also appears in an allow section.
Shorthand forms#
The allow and deny fields also accept a flat list of strings as a shorthand for the tools sub-key:
allow:
- screenshot
- wait
deny:
- shell_executeallow.tools#
A list of tool names that are permitted for any argument values. Order does not matter.
allow:
tools:
- screenshot
- get_window_state
- list_appsallow.rules#
A list of tool rules. Each rule names a tool and an optional set of argument constraints. A call matches a rule when all constraints for that rule pass. If the same tool appears in multiple rules, the call is allowed when any rule matches.
allow:
rules:
- tool: type_text
constraints:
text:
max_length: 200
- tool: scroll
constraints:
amount:
min: -10
max: 10The constraints map keys are JSON argument names (snake_case, matching the names in MCP tools).
Constraint fields#
Each constraint object may include one or more of the following checks. At least one check is required.
| Field | Type | Applies to | Meaning |
|---|---|---|---|
min | number | numeric argument | argument value must be ≥ this |
max | number | numeric argument | argument value must be ≤ this |
max_length | integer | string argument | Unicode codepoint count must not exceed this |
pattern | string (regex) | string argument | argument must match this regular expression |
allowed | list of JSON values | any | argument must equal one of the listed values |
When both min and max are set, min must be ≤ max or the policy fails to load.
The pattern field uses the Rust regex crate syntax (RE2-compatible; no backtracking, no lookahead).
deny.tools#
A list of tool names that are unconditionally blocked, regardless of allow rules.
deny:
tools:
- shell_execute
- run_javascriptFull YAML example#
allow:
tools:
- screenshot
- get_window_state
- list_apps
- list_windows
- click
- press_key
- scroll
- hotkey
- wait
rules:
- tool: type_text
constraints:
text:
max_length: 500
pattern: "^[\\x20-\\x7E\\n\\t]*$"
- tool: launch_app
constraints:
bundle_id:
allowed:
- "com.apple.Safari"
- "com.google.Chrome"
- "com.microsoft.VSCode"
- tool: scroll
constraints:
amount:
min: -20
max: 20
deny:
tools:
- shell_execute
- run_javascriptRego policy format#
Rego policies must define a rule named data.cua.policy.allow. The engine uses Regorus 0.10.x, which implements the OPA policy language.
Package declaration#
package cua.policyallow rule#
The rule must evaluate to a boolean. undefined is treated as false (deny).
package cua.policy
import rego.v1
allow if {
# rule body
}Input object#
The engine sets input to the following object for each call:
| Field | Type | Description |
|---|---|---|
input.server | string | Always "cua-driver" |
input.tool | string | Canonical tool name (e.g. "type_text") |
input.arguments | object | Tool arguments with _session_id removed |
input.tool is normalized before evaluation: type_text_chars is mapped to type_text.
Minimal Rego example#
package cua.policy
import rego.v1
safe_tools := {
"screenshot",
"get_window_state",
"list_apps",
"list_windows",
"click",
"press_key",
"scroll",
"wait",
}
allow if {
input.tool in safe_tools
}
allow if {
input.tool == "type_text"
count(input.arguments.text) <= 500
}Loading multiple Rego files#
Point CUA_DRIVER_POLICY_FILE at a directory to load all .rego files in that directory. Files are loaded in lexicographic order (sort by filename), which controls rule merge order across files.
~/.cua-driver/policies/
00_base.rego
10_type_text.rego
20_launch_app.rego
export CUA_DRIVER_POLICY_FILE=~/.cua-driver/policiesEvaluation order#
The daemon first checks the managed policy and then the user policy. A denial or evaluation error in either layer stops the call. Within each YAML layer, evaluation follows this order:
For YAML policies:
- Check
deny.tools— if the tool is listed, deny immediately. - Check
allow.tools— if the tool is listed, allow immediately. - Evaluate
allow.rulesfor matching rules — if any rule's constraints pass, allow. - No match — deny with
"tool 'X' is not allowed by the YAML policy".
For Rego policies:
- Evaluate
data.cua.policy.allowwith the call's input. true→ allow;falseorundefined→ deny; evaluation error → error (the call is blocked with the engine error message).
Tool name normalization#
Before any policy evaluation the driver canonicalizes deprecated aliases:
| Alias | Canonical name |
|---|---|
type_text_chars | type_text |
Write policies against the canonical name; the alias is accepted automatically.
Denial response#
When a call is blocked, the MCP response is a tool error with a human-readable message:
Permission denied: tool 'shell_execute' is explicitly denied
Permission denied: tool 'launch_app' is not allowed by the YAML policy
Permission denied: tool 'type_text' argument constraints were not satisfied: argument 'text' must be at most 500 characters
Permission denied: tool 'launch_app' is not allowed by the Rego policy
Related#
- Restrict tool access with permission policies: step-by-step setup guide
- How permission policies work: evaluation engine internals and trust model
- Permission profiles and capability manifests: startup profiles, narrow-only manifests, launch grants, host callbacks, and revocation
- MCP tools: tool names and argument schemas