Allowed Parent Origins

Your manifest's iframeSession.allowedParentOrigins is the list of page origins allowed to embed and drive your game. Every inbound bridge message is origin-checked against it before the payload is even parsed; a page origin not on the list cannot complete the SDK handshake, so the session never starts.

// bodylink.game.json
        {
          "iframeSession": {
            "channel": "postmessage",
            "allowedParentOrigins": [
              "http://localhost:7500",
              "http://127.0.0.1:7500",
              "http://localhost:5200",
              "http://127.0.0.1:5200",
              "https://bodylink.tv"
            ],
            "sandbox": ["allow-scripts"]
          }
        }
        

Why no wildcard

"*" is forbidden and the validation gate rejects it (bundle.parent_origin_wildcard). The origin allowlist is the load-bearing embedding control: your game receives live pose input and remote/pointer events over the bridge, and a wildcard would let any page on the web frame your bundle and drive that channel. Empty or whitespace origins are invalid too — the list must be explicit, exact origins (scheme + host + port).

List, and only list:

  • The production host origin(s) your game is meant to run under — for the BodyLink catalog today that is https://bodylink.tv (plus any legacy host origin still in service; the scaffold template carries both current and legacy entries).
  • Your local development origins — the loopback http://localhost:<port> / http://127.0.0.1:<port> pairs you actually develop against.

How local dev ports interact

The scaffolded template declares a set of loopback ports and wires them end-to-end: the Vite dev server runs on 7500 (preview on 7501), and the manifest lists the matching loopback origins. If you change a dev port, change it everywhere together — the dev script, the Vite config, and the manifest origins — or the hosted handshake will refuse the session.

The npm run dev:hosted harness (the self-hosted game-runner) is stricter than the browser check, on purpose:

  • Its serving origin becomes your iframe's parent origin, so it must be in your manifest's allowedParentOrigins.
  • With no port configured, the harness reads the local origins from your manifest and picks one by preference (7320, then 5200, 4173, 3000, 5179, else the first local origin listed).
  • An explicit BODYLINK_HARNESS_PORT=<port> must name a port your manifest allows — a typo'd port fails loud at startup rather than mysteriously at handshake time.
  • A manifest with no local origin at all cannot be served by the harness; add a loopback origin (this is why the template ships several).

Loopback ports are intentionally unrestricted at the runtime-shell layer for local development, but keeping your manifest and your tools in agreement is what makes local behavior match the declaration you actually ship.

When the host origin migrates

Bundles are immutable per <slug>@<version>, and the origin check reads the manifest inside the published bundle — so a host-origin migration (new domain, new hosting provider) means already-published bundles keep whatever list they shipped with. The playbook:

  1. Add the new origin alongside the old one in your manifest — do not swap-and-drop. Both origins coexist during the transition.
  2. Ship it as a new version through the portal (same slug, higher version). The old version keeps working on the old origin; the new version works on both.
  3. Drop the retired origin in a later release, once the old host origin is actually out of service.

Extra origins that are dead are only cruft; a missing origin is an outage for your game on that host. When in doubt during a migration, carry both.


See the Developer Guide §2.3 for the full iframeSession validity rules, and the Validation Reference for the bundle.parent_origins / bundle.parent_origin_wildcard / bundle.sandbox check codes.