Skip to content
Charming Docs
Esc
navigateopen⌘Jpreview
On this page

Browser features in your app

Camera, microphone, and location ask the browser for permission, and a chat host can block that prompt before it shows. A file picker asks for nothing, so it always works.

Which features work where

Feature Standalone app URL Rendered inline in a chat
Camera Works, once you claim the app Blocked: the chat host denies the permission prompt
Location Works, once you claim the app Blocked: the chat host denies the permission prompt
Microphone Works, once you claim the app Blocked: the chat host denies the permission prompt
File picker Works Works

Camera, location, and microphone. Claim the app first, linking it to a signed-in account, and the browser’s own permission prompt fires normally at the app’s own URL. Inside a chat, the app never reaches a context where the browser would show that prompt at all, so the same claimed app running inline inside Claude or ChatGPT can ask for the same permission and still get nothing back. Charming can’t reach through that boundary from inside the app. When it detects a claimed app asking for one of these features inside a chat, it shows its own message pointing at the app’s own URL automatically; no code in the app has to build that message.

File picker. Letting someone pick a file from their device isn’t one of the features Charming gates behind a permission. It needs no manifest import and no claimed app, so it works the same standalone and inside a chat. Read the picked file through Charming’s storage API; see Data storage for the rest of the file API.

Build so it works in every host

For camera, location, and microphone: call the browser API the normal way and handle denial like any web app would, a caught rejected promise or an empty result. Charming’s own chrome already carries the message for the part your code doesn’t control. An unclaimed app that asks for one of these shows “Claim this app to access…” before your code even runs; a claimed app blocked by a chat host shows a message linking to the app’s own URL. Don’t build a second version of either message: wire the normal permission-denied path and let Charming’s message show through it.

For a file picker: nothing extra. A file picker works the same everywhere.

Technical

Camera, location, and microphone are three entries in Charming’s browser-permission registry, each granted through the document’s Permissions-Policy and the app iframe’s allow= attribute. The file picker isn’t in that registry at all: it’s a plain HTML form control, not a permission.

Copy this prompt for your agent

Add a browser feature to my Charming app: camera, location, or
microphone. Read
https://charm.ing/docs/guides/browser-features.md first. Work out
which one I need from what I'm asking for, and build it so it falls
back cleanly when the permission is blocked. Ask me if it isn't
obvious which feature I want.
If you haven't built a Charming app before, read
https://charm.ing/docs/build-mcp.md (or build-http.md for the HTTP
API) for the app skeleton first.

How an agent performs this job

Declare buildy:browser/camera@1.0, buildy:browser/geolocation@1.0, or buildy:browser/microphone@1.0 in manifest.capabilities.imports when creating or updating the app, then call the matching Web API: navigator.mediaDevices.getUserMedia({ video: true }) for camera, navigator.geolocation.getCurrentPosition or watchPosition for location, navigator.mediaDevices.getUserMedia({ audio: true }) for microphone. Each import is claim-gated: the permission is grantable only once the app is claimed, on every surface, standalone or embedded. A file picker needs neither an import nor a claimed app: an <input type="file"> or a call to window.buildy.assets.upload(file) reading the selection works whether the app is claimed or not, standalone or embedded.

The contract

Camera, location, and microphone share one rule: an unclaimed app can declare the import but never gets a real grant, on any surface. A claimed app gets a real grant only outside a chat host’s own frame, at the app’s own standalone URL. That boundary belongs to the chat host, not to Charming or to the app: inside the host’s own frame the app never reaches a context where the browser would show the permission prompt, so no app code and no Charming setting can grant it there.

Was this page helpful?