e30e75b5d4
Code Quality / Oxlint + Oxfmt (push) Waiting to run
Code Quality / Template Sync (push) Waiting to run
Code Quality / Build Changed Packages (push) Waiting to run
Code Quality / Test Changed Packages (push) Waiting to run
Deploy Expo Example / Deploy Production (push) Waiting to run
Deploy Ink Example / Deploy Production (push) Waiting to run
Python Tests / pytest (assistant-stream, 3.10) (push) Waiting to run
Python Tests / pytest (assistant-stream, 3.12) (push) Waiting to run
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Waiting to run
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Waiting to run
Deploy Shadcn Registry / Deploy Production (push) Waiting to run
Template Metrics / LOC + Bundle Size (push) Waiting to run
Changesets / Create Version PR (push) Has been cancelled
safe-content-frame
Render untrusted HTML, PDF, or arbitrary Blob content inside a sandboxed iframe whose origin is hashed per-content. The frame is hosted on scf.auiusercontent.com, a separate eTLD+1 from your app, so model-generated scripts cannot reach document.cookie, localStorage, or the parent window even if allow-scripts is set.
safe-content-frame is framework-agnostic vanilla JS. There is no React or DOM-framework dependency.
Installation
npm install safe-content-frame
Usage
import { SafeContentFrame } from "safe-content-frame";
const frame = new SafeContentFrame("my-app");
const container = document.getElementById("preview")!;
const rendered = await frame.renderHtml(modelGeneratedHtml, container);
await rendered.fullyLoadedPromiseWithTimeout(5000);
rendered.sendMessage({ type: "theme", value: "dark" });
// later
rendered.dispose();
The first argument to SafeContentFrame is your product identifier; it scopes the hashed origin so different products on the same domain do not collide.
Methods
| Method | Purpose |
|---|---|
renderHtml(html, container, opts?) |
Render an HTML string. |
renderRaw(content, mimeType, container) |
Render any MIME type from a string or Uint8Array. |
renderPdf(content, container) |
Render a PDF document. |
Each renderer returns a RenderedFrame:
| Property/Method | Description |
|---|---|
iframe |
The created <iframe> element. |
origin |
The hashed origin the frame was loaded from. |
sendMessage(data, transfer?) |
postMessage to the frame, scoped to its origin. |
fullyLoadedPromiseWithTimeout(ms) |
Resolves once the frame signals it is fully loaded; rejects on timeout. |
dispose() |
Remove the iframe from the DOM. |
Options
| Option | Default | Effect |
|---|---|---|
useShadowDom |
false |
Mount the iframe inside a closed shadow root. |
enableBrowserCaching |
false |
Derive the salt from the content hash so repeated renders reuse the same origin and HTTP cache. |
sandbox |
["allow-same-origin", "allow-scripts"] |
Extra iframe[sandbox] permissions to grant. The two defaults are always added. |
salt |
random per render | Override the salt explicitly (useful for tests or stable-origin embeds). |
Sub-paths
safe-content-frame: the mainSafeContentFrameclass above.safe-content-frame/shadow_dom: shadow-DOM rendering variant for embedding inside a custom element.
See the docs page for the threat model and a live demo.