Cua Docs

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#

VariableTypeDefault
CUA_DRIVER_POLICY_FILEpath to a .yaml, .yml, .rego file, or a directorynot set (policy disabled)
CUA_DRIVER_MANAGED_POLICY_FILEpath to an administrator-owned policy with the same formatsnot 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 blocked

Both 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_execute

allow.tools#

A list of tool names that are permitted for any argument values. Order does not matter.

allow:
  tools:
    - screenshot
    - get_window_state
    - list_apps

allow.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: 10

The 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.

FieldTypeApplies toMeaning
minnumbernumeric argumentargument value must be ≥ this
maxnumbernumeric argumentargument value must be ≤ this
max_lengthintegerstring argumentUnicode codepoint count must not exceed this
patternstring (regex)string argumentargument must match this regular expression
allowedlist of JSON valuesanyargument 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_javascript

Full 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_javascript

Rego 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.policy

allow 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:

FieldTypeDescription
input.serverstringAlways "cua-driver"
input.toolstringCanonical tool name (e.g. "type_text")
input.argumentsobjectTool 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/policies

Evaluation 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:

  1. Check deny.tools — if the tool is listed, deny immediately.
  2. Check allow.tools — if the tool is listed, allow immediately.
  3. Evaluate allow.rules for matching rules — if any rule's constraints pass, allow.
  4. No match — deny with "tool 'X' is not allowed by the YAML policy".

For Rego policies:

  1. Evaluate data.cua.policy.allow with the call's input.
  2. trueallow; false or undefineddeny; evaluation error → error (the call is blocked with the engine error message).

Tool name normalization#

Before any policy evaluation the driver canonicalizes deprecated aliases:

AliasCanonical name
type_text_charstype_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