How permission policies work
How the Cua Driver permission policy engine evaluates YAML and Rego policies, the trust model, and the guarantees the engine makes.
Cua Driver's permission policy engine sits at the native runtime dispatch boundary, between every public caller and the tool implementation. Before a direct SDK runtime, private worker, MCP process, or daemon executes a tool call, it asks the same policy engine whether the call is allowed.
The enforcement point#
Every public path reaches the authorization coordinator before platform dispatch. It evaluates the built-in risk map, managed policy, user policy, and optional capability manifest in order. Adapters may repeat a check earlier as defense in depth, but they cannot authorize a request the runtime denies. A denial returns an error to the client and the tool implementation is never reached.
Deny-by-default#
The engine is deny-by-default. A tool that is not explicitly mentioned in the policy is blocked. This means adding a new tool to the driver does not automatically expose it to agents; each tool must be explicitly permitted.
The deny-by-default behavior applies within each configured policy. When CUA_DRIVER_POLICY_FILE is unset, that layer is absent for compatibility. The reviewed built-in tool and risk map still rejects unknown tools, and the default permission mode remains standard.
An explicitly configured policy path is an operator assertion that the layer must exist. If the path is missing, unreadable, empty, or invalid, runtime construction fails before tools are registered or a service binds its action endpoint.
Policy composition and modes#
CUA_DRIVER_MANAGED_POLICY_FILE loads an administrator ceiling in the same
YAML or Rego formats. A call must pass both the managed and user layers. The
runtime hashes each immutable policy snapshot and includes those hashes in
authorization-host requests and status output.
Permission mode is separate from capability policy. Policy answers whether a
call is inside the allowed ceiling. Mode supplies the default autonomy model:
standard admits the reviewed built-in operation set, bounded narrows calls
to a reviewed manifest, and unrestricted removes Cua's runtime restrictions
after explicit launch-time risk acceptance. No mode can widen a managed or
user policy ceiling. See Permission modes and bounded
autonomy.
Runtime-lifetime snapshot#
The policy file is loaded once when the runtime starts. All subsequent calls through that runtime generation share the same immutable policy object. There is no reload endpoint and no hot-swap path. Changing the policy takes effect only after a direct runtime, private worker, MCP process, or daemon is restarted.
This makes the policy a reliable static contract: the same rule that was in effect when the runtime started remains in effect through its last admitted call.
YAML evaluation#
A YAML policy encodes three data structures at load time:
- A set of denied tool names (
deny.tools). These are checked first, before allow rules. - A set of unconditionally allowed tool names (
allow.tools). - A list of compiled rules (
allow.rules), each pairing a tool name with a set of compiled constraints.
At evaluation time, for a given (tool, arguments) pair:
- If the tool name is in the deny set, return Deny.
- If the tool name is in the allow set, return Allow.
- Find all rules whose
toolfield matches. If there are none, return Deny. - For each matching rule, test all constraints against the argument object. If all pass, return Allow. If any fail, collect the failure reason and try the next rule.
- If no rule passed, return Deny with the collected reasons.
Regular expression patterns in YAML constraints are compiled once at load time using the RE2-compatible Rust regex engine. There is no backtracking and no lookahead, which bounds evaluation time regardless of input length.
Rego evaluation#
Rego policies are loaded into a Regorus engine — a Rust-native OPA evaluator — at process startup. The engine validates that data.cua.policy.allow exists and evaluates to a boolean on a synthetic input before accepting the policy. A policy whose rule returns the wrong type is rejected at load time, not at call time.
At evaluation time, the driver:
- Constructs the input object:
{ "server": "cua-driver", "tool": "<canonical name>", "arguments": { … } }. - Clones the engine (the clone carries the compiled policy but gets a fresh input).
- Sets the input and evaluates
data.cua.policy.allow. - Maps the result:
true→ Allow,falseorundefined→ Deny, error → Error.
Because Regorus runs inside the runtime owner and does not spawn a policy subprocess, there is no additional policy IPC per call.
Argument sanitization before evaluation#
Two transformations happen before the arguments reach the policy engine:
- Internal session-field removal. Runtime and transport adapters may inject reserved session fields for lifecycle tracking. These are stripped before evaluation so a caller-controlled label cannot change policy authority.
- Tool name canonicalization. The deprecated
type_text_charsalias is normalized totype_textbefore any rule is consulted, so policies written against the canonical name cover both forms automatically.
What the engine does not cover#
The policy engine controls which tool calls are executed. It does not:
- Inspect or modify tool responses.
- Limit screenshot output, file paths read, or network traffic.
- Enforce rate limits or per-session quotas.
- Authenticate or identify the caller.
A policy that allows screenshot permits an agent to take an unlimited number of screenshots. A policy that allows type_text with a length constraint still permits the agent to call type_text up to the character limit on each call. Use the policy to define an allowed set of operations; combine it with OS-level sandboxing and process isolation if you need stronger guarantees.
Trust model#
The policy is evaluated in the same process as the tool implementation. An agent that can replace or inject code into the runtime-owning process can bypass it. Cua Driver does not render authorization UI. Use a trusted host to construct direct runtimes, a trusted launcher to supply explicit launch grants, or OS isolation for workers and services.
For remote agents connecting through an authenticated service, the policy provides a meaningful boundary: the service runtime will not execute a tool that the policy blocks, regardless of what the agent sends.
Related#
- Restrict tool access with permission policies: step-by-step setup guide
- Permission policies: YAML schema and Rego input interface
- Permission modes and bounded autonomy: how modes, launch grants, manifests, and policy layers compose
- Process model: direct, worker, MCP, and service ownership