Apps
Ship a persistent, interactive surface (a dashboard, a form, a small tool) that renders in the workspace panel. A plugin can bundle one or more apps next to hooks, tools, routes, and skills.
What an app is
Unlike a tool (the model calls it) or a route (an external caller hits it), an app is UI the user opens. It lives under apps/<app>/. The directory name is the app name. A missing apps/ directory is skipped.
Write TSX/React under src/. The bundler maps react onto preact/compat. Apps load scripts from sibling dist/ under a strict Content-Security-Policy (script-src 'self', no inline scripts). The compile is off the assistant process hot path. Generated dist/ is not treated as tracked source.
How apps are addressed
plugins~<plugin-name>~<app-dir>
That maps to <workspace>/plugins/<plugin-name>/apps/<app-dir>/. The host reports origin as plugin:<plugin-name>, distinct from a workspace app.
Serving and isolation
Served only for an installed, enabled plugin (manifest present, no .disabled sentinel). Asset paths cannot leave the app directory.
Read-only over management
A plugin app arrives on install, updates on upgrade, and is removed on uninstall. Durable state lives in the plugin data/ directory, not in the app source. See Plugins.
Anatomy
my-plugin/
└── apps/
└── dashboard/
├── src/
│ ├── index.html
│ └── main.tsx
└── dist/ # generated
When to write an app
When the user needs a surface they look at, not an action the model invokes. Pair with a route when the UI needs a backend. Call those routes through window.cadence.fetch at /x/plugins/<name>/…, never the global fetch (the app origin is sandboxed).
Hub: Extensibility.