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: falseThis 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_launchedWindows 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 adapterThe 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: falseIf 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.comAn 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: true4. Choose file roots#
Directory grants are checked by canonical path component:
resources:
files:
read:
- dir: /data/input
recursive: true
write:
- dir: /data/output
recursive: truerecursive: 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-manifestThe 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:
- An allowed tool against an allowed resource succeeds without a Cua prompt.
- An allowed tool against a different app, origin, or path returns
bounded_resource_outside_manifest. - A tool omitted from
allow.toolsreturnspermission_denied.
Also verify expiry and revocation:
cua-driver revoke --session test-run
cua-driver revoke --allrevoke --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_launchedrequires a fresh process fingerprint match.- Version 3 has no
ask.tools; legacy version 1/2ask.toolsentries 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.