> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vpod.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Embed a preview

> A live sandbox in an iframe on your own page, configured entirely by URL.

`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.

```html theme={null}
<iframe src="https://preview.vpod.sh/vsnap-base-512mb?theme=apprentice"
        allow="cross-origin-isolated"
        style="width:100%;height:420px;border:0"></iframe>
```

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

| Parameter      | Values                                                                               | Default          |                                                                                |
| :------------- | :----------------------------------------------------------------------------------- | :--------------- | :----------------------------------------------------------------------------- |
| `theme`        | `vpod`, `vpod-light`, `homebrew`, `apprentice`, `gruvbox`, `nord`, `solarized-light` | `vpod`           | Unknown names fall back.                                                       |
| `autoboot`     | `0`, `1`                                                                             | `0`              | Boot on load instead of showing the poster.                                    |
| `cmd`          | a shell command, URL-encoded                                                         | none             | Run once, after boot.                                                          |
| `banner`       | `0`, `1`                                                                             | `0`              | A snapshot and version line above the first prompt.                            |
| `readonly`     | `0`, `1`                                                                             | `0`              | Render output, ignore keystrokes.                                              |
| `net`          | `0`, `1`                                                                             | `1`              | Request guest networking.                                                      |
| `rows`, `cols` | `1`–`500`                                                                            | fit to container | Escape hatch. The default is a resize observer.                                |
| `key`          | a `vpod_pk_` key                                                                     | none             | Reaches your organisation's snapshots, and applies the key's origin allowlist. |

**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 key](/private-snapshots) — `vpod_pk_`, the kind guarded by an
origin allowlist rather than by secrecy:

```html theme={null}
<iframe src="https://preview.vpod.sh/snap_abc123?key=vpod_pk_..."
        allow="cross-origin-isolated"></iframe>
```

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.

<Warning>
  `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.
</Warning>

<Note>
  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.
</Note>

## 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](/preview/bridge) `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](/preview/bridge).
