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, then5200,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:
- Add the new origin alongside the old one in your manifest — do not swap-and-drop. Both origins coexist during the transition.
- Ship it as a new
versionthrough the portal (same slug, higher version). The old version keeps working on the old origin; the new version works on both. - 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.