Widget Installation Boundary
How the Keva widget is published and installed, and its current availability
Keva builds the widget bundle from source on every deploy and serves it at
<your-keva-origin>/widget/v1/keva-widget.js. Settings > Widget shows the
exact snippet for each enabled installation; it is built from the address you
are reading Settings from, so a widget installed from a preview talks to that
preview.
Required authority
Current source uses two separate values:
- A non-secret
publicId, created for one selected workspace and an exact set of HTTPS origins. - A short-lived opaque session returned after an allowed browser origin exchanges the public ID. The SDK holds the token in memory and sends it only in an authorization header.
General REST credentials are not widget installation credentials and must not be placed in browser markup, public environment variables, URLs, analytics, or logs.
Dashboard state
In Settings > Widget, an authorized member can configure the tenant-wide widget settings and, once the schema exists, manage workspace-scoped public installations. A public installation requires:
- a descriptive name;
- the currently selected workspace;
- one to ten exact HTTPS origins with no path, query, fragment, wildcard, or user information.
Revoking an installation also revokes its active sessions locally. The widget is not available for customer use until database setup, deployment, and browser behavior are ready.
Installing the widget
Each enabled, unrevoked installation shows an install snippet. Copy it and
paste it before </body> on your site:
<script src="https://your-keva-origin/widget/v1/keva-widget.js"
data-public-id="keva_widget_…"
data-api-url="https://your-keva-origin"
async></script>data-api-url is required, and must be the origin the src points at. Without
it the SDK falls back to production, so a widget installed from a preview would
send its visitors' conversations to the wrong deployment.
The site's address must be in that installation's allowed origins, exactly: no path, query, fragment, or wildcard.
Strict content security policy
The widget does not require unsafe-inline. If the host page uses a CSP that
requires a style nonce, put the response's nonce on the embed script:
<script src="https://your-keva-origin/widget/v1/keva-widget.js"
data-public-id="keva_widget_…"
data-api-url="https://your-keva-origin"
nonce="YOUR_RESPONSE_NONCE"
async></script>The widget copies that nonce to its shadow stylesheet. The policy must allow the
Keva script and API origin, and its style-src must allow the same response
nonce. Keep the nonce generated per response; do not replace this contract with
unsafe-inline. The widget's launcher, panel, form, media controls, saved
brand colour, and bottom-left position are all rendered from that authorized
stylesheet.
When using window.Keva.init, pass the same response nonce as nonce in the
options object (see the SDK reference).
Integrity
The snippet has no integrity attribute. The bundle changes with every
release, so a content hash pinned in page markup would make every installed
site fail to load the widget until its owner edited their site — the opposite
of what an integrity attribute is for. Integrity is checked on Keva's side
instead: the published file is checked after each deploy and compared with
the bundle built from source.
The published path is /widget/v1/, and it is served with
Cache-Control: public, max-age=300. It is deliberately not private, no-store — a customer's visitors load it cross-origin, and no authority is
involved, so no authentication runs on it.
What must be ready before availability
- Apply the supervised widget identity migration and confirm it completes correctly.
- Check the published bundle after each deploy, as described above.
- Create one clearly labelled Keva-owned fixture installation.
- Test exact-origin success and missing, sibling, and unlisted-origin denial.
- Test chat, structured form, follow-up, stream, revocation, expiry, and idempotent retry behavior.
- Check tenant, workspace, session, conversation, message, attachment, and outbox records separately.
A successful build or HTTP request alone does not make the widget ready for customers.