Docs menu
DocsBuild an app

Capabilities & permissions

What the user experiences: which capabilities prompt, which are declared silently, and what is enforced in Rust.

Every capability falls into one of three consent tiers, and a fresh install shows an install-disclosure screen listing everything the app requests before it enters the dock. The user can turn ANY capability off at any time in the app's Permissions panel (the ⋯ menu → Settings on its row in the Apps manager) — even ones that never prompt — so treat every window.chatoss call as fallible and handle denial gracefully.

What the user experiences, per capability

  • chatApi, fileDrop, webSearch, documents, sqlite, secrets, proposeTask, boards, drive: declared in the manifest, never prompt.
  • driveShared: prompt-gated — it is the one drive path other apps' containers can see. Plain drive (your own container) never prompts.
  • No capability at all, so nothing to declare and nothing to prompt: manifest.get, scopedData.*, scopedTools.*, data.get/onChanged/publish, approvals.*, platform.info/platform.apis, apps.listApis, background.isHeadlessRun, documents.base64ToBytes.
  • fileAccess: one prompt on first use (Allow once / always / Deny). Denied dialogs return null.
  • terminal: prompts per command with the exact command shown; "always" is per program. Declared terminalCommandPrefixes are approved at install and skip the prompts; in headless/background runs only declared prefixes may run. → Terminal
  • webview: no per-call prompt — the manifest's webviewAllowlist IS the boundary (shown to the user and OS-enforced), so windows can only reach the domains you declared. → Webview
  • notifications: prompts once on first send (Allow once / always / Deny); send() returns false if denied.
  • clipboardWrite / clipboardRead: NO prompt — a low-friction convenience. The user can still turn either off any time in the app's Permissions panel; a disabled call rejects.
  • hostHttp: NO prompt for listed hosts — the httpAllowlist IS the boundary (enforced in Rust + an SSRF guard). An UNDECLARED host prompts per request (scoped to the host; "Allow always" persists). Private/loopback/metadata addresses are always refused. → HTTP
  • globalShortcut: prompts once per accelerator on first registration (a system-wide hotkey); register() returns false if denied.
  • openExternal: NO prompt — restricted to the manifest's openExternalAllowlist (http/https) + mailto:, enforced in Rust; file:// and off-list hosts are refused.
  • background: disclosed on the install screen (with each task's schedule) and prompts once on first fire (Allow once / always / Deny; a Trusted app skips it). Bounded to the manifest's backgroundTasks; switching it off in Permissions stops every task immediately. Background-capable apps' terminal sessions also survive ChatOSS quitting. → Background tasks
  • appInstall / preview / mcp: prompt-gated escalations — first use prompts (appInstall never auto-allows even for a Trusted app); mcp prompts per server host / command prefix.
  • apps.call / apps.registerApi: NO runtime prompts — the manifest's apiRequests/apiExports are disclosed and approved at install; the OS enforces both directions. → App-to-app APIs
  • data.set / tools.register: prompt quoting your manifest's "what"/"why". → Shared data & tools

Updates never widen permissions silently

A new .aip with the same id that adds ANY new capability shows the user the exact additions; denying keeps the installed version as-is. Write complete manifests from v1.

Prompts never hang forever

An unanswered permission prompt settles as denied after 5 minutes — a tool call never hangs forever. From inside your app, an unanswered prompt looks identical to a hang; the approvals API lets you show "waiting for your approval" instead.

Handle null/false results and rejections from every gated call gracefully — denial (or a turned-off capability) must not break the app.