Permission modes
Choose standard, bounded, or unrestricted authorization for Cua Driver.
Cua Driver enforces authorization inside the native runtime, after transport arguments are sanitized and before a platform action runs. CLI, MCP, direct SDK, private-worker, and service-backed calls all reach the same enforcement boundary.
The three public names are authorization profiles over that shared engine. They select built-in capability and approval behavior; a capability manifest is a separate narrow-only ceiling.
Choose a mode#
| Mode | Best for | Runtime behavior |
|---|---|---|
standard | Normal local CLI and MCP use | Routine operations follow the built-in profile. Residual boundaries still require a launch grant or trusted host decision. An optional capability manifest can narrow tools and resources. |
bounded | Unattended agents, gateways, and embedded applications | A capability manifest is required. In-scope work is silent and undeclared scope is denied. |
unrestricted | Disposable or fully trusted environments | Cua approval checks are bypassed after an explicit dangerous acknowledgement. An optional capability manifest limits that bypass to declared scope. |
| Profile | Manifest absent | Manifest present |
|---|---|---|
standard | Preserve standard behavior | Standard behavior intersected with manifest scope; residual grants remain required |
bounded | Startup fails | Unattended, deny-by-default bounded behavior with required lifetimes |
unrestricted | Preserve the acknowledged approval bypass within existing ceilings | Bypass approval only inside manifest scope |
standard is the default:
cua-driver mcpUse bounded with a reviewed manifest:
cua-driver serve \
--permission-mode bounded \
--capability-manifest ./cua-capabilities.yaml \
--approve-capability-manifestUse unrestricted only when full autonomous access is intentional:
cua-driver serve --dangerously-bypass-approvalsAdd the same two capability-manifest flags to standard or unrestricted
when that runtime should have a smaller tool and resource surface. A manifest
can narrow a profile but never widen it.
Passing --permission-mode unrestricted without
--dangerously-bypass-approvals fails closed.
Unrestricted mode does not defend against prompt injection or unintended model actions. Use it only where you accept the full effect of every capability allowed by the built-in, managed, and user policy ceilings.
autonomous remains a manifest compatibility alias for bounded. yolo
remains a configuration alias for unrestricted. New integrations should use
the canonical names.
Configure a mode without CLI flags#
The mode is read once, when the runtime that owns the driver starts. Only
cua-driver serve takes the flags above. A launcher that cannot pass them —
cua-driver mcp owning its own runtime on Windows and Linux, a Windows
Scheduled Task registered by cua-driver autostart enable, or an embedding
host — uses the equivalent environment variables:
| Flag | Environment variable |
|---|---|
--permission-mode <mode> | CUA_DRIVER_PERMISSION_MODE=<mode> |
--capability-manifest <path> | CUA_DRIVER_CAPABILITY_MANIFEST_FILE=<path> |
--approve-capability-manifest | CUA_DRIVER_CAPABILITY_MANIFEST_APPROVED=1 |
--dangerously-bypass-approvals | CUA_DRIVER_DANGEROUSLY_BYPASS_APPROVALS=1 |
The variables accept 1, true, yes, or on for their boolean form. The CLI
flag normalizes mode plus acknowledgement in one step; the environment form does
not, so unrestricted requires CUA_DRIVER_PERMISSION_MODE=unrestricted and
CUA_DRIVER_DANGEROUSLY_BYPASS_APPROVALS=1, and bounded requires both the
capability manifest and its approval. Missing halves fail startup instead of downgrading
to standard.
CUA_DRIVER_SESSION_POLICY_FILE, CUA_DRIVER_SESSION_POLICY_APPROVED,
--session-policy, and --approve-session-policy remain deprecated aliases
for one compatibility window. New integrations should use the capability
manifest names. Conflicting old and new paths fail closed.
These variables are trusted launch configuration on the same footing as the flags. Whoever can write them for the daemon's environment sets its mode, so treat them like the plist, unit file, or Scheduled Task that carries them. An agent tool call can never set them.
A running daemon's mode cannot be changed. Stop it and start it again with the configuration you want.
What standard allows#
Standard mode is designed to preserve practical autonomous workflows. It does not open a Cua confirmation card for routine actions.
| Operation | Standard behavior |
|---|---|
| Observe windows, applications, and the desktop | Allow |
| Click, type, scroll, drag, and focus | Allow |
| Create and use a driver-owned isolated browser | Allow |
| Read visible page content through typed browser tools | Allow |
| Upload, download, screenshot, record, and replay validated paths | Allow |
| Change agent-adjustable cursor and image settings | Allow and audit |
| Terminate a process proven to have been launched by this runtime | Allow after process fingerprint revalidation |
| Terminate a foreign process | Deny |
| Run unbounded legacy page mutation scripts | Deny |
| Raise an operating-system permission prompt from an agent tool call | Deny |
| Attach to an existing logged-in Chromium profile | Require an explicit launch grant or trusted host authorization |
| Invoke an unknown risk-bearing operation | Deny |
Path canonicalization, browser-origin validation, process identity checks, and managed or user policy still apply when standard permits an operation.
Driver-owned isolated browser preparation follows the selected mode and any optional capability manifest. Standard allows it as a routine operation, bounded requires it to match the manifest, and unrestricted relies on the launcher's dangerous acknowledgement.
Existing logged-in Chromium profiles#
An existing profile can contain live cookies and authenticated sites. Standard mode therefore keeps attachment as an explicit boundary.
Authorize it for a newly launched MCP runtime:
cua-driver mcp --grant existing-profileAuthorize it for a daemon:
cua-driver serve --grant existing-profile--grant is repeatable and is trusted launch configuration. It cannot modify
an already-running daemon. Restart that daemon with the same grant when
needed.
Other valid authorization paths are:
- A capability manifest containing the exact profile scope, combined with the
profile's normal approval behavior. In
boundedit is unattended; instandarda launch grant or trusted host decision is still required. - A
DriverAuthorizationHostcallback installed by an embedding application. unrestrictedmode with its dangerous acknowledgement.
An MCP transport marker, model-supplied Boolean, environment variable, or ordinary tool argument is never an attachment grant.
One approved attachment covers the resulting browser binding. Tab changes and reconnection do not create repeated Cua authorization requests. Any configured origin scope still applies to live navigation and input.
See Browser Profile Attachment for the platform and CDP boundary.
Capability manifests#
A configured capability manifest is deny by default in every profile. A tool
must appear in allow.tools, and every resource crossed by that call must
match the manifest. Approval is considered only after those checks pass.
Manifest version 3 removes profile behavior from the file. It supports the same application identities, browser profile kinds, and directory roots as version 2:
version: 3
expires_after: 8h
idle_timeout: 30m
allow:
tools:
- start_session
- end_session
- launch_app
- list_windows
- browser_prepare
- get_browser_state
- browser_navigate
- browser_click
- browser_type
- browser_download
- kill_app
resources:
apps:
- executable: /usr/bin/example-editor
launch: true
windows: all
terminate: driver_launched
browser:
profiles:
- kind: isolated
- kind: existing_profile
origins:
- https://app.example.com
files:
read:
- dir: /data/input
recursive: true
write:
- dir: /data/output
recursive: true
desktop:
display: falseOn macOS, use bundle_id for an application identity. On Windows and Linux,
use a canonical absolute executable path. windows: all allows windows
owned by the matching application. terminate: driver_launched permits
termination only when Cua proved that the current runtime launched that exact
process instance.
Browser origins match exact scheme, host, and port. File roots are canonicalized and compared by path component, so a shared string prefix or a symlink escape does not grant access.
Origin scope excludes generic input#
resources.browser.origins binds the typed browser adapter only. A manifest
that declares origins therefore cannot also allow a tool that reaches a page
around that adapter, and the runtime refuses to start if it does:
authorization startup error: origin-scoped capability manifests cannot allow
'click' because it bypasses the typed browser origin adapterThe excluded tools are click, double_click, right_click, drag,
scroll, type_text, press_key, hotkey, set_value,
mouse_button_down, mouse_button_up, mouse_drag, parallel_mouse_drag,
get_accessibility_tree, get_window_state, verify_state,
get_desktop_state, and page.
The exclusion covers observation as well as input. A window screenshot or accessibility tree of a browser window exposes whichever tab is open, regardless of the origin allow-list, so where a browser window is in reach these tools bound neither what an agent may change nor what it may read.
The check is evaluated at load time against allow.tools alone. It does not
analyze whether the manifest's application scope actually puts a browser window
in reach, so a manifest that names only a non-browser application is refused on
the same terms. This is deliberate: deciding reachability would require the
runtime to keep a list of browser identities, and a stale list would void the
origin boundary silently instead of refusing loudly.
Every navigation is checked against the origin set, so a manifest that allows
browser_navigate with no origins refuses every navigation. Browser access and
generic desktop input consequently belong to separate manifests and separate
runtimes. Use list_windows for the window_id a browser call needs.
An origin scope binds one runtime's tool surface. It is not a property of the
browser, the profile, or the machine. A second runtime that can deliver generic
input to a browser window — including a standard-mode runtime without a
capability manifest, which allows input against every application — voids the first runtime's origin scope
without violating it. Scope a companion runtime to non-browser applications
with desktop.display: false, and treat same-user processes outside Cua Driver
as outside the boundary entirely.
Version 1 and 2 manifests remain loadable. Their mode field is retained for
compatibility, and legacy ask.tools entries are treated as deny. Version 3
removes mode and ask.tools. expires_after and idle_timeout are optional
in version 3, but any declared lifetime is enforced in every profile. Bounded
mode requires both fields.
For a complete tested example, see Write a capability manifest.
Authorization stack#
Every call must pass all applicable layers:
- Hard invariants, including self-targeting and protected-host checks.
- The reviewed built-in tool and risk map.
- Administrator policy from
CUA_DRIVER_MANAGED_POLICY_FILE, when set. - User policy from
CUA_DRIVER_POLICY_FILE, when set. - The selected profile's capability and approval behavior.
- The capability manifest, when configured.
- A launch grant or trusted host authorization for a residual standard-mode boundary.
Each layer can narrow access. No manifest, grant, approval bypass, or permission profile can widen a hard invariant or managed/user policy.
The tools/list, manifest, and status surfaces expose content-free
enforcement descriptors. Each descriptor states its adapter ID, risk class,
resource scope, mode behavior, authorization source, revocation triggers, and
stable refusal code.
Host authorization and activity#
An embedding application may install DriverAuthorizationHost when it wants
to decide a residual standard-mode request itself. Cua supplies an attested,
request-bound resource and digest. The host returns allow, deny, or cancel with
that exact digest.
Cua does not render or prescribe the host's consent experience. The host may use product UI, administrator policy, a second device, or another trusted decision source.
An optional DriverActivityObserver receives content-free action,
authorization, grant, and session events. Events never contain page text,
typed input, file contents, screenshots, or raw resource identities.
When no launch grant or host callback exists, a residual boundary returns a
structured authorization_required refusal. It does not open a Cua-owned
modal.
Revocation#
End one public session:
cua-driver revoke --session research-1Suspend the complete runtime generation:
cua-driver revoke --allSession end removes its grants, browser bindings, launch provenance, recording
state, and cursor. The ended session label remains tombstoned until an explicit
start_session re-declares it.
revoke --all is terminal for that runtime generation. Later calls, including
anonymous calls, return authorization_suspended. Restart the runtime to
create a fresh authorization generation.
Cursor and session badge#
The semantic cursor is optional activity feedback. It initializes on the first cursor-bearing action even when the transport uses an implicit session. A publicly named session displays its sanitized label in a badge below the cursor. The badge uses the session color, strips control characters, collapses whitespace, and truncates long labels.
The cursor and badge are not authorization signals. Hiding the cursor also hides the badge. Headless sessions do not require either.
Security boundary#
The host owns the permission profile, capability manifest, launch grants, and any human consent UX. Agent tools cannot change them.
Same-user native code, malware, and automation with control of the same desktop are outside Cua Driver's security boundary. Cua can prevent an agent from forging its own tool arguments, but it cannot turn an ordinary desktop window into a secure desktop.