Skip to main content
preview.vpod.sh serves one snapshot as a working terminal, on one URL, built to be dropped into an <iframe> on somebody else’s page. Docs, changelogs and landing pages get a real machine in the page rather than a recorded gif.
The snapshot id is a path segment rather than a query parameter because it is the identity of the page: it is what caching, the origin-private storage key and any share link key off. Leave it out and you get whatever the registry calls latest.

It does not boot on its own

A cold preview pulls the ~25 MB engine plus a compressed snapshot. On a docs page with three embeds, booting on load is a quarter of a gigabyte spent on a visitor who scrolled past. So the default is a poster: the snapshot’s name, its real size, and a button. autoboot=1 opts out of that, for a page where the embed is the reason the visitor is there.

Parameters

Every parameter has a working default, and an unparsable value falls back to it rather than erroring. An embed that renders a stack trace because somebody typed theme=apprentise is worse than one that renders in the wrong theme: you see the wrong colours and fix it, where a visitor sees a broken product. key is the one exception. A key that does not resolve gets an error page rather than a default, because falling back to the keyless mode would quietly serve the public catalogue to somebody who asked for a private snapshot.

Embedding a private snapshot

A snapshot you built in the console is not in the public catalogue, so the embed needs a publishable keyvpod_pk_, the kind guarded by an origin allowlist rather than by secrecy:
The allowlist on that key becomes the preview’s Content-Security-Policy: frame-ancestors, so the browser is what refuses an embed from an origin you did not list. An origin that is not on the list gets an error page instead of the snapshot.
frame-ancestors wildcards are narrower than people expect: a * only ever replaces the first label. https://*.acme.example is a valid entry; https://preview-*.acme.example is not, and the console rejects it where you type it rather than emitting a header the browser silently drops. Ports are part of an origin and have no wildcard at all, so list each one.
A preview hands the snapshot bytes to the visitor’s browser. frame-ancestors stops another site from mounting your embed; it does not stop a person looking at your page from opening devtools. The allowlist covers “unlisted, and I don’t want it embedded elsewhere”. It does not cover “must not leak” — that case is served by not previewing it.

Networking inside an iframe

Guest networking needs SharedArrayBuffer, which needs the document to be cross-origin isolated. There are exactly two ways for an iframe to get that, and the preview arranges the first:
  • Document-Isolation-Policy: isolate-and-credentialless on the preview, so it is isolated whatever your page does. Chromium only, as of 2026-08.
  • Inheriting isolation from an embedder who serves COOP and COEP on their own page. That is what allow="cross-origin-isolated" on the <iframe> is for: harmless on an ordinary page, and the path that works in browsers without DIP if your page is already isolated.
So on an ordinary page in Firefox or Safari, the embed boots and runs commands but has no network, and wget fails rather than hanging. The preview reports which it got — as a badge in the corner, and as network: "sab" | "none" in the bridge’s ready message. If your own page is cross-origin isolated, it runs require-corp, which blocks every cross-origin subresource that has not opted in. The preview opts in with Cross-Origin-Resource-Policy: cross-origin on every response, so it loads.

Caching

The snapshot lands in origin-private storage on first boot, keyed by origin — the preview’s, not yours. One shared preview origin is what makes that worth something: a visitor who booted a preview on one site arrives warm at a preview on another. The ready message’s warm flag says which happened.

Driving it from your page

Everything above is configuration fixed at load. To run commands, switch themes or watch output while the embed is live, see the iframe bridge.