Docs menu
DocsBuild an app

The manifest (app.json)

Every app.json field: ids, capabilities, allowlists, background tasks, app-to-app APIs, and supersedes.

Every app declares itself in app.json at the zip root. A minimal manifest:

{
  "id": "com.you.myapp",
  "name": "My App",
  "version": "1.0.0",
  "description": "One honest sentence about what this app does.",
  "icon": "icon.svg",
  "capabilities": ["chatApi", "fileAccess"],
  "scopedDataKeys": ["myapp.state"],
  "dataStoreRequests": [],
  "toolsStoreRequests": []
}

Only id, name, version, and description are required; everything else has a default.

Fields

Field Required Rules
id yes Unique, reverse-DNS, lowercase letters/digits/dots/dashes only (it becomes a URL host). com.chatoss.* is reserved. Reinstalling the same id updates the app in place.
name yes Shown in the dock and Apps manager.
version yes Free-form string. Bump it when you ship an update.
description yes One line.
icon no An image file in the folder (PNG/JPEG/WebP/GIF/SVG), square, ≥64×64. Becomes the dock icon. Without it, a letter fallback is used.
entry no The HTML file to open. Defaults to "index.html".
capabilities no Array of capability names (see below). Undeclared capabilities = those APIs are refused at runtime. Declare ONLY what you use.
webviewAllowlist no Required WITH "webview": array of allowed domains (e.g. ["wikipedia.org", "khanacademy.org"]). The OS restricts every webview window to these hosts (subdomains included). Ignored without the "webview" capability.
httpAllowlist no Required WITH "hostHttp": array of allowed domains (e.g. ["api.example.com"]). Requests are restricted to these hosts (subdomains match) AND to public IPs — a Rust SSRF guard blocks localhost/private/cloud-metadata even if a listed domain resolves there, and redirects aren't followed. Ignored without "hostHttp".
shortcuts no Required WITH "globalShortcut": array of accelerators (e.g. ["CmdOrCtrl+Shift+K"]) the app may register as system-wide hotkeys. First registration of each prompts. Ignored without "globalShortcut".
openExternalAllowlist no With "openExternal": http/https hosts (subdomains match) the app may open in the default browser. Enforced in Rust (scheme http/https/mailto only — never file:// or a local path). Optional — mailto: links always work. Ignored without "openExternal".
backgroundTasks no Required WITH "background": array of { "id", "name"?, "description"?, "trigger" } the app may run while its window is closed (max 8). trigger is one of { "type": "manual" } · { "type": "interval", "minutes": n } (floored to 5) · { "type": "daily", "hour": 0-23, "minute": 0-59 } · { "type": "weekly", "weekday": 0-6, "hour", "minute" } (0 = Sunday). Ignored without "background".
scopedDataKeys no The private storage keys you use (documentation; private storage never prompts).
dataStoreRequests no Only to WRITE shared OS-wide data: array of { "key", "what", "why" } — shown verbatim in the user's approval prompt.
toolsStoreRequests no Only to publish a tool to ChatOSS's own agents: array of { "toolName", "description", "why" }. Rare.
terminalCommandPrefixes no With "terminal": command prefixes (first tokens, e.g. ["git", "ls", "grep"]) the app declares it will run. Declared prefixes are disclosed + approved at install and then run WITHOUT per-command prompts; undeclared prefixes still prompt. In headless/background runs, ONLY declared prefixes may run. Ignored without "terminal".
apiExports no Functions this app EXPORTS for other apps to call: array of { "name", "description", "params"?, "why" }. Names must be valid function names and unique. Exported APIs also become global tools named <appId>.<name> that any AI agent can call — even when this app is closed.
apiRequests no Other apps' APIs this app wants to CALL: array of { "appId", "methods"?, "minVersion"?, "why" }. Approved at install — zero runtime prompts. Without an entry for an app id, apps.call to it is refused. "appId": "*" is the WILDCARD: "may call the exported API of ANY installed app or built-in service" — the broadest grant in the system, disclosed unmistakably at install, so use it only for a genuine developer tool (an API explorer) that cannot name its targets. An exact appId entry WINS over the wildcard, so you can hold "*" and still pin one named app. "minVersion" (dotted-numeric, e.g. "1.2.0") is ENFORCED at call time against the target's manifest version — an older target is refused with an error naming both versions; it is rejected outright on a "*" entry (a pin against every app on the machine is meaningless). "methods": ["*"] (or omitting methods) = any method the target exports.
supersedes no Built-in dock apps this app REPLACES, e.g. ["code"]. While your app is installed and dock-visible, that built-in's dock pill and section slot route to your app instead. Legal ids: "chat", "kanban", "code", "models", "connect", "docs" ("apps" is deliberately NOT supersedable — it is the only surface that can uninstall you). 🔴 Reversible by construction: uninstalling or hiding your app restores the built-in immediately. It is disclosed at install as the highest-risk line on the screen ("This app replaces the built-in Code app"), so only declare it when replacing the built-in IS the point of the app.

The capability list

capabilities accepts:

"chatApi", "fileAccess", "fileDrop", "terminal", "webSearch", "webview", "notifications", "clipboardRead", "clipboardWrite", "hostHttp", "globalShortcut", "openExternal", "background", "documents", "boards", "sqlite", "appInstall", "preview", "proposeTask", "secrets", "mcp", "drive", "driveShared".

Each capability unlocks its own page in the API reference — start at the runtime bridge for the model, then read the pages for the capabilities you declare.

🔴 Declare ONLY the capabilities your code uses. A fresh install shows the user every capability you request before the app lands in their dock, and any capability can be turned off at any time in the app's Permissions panel — so an over-declaring app just looks scary and breaks later.