Widget
Overview
Our widget runtime is intentionally split into three parts so you can run it with confidence:
- Loader: the only script that touches your DOM. It creates the Core iframe, manages Views, and lets you observe every message before it moves.
- Core: a sandboxed iframe hosted on our CDN that handles targeting, orchestration, and view lifecycle decisions.
- View: a sandboxed iframe built from inline
srcdocmarkup that renders what your customers see.
Key guarantees for your team
- Your DOM stays yours. Only the Loader script can touch it, and the loader is short enough for a quick audit or even self-hosting.
- Every message is visible. We raise a cancelable
getuserfeedback:trafficevent containing{ direction, from, to, payload }so you can log, adjust, or veto any payload. - No surprise network traffic. Loader/View do not call
fetch; only the Core iframe performs network requests. Views are inlined, and any asset fetches come from your own page, not hidden calls inside the widget. - Origin-pin by default. Core will only run from
https://cdn.getuserfeedback.com; unexpected origins are blocked before a handshake completes.
How messages stay secure
- Signed envelope. Every message starts with the
getuserfeedback:prefix and includes version, session, role, and typed payload details. This prevents unrelatedpostMessagetraffic from entering the channel. - Challenge/response handshake. Loader and Core exchange one-time challenges. Any mismatch shuts the session down immediately.
- Ready gate. Messages from your host app queue until the handshake finishes. Nothing reaches Core early, even if you call APIs during page boot.
Isolation by design
- Dedicated iframes. Core and View run in their own iframes with sandbox policies; neither can reach out and mutate your page directly.
- View bootstrapping. Loader injects a short bootstrap script and a
viewIdinto thesrcdocpayload so that each View is uniquely tracked. - Minimal Loader code. Because Loader has the sensitive job of touching the DOM, it remains slim and well reviewed.
Network and content controls
- Strict Content Security Policy. Core ships with locked-down
frame-srcand nonce-basedscript-srcdirectives, keeping executable content predictable. - Sandboxed iframes. We enable only the flags necessary for surveys to run (
allow-scripts,allow-same-originwhen needed). Everything else stays disabled. - Inline-first rendering. View markup runs from inline HTML; when larger experiences are required we may use Blob URLs, still without additional cross-origin fetches.
- Host CSP-friendly Loader. The Loader only injects iframes; it does not perform network calls. Your host-page CSP can avoid granting
connect-srcfor the widget and instead focus onscript-src(loader.js) andframe-src/child-src(core iframe). The Core iframe still enforces its own CSP; host CSP does not override it.
Transparency tools for you
- Hook into
window.addEventListener('getuserfeedback:traffic', handler)to review or veto traffic in real time. - Use the observable events to feed your existing monitoring or governance pipelines.
- Self-host the Loader if your policies require serving all scripts from your own domain.
Threat mitigations at a glance
| Threat | What We Do |
|---|---|
| Unauthorized iframe source | Enforce strict origin pinning and handshake validation. |
| Cross-frame DOM access | Give DOM privileges only to the Loader; keep Core/View sandboxed. |
| Protocol spoofing | Require the getuserfeedback: envelope with session and role metadata. |
| Hidden behaviour | Surface every message via getuserfeedback:traffic before it is forwarded. |
| Replay or out-of-order traffic | Sequence messages through guarded state machines before delivery. |
| Inline script attacks | Apply tight CSP and avoid dynamic eval in Loader or Core. |
Looking ahead
- CSP reporting endpoints will give you deeper visibility into blocked resources.
- Subresource Integrity (SRI) is under evaluation for teams that prefer CDN hosting without self-hosting.
- Additional automated tests continue to stress malformed envelopes, replays, and network loss patterns.
Working with the SDK
createClientreturns a client and fetches the widget by default (setdisableAutoLoad: trueto opt out).- The SDK enqueues init on load; the loader forwards it to core and the instance is ready once registration settles.
- Use
client.open(flowId),client.prefetch(flowId), andclient.prerender(flowId)for flow lifecycle control. - For per-flow control,
client.flow(flowId)exposesopen,close, andsubscribeFlowState. - The global API mirrors this model (
window.__getuserfeedback.open(flowId),prerender(flowId),close()), with loader handling secure routing.
Consent defaults for widget configuration
- Consent defaults to granted (all scopes).
- When a snapshot exists, analytics and app events require
analytics.measurementto be granted.