Skip to content

Feature: Pre-flight network connectivity check + VOD avatar fallback for blocked enterprise/VPN environments #1

Description

@zoharbabin

Summary

Enterprise customers behind corporate VPNs or strict firewall policies frequently fail to connect to the avatar service — often silently, with no actionable feedback. This issue documents the root cause, the real-world impact observed, and a fully-designed SDK-level feature to detect the block early and fall back gracefully to a pre-recorded VOD avatar.


Background: What Is Happening Today

The failure mode

When a user on a corporate VPN or behind a network proxy visits an app using this SDK, the following sequence occurs:

  1. SDK loads and calls sdk.connect()
  2. Socket.IO begins with XHR long-polling as its first transport to https://conversation.avatar.us.kaltura.ai/socket.io/?client=...
  3. The corporate SSL-inspection proxy intercepts the HTTPS connection, re-terminates TLS, and proxies the request — but strips the Access-Control-Allow-Origin response header before it reaches the browser
  4. The browser enforces the missing CORS header and blocks the request
  5. Socket.IO never upgrades to WebSocket; Error 1001 fires immediately
  6. The SDK emits a generic connection-error event with [KalturaAvatar] ERROR: Connection error — xhr poll error
  7. The app shows a generic "Error loading avatar" spinner

Console errors observed by the customer:

Access to XMLHttpRequest at 'https://conversation.avatar.us.kaltura.ai/socket.io/?client=115767973963...'
from origin 'https://cdnapi-ev.kaltura.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Failed to load resource: net::ERR_FAILED  conversation.avatar...polling&t=PxqPKZY:1

[KalturaAvatar] ERROR: Connection error server error
[KalturaAvatar] ERROR: Connection error xhr poll error
[Avatar] Error: 1001 xhr poll error

A second, distinct failure mode (concurrent session)

Separately, when the avatar flow has a single-session concurrency limit: if another session already holds the flow, a second user gets Socket.IO signaling connected ("avatar connected") but WHEP media delivers zero RTP packets — resulting in a black/silent avatar with no error message at all. The user has no way to know whether to wait, refresh, or give up.


Who Is Affected

  • Any enterprise customer whose IT department runs a next-gen firewall, SSL inspection proxy (Zscaler, Palo Alto, Cisco Umbrella, Netskope, etc.), or zero-trust network gateway
  • This is a majority of large enterprise accounts — the exact customer segment this SDK is designed to serve
  • Customers working remotely via corporate VPN are particularly affected, as VPN split-tunneling decisions are made at the IT level without end-user control

Why This Is an SDK Problem, Not an App Problem

The SDK has full knowledge of:

  • The target WebSocket endpoint it needs to reach
  • The connection state and failure mode
  • The transport sequence (XHR poll → WebSocket upgrade)

Applications built on top of the SDK have none of this knowledge and cannot reliably distinguish a CORS-blocked network probe from a server error, a transient failure, or a misconfiguration. The correct place to implement detection and graceful degradation is inside the SDK, where it can be done once and benefit all integrations.


Proposed Solution

Part 1: Pre-flight connectivity probe

Before initiating the Socket.IO connection, the SDK runs a lightweight HTTP HEAD request to the avatar service's /health endpoint with a short timeout:

async function probeConnectivity(baseUrl, timeoutMs = 4000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    await fetch(baseUrl + '/health', {
      method: 'HEAD',
      signal: controller.signal,
      mode: 'cors'
    });
    return { blocked: false };
  } catch (e) {
    return {
      blocked: true,
      reason: e.name === 'AbortError' ? 'timeout' : 'cors-or-network'
    };
  } finally {
    clearTimeout(timer);
  }
}

Requirements on the server side: The /health endpoint must return HTTP 200 with correct Access-Control-Allow-Origin headers. This endpoint also acts as the canonical connectivity signal — if it is reachable, the full Socket.IO connection will be too.

If the probe fails, the SDK:

  1. Emits a new connection-blocked event (see API below) instead of attempting the doomed full connection
  2. Does not retry (retrying a CORS-blocked endpoint is pointless and wastes time)
  3. Optionally renders a VOD fallback if configured (see Part 2)

Part 2: VOD avatar fallback rendering

When a network block is detected, the SDK can render a pre-recorded avatar video in place of the live avatar, explaining the issue and providing IT resolution steps.

New constructor option:

interface NetworkFallbackConfig {
  /**
   * 'vod-entry'      — customer supplies their own Kaltura entry ID
   * 'kaltura-default' — SDK renders a standard Kaltura-hosted fallback video
   *                    for the given persona name
   * 'none'           — SDK only emits the event; app handles rendering
   */
  mode: 'vod-entry' | 'kaltura-default' | 'none';

  /** For mode='vod-entry': Kaltura entry ID of the customer's fallback video */
  entryId?: string;

  /** For mode='kaltura-default': avatar persona name to use for the fallback video */
  persona?: string;

  /** Kaltura partner ID (required for 'vod-entry' and 'kaltura-default') */
  partnerId?: number;

  /** URL to an IT configuration guide. Shown as overlay or burned into video.
   *  Defaults to Kaltura's official requirements page if omitted. */
  itLink?: string;
}

new KalturaAvatarSDK({
  clientId: '...',
  flowId: '...',
  container: '#avatar-pip',
  networkFallback: {
    mode: 'kaltura-default',
    persona: 'sophia',
    itLink: 'https://knowledge.kaltura.com/help/avatars-network-requirements-and-firewall-configuration'
  }
});

New event:

sdk.on('connection-blocked', (event: {
  reason: 'cors' | 'timeout' | 'network-error';
  itLink: string;
  probeUrl: string;
}) => {
  // Application can render its own UI when mode='none' or augment the built-in fallback
});

Behavior by mode:

Mode What SDK renders Customer effort
'kaltura-default' Standard Kaltura-branded fallback video for the named persona Zero — just set persona name
'vod-entry' Customer's own branded fallback video via Kaltura Player Customer pre-generates one video using the VOD Avatar Studio API
'none' Nothing — emits connection-blocked event only App builds custom fallback UI

Part 3: Infrastructure — pre-generated fallback video library

For mode: 'kaltura-default' to work, Kaltura needs to produce a set of standard fallback videos using the VOD Avatar Studio API, one per popular persona. Suggested script:

"Hi — it looks like your network is blocking the live avatar connection. To fix this, please ask your IT team to allow the Kaltura avatar domains. You can find the complete list of requirements at the link shown below. We look forward to connecting with you soon."

These videos would be hosted on Kaltura CDN and referenced in a lookup table inside the SDK bundle:

const FALLBACK_ENTRY_IDS = {
  sophia:    '1_xxxxxxx',
  ron:       '1_yyyyyyy',
  james:     '1_zzzzzzz',
  // ... remaining personas
};

Suggested priority order for first batch: the 10 most commonly used personas across active deployments.

Part 4: Concurrent-session detection (separate but related)

When sdk.connect() succeeds at the signaling level but WHEP media delivers zero RTP after a configurable timeout (suggested: 5 seconds), emit a distinct media-unavailable event rather than silently showing a black avatar:

sdk.on('media-unavailable', (event: {
  reason: 'no-rtp' | 'track-muted';
  sessionId: string;
}) => { });

Built-in message suggestion when this event fires: "The avatar is currently in use — please try refreshing in a moment."


Rollout / Who Does What

Task Owner Notes
Add /health CORS endpoint Backend / infra Prerequisite for probe
Implement pre-flight probe in SDK SDK team probeConnectivity() + connection-blocked event
Add networkFallback config + VOD renderer SDK team Uses existing Kaltura Player embed
Pre-generate fallback video library Kaltura content / VOD Avatar Studio One-time batch job via VOD Avatar API
Add media-unavailable event for black-video case SDK team Track muted state on WHEP stream
Update SDK documentation SDK team Document new config options and events

Reference: Network Requirements

The official Kaltura avatar network requirements page that the fallback should link to:
https://knowledge.kaltura.com/help/avatars-network-requirements-and-firewall-configuration

This URL should be the default value of itLink when the customer does not supply their own.


Acceptance Criteria

  • probeConnectivity() runs before sdk.connect() and completes in ≤ 4 seconds
  • CORS block / timeout causes connection-blocked event emission; full connection attempt is skipped
  • mode: 'kaltura-default' renders a Kaltura Player embed with the pre-generated persona video in the SDK container
  • mode: 'vod-entry' renders the customer-supplied entry in the SDK container
  • mode: 'none' emits only the event with no DOM changes
  • itLink is surfaced as a clickable overlay or caption on the fallback video
  • media-unavailable event fires when signaling succeeds but no RTP received within 5s
  • All new options are documented with TypeScript types and JSDoc
  • Existing behavior is unchanged when network probe succeeds (zero regression)
  • Works in all SDK transports: Socket SDK and Iframe SDK

Priority

High — this blocks enterprise sales. The affected customer profile (large enterprises on VPN) is the primary buyer of Kaltura Avatar deployments. The feature requires no breaking changes and is fully backwards-compatible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions