BrowserBox Documentation

06 Make it yours

Embedding and Origin Allowlisting

Customer guide · v19.3.1 · September 29, 2026

In this chapter

For the hosted BrowserBox SaaS service, the current REST API and embed reference is published at https://hyper-frame.art/api/. That reference covers both session-lifecycle REST endpoints and the canonical <hyper-frame> custom element using the current method and event names.

6.1 Server-side embedding allowlist #

For self-hosted BrowserBox embeds, the main server-side allowlist is:

export ALLOWED_EMBEDDING_ORIGINS="https://app.example.com https://*.customer.example:*"

This controls which parent pages may embed BrowserBox.

Supported patterns include:

  • exact origins, for example https://app.example.com

  • subdomain wildcards, for example https://*.customer.example

  • trailing port wildcards, for example https://app.example.com:*

  • both together, for example https://*.customer.example:*

Important boundary:

  • ALLOWED_EMBEDDING_ORIGINS controls which parent pages may embed BrowserBox.

  • It does not control where the remote browser may browse.

  • Browsing targets are controlled by BrowserBox policy.

6.2 Canonical embedder controls #

For new embeds, prefer explicit webview controls over encoding everything into the login URL:

  • embedder-origin="https://app.example.com"

  • ui-visible="false"

  • allow-user-toggle-ui="false"

  • first-load-cleanse="https://example.com" when the embedder should remove inherited tabs and open a known first page

Minimal example:

<hyper-frame
  login-link="https://localhost:8888/login?token=..."
  embedder-origin="https://app.example.com"
  ui-visible="false"
  allow-user-toggle-ui="false"
></hyper-frame>

6.3 First-load tab cleansing #

Some hosted or demo embedders need the first visible session state to be deterministic even when the remote browser already has tabs. The first-load-cleanse attribute runs once per login-link: it closes the current tab set when the webview API becomes ready, then optionally opens one replacement tab.

<hyper-frame
  login-link="https://localhost:8888/login?token=..."
  first-load-cleanse="https://duckduckgo.com"
></hyper-frame>

The value is intentional:

  • a non-empty URL closes existing tabs, then opens that URL

  • an empty value, such as first-load-cleanse="", closes existing tabs and opens no replacement tab

  • omitting the attribute leaves the existing session tabs alone

The public firstLoadCleanse(url) method has the same semantics for explicit embedder control. Passing a non-empty url opens that URL after the cleanse; passing an empty string performs the delete-only cleanse.

6.4 Embedder media permissions #

Embedders that do not want BrowserBox to request microphone, camera, or display-capture access can set:

<hyper-frame
  login-link="https://localhost:8888/login?token=..."
  embedder-origin="https://app.example.com"
  media-permissions="none"
></hyper-frame>

The current values are:

Value Meaning
default BrowserBox uses its normal media behavior. This is also the fallback for missing, empty, or unknown values.
none The embedder does not want BrowserBox to use microphone, camera, or display capture for this embedded session. The webview removes those features from the iframe allow attribute and syncs the setting into the BrowserBox client. Safari-family clients use this setting to skip the BrowserBox media explainer and the related preflight media request.

This is a coarse embedder media control. It is not a substitute for BrowserBox browsing policy, and it does not currently expose separate per-device settings such as audio-only or camera-only controls.

6.5 Embedder session unload warning #

BrowserBox shows a beforeunload warning (“you are about to leave your remote browser”) on the BrowserBox wrapper page itself when the end-user has interacted with the session. For embedders that own the outer page and do not want BrowserBox to interrupt the user’s navigation of their own application, this can be suppressed:

<hyper-frame
  login-link="https://localhost:8888/login?token=..."
  embedder-origin="https://app.example.com"
  session-unload-warning="none"
></hyper-frame>
Value Meaning
default BrowserBox installs its wrapper-page beforeunload warning. This is also the fallback for missing, empty, or unknown values.
none BrowserBox’s wrapper-page beforeunload handler still runs, but is lazily evaluated at fire time: when the embedder’s session-unload-warning is none, the handler refuses to block the unload. This design is race-free; the handler checks the current setting each time the user tries to navigate, so the embedder value does not need to win a setup-time race against the BrowserBox script load.

This setting does not affect the BrowserBox modal for beforeunload prompts originating from the remote page being browsed. Those are governed by the beforeunload-behavior attribute below.

6.6 Remote page beforeunload behavior #

When a remote page being browsed inside BrowserBox attempts to block navigation via a beforeunload dialog, BrowserBox normally intercepts this and shows a modal to the user. For automated or programmatic use cases where navigation should never be blocked, this can be configured to automatically allow or block the navigation without showing a UI:

<hyper-frame
  login-link="..."
  beforeunload-behavior="leave"
></hyper-frame>
Value Meaning
default BrowserBox shows its standard modal, allowing the user (or the embedder via the Modal API) to decide.
leave BrowserBox automatically clicks "Leave" (proceed with navigation) and does not show the modal UI.
remain BrowserBox automatically clicks "Remain" (stay on page) and does not show the modal UI.

This design uses the same JiT-evaluation pattern as the session unload warning, making it robust against initialization races.

BrowserBox modals are exposed to embedders using the same event convention as the rest of the webview surface: the BrowserBox frame sends a dashed message type with a data payload, and <hyper-frame> dispatches a CustomEvent whose detail is that payload. Where dotted aliases exist, they carry the same detail object.

The modal API should not require customers to learn separate methods for alert, confirm, prompt, file chooser, auth, or notice modals. Each modal is represented as data with an advertised list of actions. Customers either choose one of those actions or call the convenience dismissal method.

webview.addEventListener('modal-opened', event => {
  const modal = event.detail;
});

webview.addEventListener('modal-updated', event => {
  const modal = event.detail;
});

webview.addEventListener('modal-closed', event => {
  const result = event.detail;
});

For compatibility with the webview’s existing alias style, the implementation may also emit dotted aliases:

  • modal.opened for modal-opened

  • modal.updated for modal-updated

  • modal.closed for modal-closed

A modal detail object should be safe to expose to the embedding page:

{
  id: "modal-42",
  type: "notice",
  title: "Safari Permissions",
  message: "...",
  url: null,
  link: {
    title: "View Bug",
    href: "https://bugs.webkit.org/show_bug.cgi?id=189503",
    target: "_blank"
  },
  actions: [
    { id: "close", label: "OK", role: "close" }
  ],
  defaultAction: "close",
  dismissAction: "close",
  requiresInput: false
}

The public modal methods are:

const modal = await webview.getCurrentModal();

await webview.respondToModal({
  id: modal.id,
  action: modal.dismissAction
});

await webview.dismissModal();

The low-level API equivalents use the same method names:

await webview.callApi('getCurrentModal');
await webview.callApi('respondToModal', { id, action, value });
await webview.callApi('dismissModal', { id });

dismissModal() activates the modal’s safest advertised dismissal action. The selection order is:

  1. use the action whose id is dismissAction, when present;

  2. otherwise prefer an action with role cancel;

  3. otherwise prefer role close;

  4. otherwise prefer role deny;

  5. otherwise, if there is only one action, use that action;

  6. otherwise reject with an ambiguous-action error.

Prompt-like modals can accept a value through respondToModal:

await webview.respondToModal({
  id: "modal-43",
  action: "ok",
  value: "customer supplied text"
});

Unsupported or sensitive operations are not required to be advertised as actions. dismissModal() still works whenever the modal has a cancel, close, deny, or single remaining action.

The webview policy capability names are modals.read for modal lifecycle events and modals.respond for respondToModal() and dismissModal(). Both are enabled by default for the canonical webview policy.

BrowserBox · Published by DOSAYGOHappy browsing.