For the complete documentation index, see llms.txt. This page is also available as Markdown.

Secure Mode

Secure mode ties chat sessions to a verified user in your app. Without it, the widget uses an anonymous browser-local sessionId. With it, the widget sends a verifiable username plus a cryptographic signature (HMAC-SHA256), and the server scopes the user's sessions and history to that identity, so prior messages can reappear across visits and devices.

When to use secure mode

  • You know who the end user is (logged-in app, Shopify admin, your dashboard's customer).

  • You want chat history to persist per-user across page loads, devices, or browsers.

  • You want to protect sessions from being impersonated by changing the username attribute.

If none of that matters, you don't need secure mode.

Keys and secrets

Every assistant has a widget API key with two parts:

Value
Prefix
Who sees it

Public key

yk_…

Embedded in HTML as the key attribute. Sent as X-Yedric-Key.

Secret

yks_…

Server-side only. Never should be sent the browser.

Get the pair from Agents → Widget → Code in the dashboard, or create/refresh via the API.

Domain restrictions

A widget key can be limited to a list of allowed domains. That check now fails closed: a request carrying no Origin or Referer header at all is rejected, where previously the check was skipped.

Browsers always send one of those headers, so ordinary embeds are unaffected. This does matter for anything calling the widget API outside a browser, such as a server-side script or an integration test, since those send no origin by default. Either send an Origin header that is on the list, or use a key with no domain restrictions for that caller. A key with an empty domain list stays unrestricted, so this only affects keys you have actually limited.

See Yedric API Keys and Agents for the full key management flow.

Signing the username

The signature is HMAC-SHA256(secret, username) encoded as lowercase hex.

The username is whatever stable identifier makes sense for your app: an email, a UUID, a Shopify shop UUID, a numeric user id, as long as the exact same string goes into the HMAC on the server and the username attribute on the widget.

Node.js (server)

PHP (server)

A real Shopify embed computes the signature from the shop UUID and passes the full config to the view:

Then the view renders:

Browser SubtleCrypto (edge functions, workers)

Useful when your "server" is a Cloudflare Worker, Deno Deploy function, or anywhere without node:crypto.

Never run this in the end-user's browser, because it needs the raw secret.

Wiring it to the widget

Render the signed bundle into HTML attributes:

Carrying a conversation across widget instances

Each <yedric-widget> is its own instance. A second widget (on another page, or in a separate iframe such as a fullscreen editor opened from a list view) loads the user's default thread on mount, not whatever conversation was active in the first instance. To carry the active conversation across, hand off its session id.

  1. Read the active session id on the source page:

  2. Mount the second instance with that id via the resume-session-id attribute. Because it's an attribute, you can server-render it into the iframe URL or set it before the element connects:

    The second instance resumes that exact thread instead of the user's default thread. The server still validates that the thread belongs to the verified user.

  3. Re-sync the first instance when the second one closes, so messages added in it appear:

getSessionId() and refresh() are also available on the element (document.querySelector('yedric-widget')). All three are secure-mode features: getSessionId() falls back to the anonymous session id outside secure mode, and refresh() is a no-op when there is no active secure session.

Common pitfalls

  • Never ship yks_… to the browser. Only the HMAC output (signature) goes into HTML. If your secret is ever exposed, rotate it via POST /api/agents/:id/key/refresh.

  • Username must match exactly. A trailing space, different capitalization, or a different field (id vs email) will produce a different HMAC and a 401 Invalid signature error. Log the exact string you pass to the HMAC and to the username attribute. It must be byte-for-byte identical.

  • One signature per username, not per session. You don't re-sign for every page or session; the signature is static until the username changes.

  • Storing the secret in version control. Prefer env vars or a secrets manager. Even in a private repo, committing yks_… makes rotation painful and broadens the blast radius of a leak.

  • Session prefix matters. session-id-prefix="preview" scopes secure sessions under a different prefix than the default widget. A user's "widget" history and "preview" history are separate, so don't mix them.

  • Key/secret pair mismatch. If you rotate the assistant's key but forget to redeploy the new secret on your signing server, every request will return 401 Invalid signature. Keep them in sync.

Last updated