Skip to content

UI blank / unusable over plain HTTP (non-secure context): unguarded crypto.subtle.digest crashes hydration (0.9.7 & 0.9.8) #2027

Description

@braghettos

Summary

The kagent UI is completely unusable when served over a non-secure context (plain HTTP on a raw IP / non-localhost host — e.g. a LoadBalancer or NodePort Service reached at http://<IP>:8080). The entire SPA renders a blank page, so the chat input never appears and no message can ever be sent. This reproduces on both 0.9.7 and 0.9.8.

Root cause: the client bundle calls crypto.subtle.digest(...) unguarded. crypto.subtle (SubtleCrypto) is a secure-context-only API — it is undefined outside HTTPS/localhost — so the call throws TypeError during client hydration, crashing the whole React tree.

This is distinct from the already-fixed crypto.randomUUID issue (#1868 / the generateId() feature-detect fallback is present and works correctly). This is a second, separate secure-context dependency.

Environment

  • kagent UI image cr.kagent.dev/kagent-dev/kagent/ui:0.9.7 and :0.9.8 (both reproduce)
  • Served over plain HTTP via a LoadBalancer at http://<LB-IP>:8080 (no TLS), Chrome
  • window.isSecureContext === false

Symptom / evidence

In the browser console at the chat URL over plain HTTP:

JSON.stringify({ s: isSecureContext, uuid: typeof crypto.randomUUID, subtle: typeof (crypto.subtle) })
// => {"s":false,"uuid":"undefined","subtle":"undefined"}
document.body.innerText.length   // => 0   (entire app blank)
document.querySelectorAll('textarea,input,button').length  // => 0

The offending code is in a client chunk (e.g. static/chunks/0pvx_*.js). De-minified shape:

async function c(e) {
  const t = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", u.encode(e))).subarray(0, 12);
  let r = "";
  for (let i = 0; i < t.length; i++) r += String.fromCharCode(t[i]);
  return btoa(r).replace(/\+/g, "-").replace(/\//g, "_") /* … base64url */;
}

It looks like a SHA-256 → base64url short-key/id helper (hashing joined args). It is not in kagent's own ui/src (I grepped the v0.9.8 tree) — it appears to come from a bundled dependency, which is why the 0.9.8 npm bump (#2016) didn't change it.

Server-side rendering succeeds (Node has crypto.subtle), so the backend logs show the UI server happily listing agents/sessions — only the browser crashes. Consequently a chat session is created but stays at 0 tasks, and the target agent's logs show only GET /.well-known/agent-card.json polls (no invoke ever dispatched). It looks like "chat hangs / does nothing", but the real cause is the blank/hydration crash upstream.

Steps to reproduce

  1. Deploy kagent and expose the UI over plain HTTP on a non-localhost host (e.g. Service type: LoadBalancer, access http://<EXTERNAL-IP>:8080).
  2. Open any agent chat page.
  3. Page is blank; no input renders; no message can be sent.

(Accessing the same deployment via https:// or http://localhost:8080 works, because those are secure contexts where crypto.subtle is defined.)

Impact

Any non-TLS, non-localhost deployment of the kagent UI is fully broken — a common setup for quick LoadBalancer/NodePort/IP access. The failure is silent (blank page, no console-visible error toast for users).

Suggested fix

  • Feature-detect crypto.subtle (like generateId() already does for crypto.randomUUID) and fall back to a pure-JS SHA-256 (or getRandomValues-based) implementation when it's unavailable; or
  • Identify the bundled dependency performing this crypto.subtle.digest and pin/replace it with a secure-context-safe one; and/or
  • At minimum, document that the UI requires a secure context (HTTPS or localhost) and surface a clear error instead of a blank page.

Happy to help bisect the dependency or test a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions