Storage and delivery
Persist, serve, render, and observe generated media without accidental regeneration, retention, or payload exposure.
Crux keeps generation separate from storage. A completed operation returns a usable asset; your application decides whether and where to persist it.
Use the asset directly
Generated images and audio are ordinary canonical assets:
const picture = await openai.generateImage({
model: "gpt-image-2",
prompt: "A quiet canal at sunrise",
});
if (picture.image.type !== "data") {
throw new Error(
"This delivery path requires locally materialized image data.",
);
}
const imageBlob = new Blob([picture.image.data], {
type: picture.image.mediaType,
});Use the returned MIME type rather than assuming PNG, MP3, or WAV. Provider configuration and native output may select a different representation.
Persist explicitly
import { inMemoryAssetStore } from "@use-crux/core/storage";
const assets = inMemoryAssetStore();
const stored = await assets.put(picture.image);
console.log(stored.ref);No model operation accepts an AssetStore or writes implicitly. This keeps two
different failures separate:
generation failed → retry or route the model operation
storage failed → retry storage with the already generated assetDo not repeat a paid generation call merely because a database or object-store write failed.
Hydrate before reuse
An AssetRef is a bearer reference owned by its store, not model input:
import { z } from "zod";
import { prompt, type Asset } from "@use-crux/core";
const inspectPicture = prompt({
input: z.object({ picture: z.custom<Asset>() }),
messages: ({ input }) => [
{
role: "user",
content: [
{ type: "text", text: "Describe this image." },
{ type: "image", source: input.picture },
],
},
],
});
const reusable = await assets.get(stored.ref);
await openai.generate(inspectPicture, {
model: "gpt-4o",
input: { picture: reusable },
});Store namespaces organize references; they are not an authorization boundary. Authorize the caller before hydration and again before serving a stored asset.
Deliver images
In a browser, create an object URL for short-lived local display:
if (picture.image.type !== "data") {
throw new Error(
"This browser path requires locally materialized image data.",
);
}
const blob = new Blob([picture.image.data], {
type: picture.image.mediaType,
});
const objectUrl = URL.createObjectURL(blob);
try {
imageElement.src = objectUrl;
} finally {
URL.revokeObjectURL(objectUrl);
}For durable delivery, serve stored bytes through an authenticated application route or a deliberately scoped signed URL. Do not place bearer references or private provider file identifiers into public markup.
Image previews are provisional. Replace the previous preview for the same
outputIndex; do not accumulate complete preview replacements as though they
were frames.
Deliver audio
Encoded formats such as MP3 or WAV can usually become a Blob directly:
const audioBlob = new Blob([narration.audio.data], {
type: narration.audio.mediaType,
});Some Google speech output is headerless raw PCM. The MIME parameters describe the native sample format. A browser audio element generally cannot play those bytes directly. Explicitly:
- stream them to a PCM-capable playback path;
- wrap them in a correct container after reading the declared parameters; or
- transcode them in application infrastructure.
Crux does not synthesize a WAV header because an incorrect header would turn unknown framing into corrupted audio.
Own stream accumulation
Media streams retain replay data only for the operation lifetime. They are not an asset store:
const result = await google.streamSpeech(options);
const chunks: Uint8Array[] = [];
for await (const event of result.fullStream) {
if (event.type === "audio-delta") chunks.push(event.data);
}
const completed = await result.completion;
await assets.put(completed.audio);Persist the validated final asset rather than provisional chunks. If the
consumer leaves, call cancel() when the logical operation should stop; merely
returning from one iterator detaches that reader and does not cancel eager
execution.
Keep framework ownership clear
AI SDK and Convex integrations own their native attachment, thread, and file lifecycles. Crux translates supported model-visible content at the call boundary but does not duplicate framework uploads or autosave behavior.
When moving an asset between systems, make the transfer explicit and document:
- which system authorizes reads;
- which system owns deletion and retention;
- whether the destination receives bytes or a remote URL;
- whether the transfer changes privacy or provider residency.
Inspect media work
Project Index and Catalog show authored generateImage, streamImage,
transcribe, generateSpeech, streamSpeech, and ingestion operations. They
include modality, portable option facts, provider support, source navigation,
Safety relations, and deterministic lints.
Runs shows execution as one logical operation with a separate physical-attempt timeline:
- route selection and commitment;
- preview, delta, and final counts;
- byte totals and validated MIME types;
- first-public-event and total duration;
- cancellation, timeout, failure, or success;
- preview/final Safety provenance.
An exact safe media-operation definition identity links Runs back to Catalog. Crux does not guess by provider, model, name, or an untrusted attribute.
Privacy contract
Local capture and production telemetry never include media payloads. Media observability excludes:
- bytes and base64/data URLs;
AssetRefvalues and provider file IDs;- filenames and hashes;
- signed URL details;
- native stream events;
- thumbnails, waveform data, or playback.
Text and transcript capture follows its own capture policy. Enabling more text capture does not enable raw media capture.
Read Runs and delivery, Telemetry, and Privacy for the complete observability contract.
Derive instead of exposing
Ingestion can derive attributed text from media without putting media bodies into vector metadata. Retrieval returns structured source facts and does not hydrate the original media. Use Multimodal search when the query itself should be embedded as media.