Cua Docs

Write a capability manifest

Narrow any Cua Driver permission profile to explicit tools and resources.

Use a capability manifest when a Cua Driver runtime should stay inside a reviewed set of applications, browser origins, files, and tools. The manifest is optional in standard and unrestricted and required in bounded.

1. Create the manifest#

Save this as cua-capabilities.yaml and replace the example paths:

version: 3
expires_after: 8h
idle_timeout: 30m
 
allow:
  tools:
    - start_session
    - end_session
    - launch_app
    - get_window_state
    - click
    - type_text
    - press_key
    - kill_app
 
resources:
  apps:
    - executable: /usr/bin/example-editor
      launch: true
      windows: all
      terminate: driver_launched
 
  files:
    read:
      - dir: /data/input
        recursive: true
    write:
      - dir: /data/output
        recursive: true
 
  desktop:
    display: false

This manifest drives a native application. Browser work uses a separate manifest, for the reason described in the next step.

On macOS, replace executable with the application's bundle_id:

resources:
  apps:
    - bundle_id: com.example.Editor
      launch: true
      windows: all
      terminate: driver_launched

Windows and Linux application entries use a canonical absolute executable path.

2. Select browser access#

A capability manifest that drives a browser is a separate, typed-browser-only manifest. It cannot also allow generic input or window observation.

Every navigation is checked against resources.browser.origins, so a browser manifest must list at least one origin. Declaring origins then excludes the tools that could reach a page without crossing the typed browser adapter: click, double_click, right_click, drag, scroll, type_text, press_key, hotkey, set_value, the mouse primitives, get_accessibility_tree, get_window_state, verify_state, get_desktop_state, and page. Including any of them alongside origins is refused when the runtime starts:

authorization startup error: origin-scoped capability manifests cannot allow
'click' because it bypasses the typed browser origin adapter

The check reads allow.tools alone, so it applies even when the manifest's application scope contains no browser. Use list_windows rather than get_window_state to find the window_id a browser call needs.

For a driver-owned browser profile:

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
 
resources:
  browser:
    profiles:
      - kind: isolated
    origins:
      - https://app.example.com
 
  desktop:
    display: false

If the task must use a logged-in Chromium profile, make that choice explicit by replacing the profile kind:

resources:
  browser:
    profiles:
      - kind: existing_profile
    origins:
      - https://app.example.com

An existing-profile entry supplies resource scope. In bounded, the in-scope attachment is unattended. In standard, the normal launch grant or trusted host decision is still required. The typed browser tools validate the live top-level origin before input in every profile.

An agent that needs both a browser and generic desktop input needs two runtimes: this manifest for the browser, and a separate application-scoped manifest for the desktop work.

Scope that second runtime so it cannot reach a browser window. A runtime that can click the browser makes the first runtime's origin list decorative, because nothing checks an origin on the generic input path. Give it a capability manifest naming only the non-browser applications it needs, and keep desktop.display: false. A standard-mode runtime is not a substitute: standard allows input against any application, including the browser.

3. Authorize display access#

Application entries with windows: all authorize observation and input for windows belonging to that application.

Keep desktop.display: false when the workflow does not need unfiltered desktop capture or desktop-coordinate input. Set it to true only when the agent needs full-display access:

resources:
  desktop:
    display: true

4. Choose file roots#

Directory grants are checked by canonical path component:

resources:
  files:
    read:
      - dir: /data/input
        recursive: true
    write:
      - dir: /data/output
        recursive: true

recursive: false allows direct children only. Existing version 1 manifests may still list exact path strings, but version 2 and 3 directory roots are more practical for unattended output.

5. Start Cua Driver#

cua-driver serve \
  --permission-mode bounded \
  --capability-manifest ./cua-capabilities.yaml \
  --approve-capability-manifest

The approval flag confirms that the trusted launcher reviewed this exact file. It is not accepted from a tool call.

To narrow standard, use the same flags with --permission-mode standard. Routine standard behavior remains promptless, while residual boundaries such as existing-profile attachment still require their normal grant. To narrow unrestricted, add the manifest flags alongside --dangerously-bypass-approvals; the approval bypass then applies only inside the manifest.

For MCP, point the client at the running daemon:

{
  "mcpServers": {
    "cua": {
      "command": "cua-driver",
      "args": ["mcp", "--socket", "/path/to/cua-driver.sock"]
    }
  }
}

Use the platform's reported default endpoint unless your launcher selected a custom --socket.

6. Test denial before unattended use#

Confirm all three cases:

  1. An allowed tool against an allowed resource succeeds without a Cua prompt.
  2. An allowed tool against a different app, origin, or path returns bounded_resource_outside_manifest.
  3. A tool omitted from allow.tools returns permission_denied.

Also verify expiry and revocation:

cua-driver revoke --session test-run
cua-driver revoke --all

revoke --all suspends the complete runtime generation. Restart the daemon before starting a new run.

Know what the manifest is worth#

A manifest bounds one runtime's tool surface. It is not a sandbox around your machine, your browser, or your logged-in accounts. Another Cua Driver runtime, or any other process running as the same user, is unaffected by it. A standard-mode runtime without its own capability manifest allows input against every application.

Read Permission modes before relying on a manifest as a security boundary rather than as a reviewed statement of what one agent run may do.

Rules to remember#

  • The manifest only narrows the built-in and configured policy ceilings.
  • Ownership never bypasses the manifest.
  • Unknown tools and missing resources fail closed.
  • Browser origins match exact scheme, host, and port.
  • Declaring browser origins excludes generic input and window observation from the same manifest.
  • terminate: driver_launched requires a fresh process fingerprint match.
  • Version 3 has no ask.tools; legacy version 1/2 ask.tools entries are deny.
  • Keep expiry and idle timeouts as short as the task permits.

See Permission modes for the complete mode matrix and Permission policies for administrator and user policy layers.