ConvergedHub · dev

Embed widget, on a page that is not ours

This page carries one script tag and one opaque id. Everything else the chat needs — which tenant it belongs to, where the engine is, which card origin to trust — it learns at runtime from the broker.

Look bottom-right. Open the launcher and ask something — try “my policy expires next month, how do I renew?”

What this page contains

<script
  src="https://d3urlaz5paj1ie.cloudfront.net/loader.js"
  data-experience-id="69ce4565ffa9b062611f847f"
  defer></script>

That is the whole integration. No tenant id, no API key, no endpoint. The id is opaque and carries no authority: the broker resolves it to a tenant, and the tenant of every turn comes from a verified session token, never from anything on this page. This page itself loads /loader.js relatively, because it is served from the widget origin and that origin's CSP permits script-src 'self' only.

What happens when you open it

  1. The loader (< 4 KB, no business logic) injects the real bundle, pinned by an SRI hash it computed at build time. A tampered bundle does not execute.
  2. The panel mounts in a closed shadow root, so this page's CSS cannot restyle it and this page's scripts cannot read the transcript.
  3. It mints a short-lived session token, then streams the answer over SSE.
  4. Cards render in a sandboxed cross-origin iframe from the card CDN — a separate origin on purpose, so a card cannot reach the session token.

This deployment

Widget
d3urlaz5paj1ie.cloudfront.net
Embed API
d3lmurrqh3i82g.cloudfront.net
Cards
d1e8j44uwma7ga.cloudfront.net
Tenant
tenant-acme (pooled) · profile policy-support
Model
livekit/openai/gpt-4.1 via LiveKit Inference

Before this page works

This origin must be allow-listed on the experience. The broker refuses a mint from any origin the experience does not list, and it returns 403 with no detail about which check failed — so a missing origin looks exactly like a bad id. Add https://d3urlaz5paj1ie.cloudfront.net to tenant-acme's allowedOrigins and re-seed.

Dev only. Publishing a page to the widget origin turns a pure-asset CDN into a site. The build pipeline deliberately publishes exactly two objects — assets/ then loader.js, in that order — so this file is not part of any build and must be uploaded deliberately. See demo/README.md.