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

Data storage

Every app comes with storage your agent can read and write: lists, settings, totals, even files.

What your app can store

Values. Lists, settings, totals, notes: the everyday things an app needs to remember. Whatever your app saves survives between visits and across code updates.

Files. Your app can keep images, PDFs, audio, video, and other files. See Limits for current caps.

Technical

Apps use env.storage for JSON values and env.assets or window.charming.assets for files. An agent can also call upload_asset.

Copy this prompt for your agent

Add persistent storage to my Charming app. Read
https://charm.ing/docs/capabilities/data-storage.md first. Work out
from the app what it should save, values or files; if it isn't
obvious, ask me.
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 declares charming:storage/[email protected] in the app’s manifest.capabilities.imports when it creates or updates the app (create_app / update_app over MCP, or POST /app / PUT /app/<id> over HTTP). Inside a route handler, it reads env.storage from the handler’s context and calls .get, .put, .delete, or .list. To read or change stored data without touching the app’s code, an agent (including a different one from whichever built it) calls query_app for a read-only operation or mutate_app for one that writes.

Files have two paths:

  • Agent upload. Call upload_asset. The tool reference defines its inputs and errors.
  • App storage. Declare charming:storage/[email protected], then use env.assets in a handler or window.charming.assets in the UI.

The contract

env.storage is only defined if the app declared the charming:storage/[email protected] import. Its shape:

env.storage.get(key); // Promise<unknown | undefined>
env.storage.put(key, value); // Promise<void> - value must be JSON-serializable
env.storage.delete(key); // Promise<boolean>
env.storage.list(); // Promise<string[]>

Storage is per-app and per-key, not a shared database across apps.

env.assets is only defined if the app declared the charming:storage/[email protected] import. Its shape:

env.assets.get(key); // Promise<{ bytes: Uint8Array; contentType: string } | undefined>
env.assets.put(key, bytes, { contentType }); // Promise<void>
env.assets.delete(key); // Promise<boolean>
env.assets.list(); // Promise<Array<{ key: string; contentType: string; sizeBytes: number }>>
env.assets.url(key); // string - same-origin serve URL

Use env.assets.url(key) or window.charming.assets.getUrl(key) for a time-limited signed Charming URL. Store the key, not the URL. Use window.charming.assets.load(key) when an uploaded image must render in a chat embed. See External files and CSP for URL lifetime and host behavior. A browser UI can also upload, list, and delete files directly:

const uploaded = await window.charming.assets.upload(file, { key: 'avatar.png' });
const files = await window.charming.assets.list();
await window.charming.assets.delete(uploaded.key);

Reading and listing require app read access. Uploading and deleting require app run access, so a read-only viewer cannot use them. The Browser runtime API defines the full method and error contract.

Limits, access, and deletion

  • Serialization. put stores JSON-compatible data: objects, arrays, strings, numbers, booleans, and null. A value that can’t be JSON-serialized (a circular reference, a BigInt) fails the write.
  • Key order. list() returns your keys but doesn’t guarantee an order; sort them yourself if order matters.
  • Caps and errors. See Limits and upload_asset.
  • Access. Storage belongs to the app. See Privacy and sharing.
  • Deletion. Deleting an app doesn’t purge key-value storage or secrets by default; delete_app takes a purge_storage: true flag to wipe those along with the app entry. Files are different: deleting an app always removes its stored files too, whether or not purge_storage is set.

Was this page helpful?