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

External images

Load images from another site by listing its address; Charming allows only the addresses you list through the browser security policy that blocks everything else.

What your app can show

Photos, covers, and icons from other sites. By default, a Charming app’s UI can’t show an image hosted somewhere else: the browser’s security policy blocks it. Your agent lists the exact web addresses the images come from when it builds or updates the app, and images from those addresses render like any other image.

Technical

The app declares its allowed image origins in the manifest, and window.charming.images handles loading them in the UI.

Copy this prompt for your agent

Add external images to my Charming app. Read
https://charm.ing/docs/capabilities/external-images.md first. List
the exact HTTPS origins the image URLs come from in the app's
manifest, then load each image with window.charming.images.load()
so it renders in every embed.
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

Your agent lists the exact HTTPS origins the app’s images come from in manifest.permissions.browser["img-src"] (full origins, not bare hostnames) when it creates or updates the app. Charming appends each origin to the app’s img-src content security policy; any origin not on the list stays blocked. Wildcards, paths, credentials, loopback, private, and link-local targets aren’t allowed. A bad entry rejects create_app or update_app with invalid_manifest_schema.

permissions: {
  browser: {
    "img-src": ["https://images.unsplash.com", "https://covers.openlibrary.org"],
  },
}

In the UI, the agent renders each image through window.charming.images.load(url) or window.charming.images.proxy(url) rather than a raw <img src="https://…">, which fails silently under some embeds’ content security policy even when the origin is on the allowlist.

The contract

window.charming.images.load(url); // Promise<string> - resolves to a data: URL
window.charming.images.proxy(url); // string - same-origin proxy URL
  • window.charming.images.load(url) fetches the image through Charming’s server-side image proxy and resolves to a data: URL. It works in every embed, including Claude and ChatGPT, both of which inject their own outer content security policy that forbids a cross-origin <img src> but permits data:. Prefer this whenever the app may be embedded.
  • window.charming.images.proxy(url) returns the same-origin proxy URL. It works standalone, but not inside either Claude’s or ChatGPT’s inline embed.

Limits, access, and deletion

  • Allowlist enforcement. Both load and proxy check the permissions.browser["img-src"] allowlist on Charming’s server; the proxy refuses an undeclared host even when called directly. Neither bypasses the allowlist.
  • Undeclared origin. An image URL whose origin isn’t on the allowlist doesn’t render, and a raw <img> tag to it fails with no visible error: the image just stays broken.
  • Finding broken images. Blocked <img> requests and other content-security-policy violations surface as diag_report events on /app/<id>/activity, so you can find them without waiting for a user report.

Was this page helpful?