host.content
host.content persists bytes for an app instance: uploads, recorded audio, generated images, exported video, a cloned-voice sample. Unlike host.kv (small JSON values), content is stored as real content under a per-instance workspace folder, so it survives reloads and rides the workspace share-cascade (everyone who can see the app can read its content).
Reach for it when a value is too big for KV, is binary, or needs to be handed to an <img>, <audio>, or <video> element.
At a glance
interface ContentNamespace {
put(
data: Blob | ArrayBuffer | ArrayBufferView | string,
opts?: { name?: string; mime?: string; scope?: ContentScope },
): Promise<{ id: string; key: string }>;
get(key: string, opts?: { scope?: ContentScope }): Promise<Blob | null>;
list(opts?: { scope?: ContentScope }): Promise<ContentItem[]>;
delete(key: string, opts?: { scope?: ContentScope }): Promise<void>;
pick(opts?: {
accept?: string[];
multi?: boolean;
kinds?: ('file' | 'url')[];
}): Promise<Array<{ kind: 'file' | 'url'; id?: string; name?: string; mime?: string; size?: number; url?: string }> | null>;
read(id: string): Promise<Blob | null>;
export(
data: Blob | ArrayBuffer | string,
opts?: { name?: string; mime?: string; visibility?: 'private' | 'public' | 'unlisted' },
): Promise<{ id: string; url?: string }>;
}
interface ContentItem { id: string; name: string; mime: string; size: number; createdAt: string }| Method | Use |
|---|---|
put(data, opts?) | Store bytes; returns { id, key }. |
get(key, opts?) | Read bytes back as a Blob (or null). |
list(opts?) | Enumerate this instance's stored items. |
delete(key, opts?) | Remove an item. |
pick(opts?) | Open the host's library picker; returns what the user chose. |
read(id) | Read the bytes of a picked library item. |
export(data, opts?) | Save app output into the user's library (MY LIBRARY). |
put accepts a Blob, ArrayBuffer, TypedArray, data: URI, or plain string. get always resolves a Blob. Wrap it in URL.createObjectURL() for a media element src.
Store and read back
await host.ready();
// `key` is the stable handle you persist (e.g. in host.kv).
const { id, key } = await host.content.put(recordingBlob, {
name: 'take-01.webm',
mime: 'audio/webm',
});
await host.kv.set('lastTake', key);
// ...after a reload:
const stored = await host.kv.get('lastTake');
const blob = stored && (await host.content.get(stored));
if (blob) {
audioEl.src = URL.createObjectURL(blob); // revokeObjectURL when done
}List and delete
const items = await host.content.list();
// → [{ id, name, mime, size, createdAt }, ...]
for (const it of items) {
if (it.mime.startsWith('image/')) await host.content.delete(it.id);
}Let the user pick from their library
pick opens the host's picker over the canvas and returns only what the user selects. The app never sees the rest of the library. Read a picked file's bytes with read(id) (the host grant-gates the access).
const picks = await host.content.pick({ accept: ['image/*'], multi: false });
if (picks?.length) {
const file = picks[0]; // { kind, id?, name?, mime?, size?, url? }
if (file.kind === 'file' && file.id) {
const blob = await host.content.read(file.id);
if (blob) imgEl.src = URL.createObjectURL(blob);
} else if (file.kind === 'url' && file.url) {
imgEl.src = file.url;
}
}Export to the user's library
export saves app output as a top-level item in MY LIBRARY (a shareable content row), distinct from the instance-scoped put.
const { id, url } = await host.content.export(renderedPngBlob, {
name: 'poster.png',
mime: 'image/png',
visibility: 'unlisted', // 'private' (default) | 'unlisted' | 'public'
});Scopes
Every method takes an optional scope. Today only instance (this app instance's own folder, the default) is implemented. widget (shared across instances of an app type) and user (the viewer's own content) are reserved and currently rejected with invalid_args. Do not depend on them yet.
await host.content.put(blob, { scope: 'instance' }); // explicit defaultNotes
- Content is per instance. For cross-instance shared data, publish a document with host.documents or call a shared host.fn.
- Stored content participates in the workspace share-cascade. Treat it as visible to every collaborator on the workspace, not as private to the author.
- Revoke object URLs (
URL.revokeObjectURL) when you swap a mediasrcto avoid leaking blobs.
Related
- host.kv: small JSON values
- host.documents: user-owned, version-tracked documents
- host.fn: server-side functions (shared, billable)
- Error codes:
invalid_args,quota_exceeded