OCP Embed profile — normative specification (v1.0-draft)

Extends Core. The widget: <script defer src=".../ocp.js" data-site="KEY">.

Topology

  • Loader ≤15 kB; chat renders in an IFRAME (origin isolation). Host↔iframe via postMessage with strict origin checks in BOTH directions; iframe↔Hub via WSS. The host page NEVER sees Hub traffic; the iframe NEVER accepts messages from anything but its declared Hub origin.
  • The loader MUST be served with SRI hashes; the widget MUST be cookie-less at T0 (visitor continuity via localStorage, rotating id).

Host ↔ widget messages (contract: embed.ts)

Host → widget (hostToWidgetSchema):

  • ocp.setContext { context } — zero-config fields the loader captured (url, title, meta description, selection ≤8 KB) plus anything the host merges in.
  • ocp.open / ocp.close — show/hide the panel.
  • ocp.identify { visitorJws } — signed visitor identity (account-bound, T1+).

Widget → host (widgetToHostSchema):

  • ocp.ready — the iframe is mounted and listening; the host MUST queue any earlier host→widget messages and flush them on receipt (the loader core does this).
  • ocp.state { phase } — idle | connecting | live | error.
  • ocp.event { name, data? } — host analytics hook. Message CONTENT never leaves the iframe unless the host opts in.

A host→widget message MUST target the exact Hub origin; a widget→host message MUST be dropped unless event.origin equals the Hub origin.

Host API (loader core: @schwaizer/ocp-embed)

ocp.setContext(…), ocp.open(), ocp.close(), ocp.identify(visitorJws), ocp.on(listener). Zero-config context (URL, title, meta description, user text selection) is captured with no host wiring (captureContext).

T0 protections

  • Site-key + origin allowlist (originAllowed: exact, *.suffix, or * for dev only); per-visitor/IP quotas; challenge before first session; tenant budgets with hard cutoffs.

Status

Loader core (context capture, origin-checked bridge, host API) is implemented and unit-tested in @schwaizer/ocp-embed. The browser entry (real document/window/iframe wiring, the ≤15 kB <script> payload, SRI, and the in-iframe chat UI holding the WSS leg) is the remaining browser-verified work.