Security & Compliance — @kaltura/intelligent-agents
Defaults are secure. Where a strict setting would hurt usability, the SDK keeps a usable default and gives you an option to tighten it, noted in this file.
- No runtime dependencies, no install scripts. The SDK is sourced directly from git (
src/), not a package registry, so there's zero transitive supply-chain surface. - All cryptography is delegated to the platform. The SDK never rolls its own crypto: TLS and DTLS-SRTP (the encryption protocol securing WebRTC audio and video) come from the host (Node
tls/crypto, browser WebCrypto/TLS). Run Node/OpenSSL in FIPS mode and a FIPS-validated OS/browser to operate in a FIPS-validated configuration (NIST SC-13). - Secrets never reach the client. The Admin Secret lives only server-side, in
Management. The browserKalturaAvatarSessiontakes only a short-lived, entitlement-ON user-session token.
Framework crosswalks map the SDK control-by-control to HIPAA, HITRUST, the OWASP LLM/Agentic Top 10, and avatar/deepfake/voice-clone law (EU AI Act Art. 50, NO FAKES, CA SB 1001, BIPA, C2PA). See the framework crosswalks section below, which also includes a copy-paste secure production baseline config.
Reporting a vulnerability
Email security@kaltura.com with details and a PoC if available. Please do not open a public issue for an undisclosed vulnerability. We acknowledge within a few business days and coordinate disclosure.
Table of contents
- AI-application controls
- KS guidance for agents
- Session type
- Token lifecycle
- Audit logging
- Transport security
- Browser hardening
- Isolation & multi-tenancy
- Supply-chain integrity
- Shared-responsibility control matrix
- FIPS mode
- Data residency
- Data handling and deletion
- Framework crosswalks
- HIPAA
- HITRUST CSF
- OWASP Top 10 for LLM Applications
- OWASP Agentic
- Avatar / digital-human / deepfake / voice-clone
- Secure production baseline
AI-application controls (OWASP LLM/Agentic; HIPAA technical safeguards)
Guardrails for the AI/agent layer. Most are opt-in: pass the callback or option. Framework mapping: framework crosswalks.
- Output handling (LLM05). Opt-in:
safeUrl,safeText,renderSafeLink(DOM-built, scheme-checked, neverinnerHTML). Call these yourself when rendering avatar text. On by default: inbound clamping of captions/segments. - Input guardrail (LLM01). Opt-in:
onBeforeSend(text, ctx)may transform or block a turn. It's a no-op until you pass it. - Agentic gate (LLM06 / ASI 01-02). Opt-in:
onAgentAction(action)and the declarativeagentActionspolicy. Always available: the read-onlycapabilitiessurface and thestop()kill switch. - Consumption valve (LLM10). Opt-in:
maxTurnsPerMinute(unlimited until you set it). - HIPAA technical safeguards. On by default:
idleTimeoutMsauto-logoff (164.312(a)(2)(iii)), 900000 ms (15 minutes); pass0to disable. Opt-in: the opaquesubjectIdunique-user-id (164.312(a)(2)(i)). Content-free turn audit events (164.312(b)) fire automatically once you wireonAuditEvent. - Avatar/deepfake. On by default: disclosure-before-speech with
synthetic/provenancedata, fired before the avatar's first words on every connect.getDisclosure()is queryable any time. Opt-in:requireDisclosureAck(also a biometric-consent gate) and an optionalconsentRefon voice/visual cloning.
KS (Kaltura Session) guidance for agents (AC-3 / AC-6 / IA-2)
A KS carries the privileges that decide what it can do. Full privilege reference: Kaltura API Authentication and Security.
| Token | Mint with | Privilege | Entitlement | Typical use |
|---|---|---|---|---|
| admin | sessions.createAdminToken({ userId }) |
disableentitlement |
OFF | Management-plane calls (provisioning, config), server-side |
| conversation / agent | sessions.createConversationToken({ configId, userId }) / createAgentToken({ agentId, userId }) |
setrole:PLAYBACK_BASE_ROLE, geniegpcid:<configId>, and agentid:<id> (agent tokens, or conversation tokens minted with agentId) |
ON | The token your server hands a live avatar/chat session. A user session (KS type 0) by default. See Session type |
| widget | sessions.createWidgetToken({widgetId}) |
server-derived | ON | Public, secret-free anonymous embed, safe to mint straight from the browser. See Session type |
For anything that reaches a browser, mint conversation/agent tokens (with a per-user userId) or widget tokens. Keep admin tokens server-side. createConversationToken/createAgentToken reject extraPrivileges that disable entitlement (entitlement_violation). Which of the two to use: Conversation token or agent token?.
Admin-type tokens (createAdminToken(), or sessionType: 'admin' on a conversation/agent token) are for your server. Your server decides at mint time what reaches a browser. The browser SDK can't check it: a real KS's privileges are encrypted, so inspectKs() reports disableEntitlement: null.
Session type
createConversationToken() and createAgentToken() mint a user session (KS type 0) by default: setrole:PLAYBACK_BASE_ROLE, entitlement ON. sessionType: 'admin' mints an admin-level session (KS type 2, no forced role), limited only by the role and privileges you set. createAdminToken() is always type 2 with disableentitlement, server-side only. Every type 2 mint needs a userId, which becomes the owner of anything the token creates. Minting options: Authentication.
What each token can reach:
| Token | Threads | OVP admin APIs |
|---|---|---|
Admin type (type 2): createAdminToken(), or sessionType: 'admin' |
Every user's threads | Yes, within the role and privileges you set |
Conversation/agent with userId (user session) |
Own threads: read, continue, list, delete | No |
Conversation/agent without userId |
One identity for all holders. Read, list and delete return 403 | No |
| Widget | Same as without userId |
No |
- Always pass a per-user
userIdwhen you mint a conversation or agent token. Any stable opaque id works. The user needs no registration. - Widget tokens can't separate users. Keep each
threadIdon your server, stored per user. To separate users in a browser, use the per-visitor path. - A
threadIdowned by another user is refused. The server answers HTTP 200 with anerrorsegment. The SDK throwsthread_access_denied: Refusals that arrive with HTTP 200. - Which methods take which token: Threads.
subjectId is for audit only. It is not an access boundary. Use userId on the token for that.
Token lifecycle (RFC 9700 OAuth 2.0 Security BCP; NIST AC family)
- Short-lived by default: browser-bound tokens (
conversation/agent) default to 30 minutes, admin to 1 hour. Short TTL is the primary revocation lever for a stateless KS (RFC 9700 §6.1). Override per call withttlSeconds, up to 86400 s (24 h) for browser-bound kinds and 7 days for admin; longer throwsttl_too_long. To refresh, your server mints a fresh token and the browser callssession.setToken(freshKs), with no reconnect. - Least privilege / binding (RFC 9700 §2.3, §4.10): tighten a token with the structured
restrictionsoption instead of hand-crafting privilege strings.{ role, actionsLimit, ipRestrict, uriRestrict, sessionGroupId }compiles to the matching Kaltura privileges (setrole/actionslimit/iprestrict/urirestrict/sessionid).roleneedssessionType: 'admin'. All restrictions are optional; none is set by default. Values and limits: Authentication. - Active revocation (RFC 9700 §5.2.1.1; SOC 2 CC6.2/CC6.3):
sessions.revoke(tokenOrKs)ends a leaked token within seconds (Kalturasession/end). Tokens that share arestrictions.sessionGroupIdare revoked together: revoking any member ends the whole family. Returns a receipt:{ revokedAt, partnerId, _meta }. - Vault/KMS (NIST IA-5): pass
getAdminSecret: () => fetchFromVault()to fetch the secret per-mint instead of holding it; it is never stored as an enumerable field.
Audit logging (NIST AU-2 / AU-3 / AU-12; OWASP Logging; SOC 2 CC7)
The SDK is an event emitter, not a logging framework. Pass onAuditEvent to Management and/or KalturaAvatarSession to receive discrete, already-redacted, structured AuditEvent objects, and route them into your SIEM. This is opt-in: no event fires until you pass this hook.
new Management({ partnerId, adminSecret, onAuditEvent: (e) => siem.write(e) });
Event catalog
| Event | Fires when |
|---|---|
token.mint |
A KS is minted (admin, conversation, agent, or widget) |
token.revoke |
sessions.revoke() ends a token |
token.refresh |
setToken() rotates a live session's token |
guard.reject |
A call is rejected for carrying the wrong token kind (e.g. an admin token where a conversation token was required) |
auth.fail |
A request returns HTTP 401 or 403. A failed conversations.stream request fires it for any error status |
session.connect |
KalturaAvatarSession finishes connecting |
session.disconnect |
KalturaAvatarSession disconnects |
session.timeout |
The idle timeout fires and auto-disconnects the session |
guardrail.block |
onBeforeSend or onAgentAction blocks a turn or action |
rate.limit |
maxTurnsPerMinute rejects a turn |
turn.user_captured |
The user's speech or text turn is captured |
turn.avatar_spoke |
The avatar starts speaking a turn |
turn.converse |
KalturaChatSession.sendText() completes or fails |
tool.invoke |
A client-side tool call is invoked (from user text or an agent action) |
tool.ack |
The app ACKs a client-side tool call via respondToTool() |
tool.spiral_detected |
The soft tool-call-spiral threshold trips for the current turn |
tool.spiral_hard_recovery |
The hard tool-call-spiral limit trips and the session force-reconnects |
agent.action.allow |
onAgentAction allows an agent-initiated action |
agent.action.deny |
onAgentAction denies an agent-initiated action |
clone.consent |
A consentRef is recorded on a voice/visual clone upload |
whep.release |
The WHEP (WebRTC-HTTP Egress Protocol) video resource fails to release cleanly on disconnect |
session.complete |
The session-completion signal (POST /thread/session_completed) fires on disconnect, tab-close, backgrounding, or bfcache freeze. It reuses the session's own conversation KS and mints no new credential. action carries the trigger reason (disconnect, pagehide, hidden_grace, suppressed:peers=N, …). See README.md § Ending a conversation cleanly |
Event fields
Every event carries this AU-3 content shape:
| Field | Type | Description |
|---|---|---|
ts |
string | ISO-8601 UTC timestamp |
type |
string | One of the event names in the catalog above |
severity |
string | info, warning, or error |
outcome |
string | success or fail |
requestId |
string | Correlation id, reused from the triggering call |
actor.partnerId |
string | Kaltura partner id |
actor.subjectId |
string | Opaque operator-supplied user id, if set. Audit only, not an access boundary |
actor.kind |
string | Token kind (admin / conversation / agent / widget) |
actor.sessionType |
string | user or admin. Not set for widget tokens |
actor.entitlementEnforced |
boolean | Whether entitlement was ON for this actor |
action |
string | The specific action taken |
scope |
string | The privilege string in effect, one-lined |
target |
string | Resource acted on, if any |
reason |
string | Failure reason, if any |
source |
string | Which SDK entry point emitted the event |
expiresAt |
number | Unix epoch seconds. Set on token.mint only |
_meta |
object | Provenance receipt |
Guarantees
- The raw KS is never included in an event. Only its kind and scope are.
- Free-text fields are stripped of CR/LF (CWE-117 log-injection guard).
- A throwing SIEM sink can never break a mint or a live turn. Event emission is crash-safe.
- This audit stream is distinct from the chatty debug
loggerand is never gated behind a debug level.
Transport security (NIST SC-8; OWASP WSS/TLS)
KalturaAvatarSessionrejects non-TLSconversationManagerUrl/srsBaseUrl(insecure_transport). The same rule covers everyManagementbase URL and the sessions'genieUrl.- A base URL that is empty, does not parse, uses a scheme other than
https/http, or carries a username or password throwsbad_requestat construction. - Loopback and private hosts (
localhost/127.0.0.1, RFC 1918 ranges, link-local, and their IPv6 equivalents) are allowed for dev, with a loud one-time warning. - Cleartext to a public host requires an explicit
allowInsecureTransport:true(dev/test only, never production). - Pass server-minted ephemeral TURN credentials (
turnCredentialsfrom appInit, RFC 7635). Without them the SDK uses its default TURN credentials and warns.
Browser hardening (OWASP ASVS / WebSocket CS)
-
Memory-only token: the SDK keeps the token in a non-enumerable instance field and drops it on
disconnect(). Do not put a conversation token inlocalStorage/sessionStorage(XSS-exfiltratable). Pass it directly and re-mint from your server on reload. -
No token in URLs: tokens travel only in the socket
authfield or theAuthorizationheader, never a query string (OWASP API2:2023). -
Prototype-pollution guard:
setDynamicPromptdata is scrubbed of__proto__/constructor/prototypebefore it touches the wire. -
CSP: the SDK uses no
eval/new Function. A working policy sets:connect-srcto your live-session control-plane host (conversationManagerUrl), the WHEP video-egress host (srsBaseUrl), and your TURN hostmedia-src blob:script-srcto your injected socket.io origin, pinned with SRI (see the README's "Injecting socket.io securely")
Also set
frame-ancestorson the embedding page. A mic-capable widget needs anti-clickjacking headers. -
AI disclosure (EU AI Act Art. 50): a
disclosureevent fires before the avatar's first words.requireDisclosureAckholds the avatar greeting untilacknowledgeDisclosure(). Note: ASR (Automatic Speech Recognition, the mic-to-text channel) connects before disclosure, so obtain consent beforeconnect()in IL/TX/WA.
Isolation & multi-tenancy (NIST SC-4 / AC-6(4))
No SDK module holds credential or tenant state at module scope. The admin secret, KS, and partnerId live only as (non-enumerable) instance fields. A single process can safely run N Management and M KalturaAvatarSession instances for different tenants, each with fully independent tokens, transports, and teardown. Nothing is shared or global.
Supply-chain integrity (SLSA / OpenSSF / EO 14028)
- Zero runtime dependencies, no install lifecycle scripts.
- No registry publish step. The SDK is consumed straight from its git tags (
src/, imported by path or served via jsDelivr's GitHub CDN). There is no npm package.
Shared-responsibility control matrix (NIST 800-53)
The SDK generates and protects the records and enforces the client-side controls below; the operator owns storage-side controls a client library cannot provide (retention, tamper-evidence, non-repudiation).
| Control | Family | SDK provides | Operator responsible |
|---|---|---|---|
| AC-3, AC-6, IA-2 | Access / least privilege | Two-token invariant; user-session tokens by default (Session type); structured restrictions |
Role/entitlement config in Kaltura |
| AC-12 | Session termination | Short TTLs; revoke(); disconnect() drops token + transports |
Session-timeout policy |
| AU-2, AU-3, AU-12 | Audit generation/content | Structured, redacted, correlated AuditEvents via onAuditEvent |
Wire the hook to a SIEM |
| AU-4, AU-9, AU-10, AU-11 | Audit storage/integrity/retention | None | Tamper-evident storage, non-repudiation, retention |
| SC-8 | Transmission confidentiality | https/wss enforced; cleartext rejected | TLS termination, cert management |
| SC-13 | Validated cryptography | Delegates to platform TLS/WebCrypto | Run Node/OS/browser in FIPS mode |
| SC-4 | Info in shared resources | Per-instance isolation; non-enumerable secrets | Process/tenant separation |
| SI-10 | Input validation | Inbound payload validation; prototype-pollution scrub | None |
| IR-6, SI-2 | Incident/flaw response | Security contact; coordinated disclosure | US-CERT/agency reporting timelines |
| GDPR Art. 17 | Right to erasure | threads.delete() (admin token, or a per-user token for its own threads; deletes are soft) and knowledge.deleteRecord() |
Emails already sent by a lifecycle rule's sendInsightEmail action are outside threads.delete()'s reach. Treat its recipients list as a data-processing decision. |
FIPS mode (how-to)
# Node with an OpenSSL FIPS provider:
node --enable-fips your-server.js
# or via OpenSSL 3 FIPS provider config (OPENSSL_CONF / fipsmodule.cnf)
In the browser, FIPS validation is a property of the OS/browser crypto module. Deploy on a FIPS-validated platform; the SDK adds no non-validated crypto.
Data residency (SC-7)
The SDK is a thin client. It contacts only the Kaltura endpoints you configure (agenticUrl/genieUrl/ovpUrl/messagingUrl/conversationManagerUrl/srsBaseUrl/turnServerUrl). There is no telemetry or hidden beacon. The optional ./experience/analytics module sends KAVA application events to analytics.kaltura.com, and only when your code calls it. Point every URL at your in-boundary (e.g. US-Gov) hosts to keep all data within your authorization boundary.
Session hosts come from appInit, not from your config. appInit returns the conversationManagerUrl, srsBaseUrl and turnServerUrl for your partner's region. Pass them to KalturaAvatarSession unchanged (new KalturaAvatarSession({ ...appInit, token: appInit.ks })). Do not hard-code them. If you omit conversationManagerUrl, the session falls back to the US host.
The region option covers the rest. It fills the four Management base URLs (agenticUrl, genieUrl, ovpUrl, messagingUrl) and the session genieUrl from one REGIONS entry. Management never falls back to another region: a service the region lacks throws region_unavailable instead of reaching a US host. region does not change the analytics host.
This residency guarantee covers the SDK's own configured endpoints only. A lifecycle rule's sendInsightEmail action sends thread-derived content to the recipients you list, and the URL-pinning above does not apply to it. Your code decides which recipients and regions are allowed. A deployment that must protect PHI/PII boundaries vets the recipients list and reviews residency before it uses this action.
Data handling and deletion
The SDK stores no conversation data itself. The one exception is Presenter session memory, which stays off unless you pass a storage object. How long Kaltura keeps data is a property of your account: ask your Kaltura Account Manager.
Every delete needs an admin token (a user-session token can also delete its own threads) and { confirmPermanent: true }.
| Data | Read or list | Delete |
|---|---|---|
| Threads | threads.list, get, transcript |
threads.delete(ids, ks, confirm). Deletes are soft: behavior and result shape |
| Messages | messages.list, get, report |
No method. The SDK cannot delete a single message |
| Knowledge records | knowledge.list, getRecord |
knowledge.deleteRecord(id, ks, confirm). It refuses while an intellect still lists the record, unless you pass force: true. knowledge.removeSource removes one source from a record |
| Agents, avatars, intellects | agents.list, avatars.list, intellects.list |
agents.delete, avatars.delete, intellects.delete. None cascades: deleting an agent keeps its avatar and intellect |
| Tools, skills, secrets, catalog items, lifecycle rules, insight settings, email templates | list or get on each |
delete on each |
To erase everything for one end user, list their threads with filter.userIdEquals, then pass the ids to threads.delete. The SDK has no single call for this and no way to see when a soft delete is purged.
Framework crosswalks
A client SDK can implement technical controls and generate the records, but it cannot sign a contract, retain logs, or authenticate the human user. Each table marks SDK (the library provides it) vs Operator (your duty, fed by the SDK's hooks/events).
HIPAA (45 CFR Part 164)
Gating item: Kaltura offers a Business Associate Agreement, covering Kaltura and its avatar/ASR/TTS/brain subprocessors, as required before any PHI flows. Contact your Kaltura Account Manager or Customer Success Manager to execute one.
| Safeguard (CFR) | SDK provides | Operator responsible |
|---|---|---|
| 164.312(e)(1)/(e)(2) Transmission security | https/wss enforced (insecure_transport); WebRTC media is DTLS-SRTP; crypto delegated to platform TLS |
TLS termination, cert management |
| 164.312(b) Audit controls | onAuditEvent (token + auth + guard lifecycle) and content-free PHI-exchange turn events (turn.user_captured, turn.avatar_spoke, session.timeout), never content |
Wire to SIEM; review (164.308(a)(1)(ii)(D)) |
| 164.312(a)(2)(iii) Automatic logoff | idleTimeoutMs (default ON, 900000 ms) → disconnect() + idleWarning + session.timeout audit |
Choose the timeout per care setting |
| 164.312(a)(2)(i) Unique user identification | Optional opaque subjectId threaded onto every AuditEvent (audit only, not an access boundary) |
Supply an opaque id (never the patient's name/PHI) |
| 164.312(a)(1) Access control | Two-token invariant; entitlement-ON user-session tokens that reach only the userId's own threads; least-privilege restrictions |
Role/entitlement config in Kaltura |
| 164.312(d) Person/entity authentication | Authenticates the session (KS), minted server-side under your control | Proof the patient before minting the conversation token; bind the token with a per-user opaque userId |
| 164.502(b) Minimum necessary | Redaction chokepoint; SDK persists no transcripts/captions/screenshots | Don't persist captions/screenshots beyond minimum necessary; apply retention |
| 164.402 Breach / safe harbor | PHI encrypted in transit + no token/PHI at rest → supports the encryption safe harbor for the SDK-controlled path | At-rest encryption; breach detection + 164.404/164.410 notification |
| 164.316(b)(2) Retention | Emits the records | Tamper-evident storage + 6-year retention |
PHI note: a patient may speak PHI to the avatar, so captions, transcripts, screenshots, and request-variable context (setDynamicPrompt's page_context) can carry PHI. The SDK surfaces these to your app but persists none of them. Treat them as PHI in your app.
HITRUST CSF (incl. the AI Security Assessment)
The SDK is an AI Application Provider component you can largely inherit in a HITRUST assessment. Kaltura's platform posture is the upstream inheritance source.
| HITRUST AI requirement | SDK provides | Operator / inherited |
|---|---|---|
| Encrypt traffic to/from the model | https/wss enforced; DTLS-SRTP | Platform/TLS |
| Restrict access to interact with the model | Two-token invariant; entitlement ON; per-user user sessions; revoke() |
Identity proofing |
| Log AI inputs/outputs (AI.PI.a) | Security + turn audit via onAuditEvent (content-free by default) |
SIEM storage/retention |
| Model rate limiting / DoS | Client-side maxTurnsPerMinute valve |
Authoritative server-side quota (inherited) |
| Humans can intervene (AI.NI.a, non-inheritable) | stop() / disconnect(), revoke(), barge-in (interrupt()), requireDisclosureAck, onAgentAction veto |
Wire at least one to a visible UI control |
| Output filtering / prompt-injection | safeUrl/safeText/renderSafeLink (output); onBeforeSend hook (input) |
Model-side guardrails (inherited); red-team the intellect (the agent's configured prompts, tools, and knowledge linkage) |
| AI supply chain | Zero deps, no install scripts, no registry publish step | Due-diligence review |
| AI transparency to end-user | Disclosure-before-speech + getDisclosure() |
Render it accessibly |
| Audit retention / tamper-evidence | Emits records | 6-year-or-longer tamper-evident storage |
Prompt-injection note: the prototype-pollution scrub on
setDynamicPrompt/inbound is object-injection defense, not prompt-injection defense.redact()is log-scoped, not an output content filter. Don't pass unsanitized end-user text intosetDynamicPromptor any request variable. Request variables stay on the thread, so see Dynamic Data Injection for what that means for a poisoned value. Instruction/data separation, source allow-listing, and model guardrails are operator/platform duties.
OWASP Top 10 for LLM Applications (2025)
| Item | Status |
|---|---|
| LLM01 Prompt Injection | onBeforeSend(text, ctx) input-filter hook (block/transform). Model-side detection is operator/platform. |
| LLM02 Sensitive Info Disclosure | Redaction of secrets in logs/audit. onBeforeSend lets you mask outbound PII. Never put secrets in any request variable, including the page_context payload setDynamicPrompt sends. |
| LLM03 Supply Chain | Zero runtime deps, no install scripts, no registry publish step, SRI on the injected socket.io. |
| LLM05 Improper Output Handling | safeUrl (scheme allow-list), safeText, renderSafeLink (DOM-built, never innerHTML), inbound clamping of captions/segments. Treat avatar text/GenUI (the SDK's on-screen widget layer: flashcards, forms, images rendered from agent output) as untrusted. |
| LLM06 Excessive Agency | onAgentAction gate + declarative agentActions policy + capabilities surface + requireDisclosureAck HITL. |
| LLM07 System-Prompt Leakage | Documented: never embed secrets/authz rules in the provisioned prompt or any request variable (setDynamicPrompt included). |
| LLM10 Unbounded Consumption | Client maxTurnsPerMinute valve (rate_limited); server quota is authoritative. |
| LLM04 / LLM08 / LLM09 | Operator: data/model poisoning, RAG/embedding ACLs, and misinformation/overreliance UX are out of a client SDK's control. |
Bounded-parser contract: nav/action commands parsed out of avatar text must be bounded allow-lists, never eval/dispatch of arbitrary strings. parseSlideNumber (integer-bounded, range-checked) is the template.
OWASP Agentic (Agentic Security Initiative / Top 10 for Agentic Apps)
The avatar is an agent (navigates, renders GenUI, captures leads, searches knowledge). The SDK is the client boundary where agent-initiated actions surface.
| Threat | Control |
|---|---|
| ASI 01 Goal Hijack / ASI 02 Tool Misuse | onAgentAction(action) chokepoint. Every agent-initiated action (navigate/render-genui/structured-data-form/…) passes through it before taking effect. Veto via false/throw. Operator (server-side api/code/csv tools): these fire server-to-server, outside the SDK's reach. Independently authorize each call against the caller's real session/permissions. Never treat model/system-prompt tool scoping, or a client-suppliable request_vars value, as an authorization claim. See Tools and Secrets § Tools. |
| ASI 03 Identity & Privilege Abuse | Scoped user-session token (per-user userId), entitlement ON, short TTL, revocable; least-privilege restrictions; agentActions policy (e.g. navigate:'off'). |
| ASI 06 Memory & Context Poisoning | Presenter session memory is bounded and operator-cleared via clearMemory(). Persisted memory is replayed context. The operator owns the storage choice. |
| ASI 08 Cascading Failures | Reconnect-window bound, media-recovery escalation, agent-stall watchdog, client rate valve. |
| ASI 09 Human-Agent Trust | Disclosure-before-speech + getDisclosure(); requireDisclosureAck. |
| T8 Repudiation | agent.action.allow/agent.action.deny audit events. |
| ASI 05 RCE | The SDK never evals agent output; nav uses a bounded parser. |
Avatar / digital-human / deepfake / voice-clone
| Obligation | Citation | Status |
|---|---|---|
| AI-interaction disclosure | EU AI Act Art. 50(1); CA SB 1001 (BOT Act); Utah AI Policy Act; Colorado AI Act | SDK: disclosure fires before first avatar speech + getDisclosure() queryable any time. Operator: render it accessibly (ARIA live region, not color-only; WCAG 2.1 SC 4.1.3). |
| Synthetic-media output marking | EU AI Act Art. 50(2); Recital 133; C2PA 2.x | SDK: disclosure.synthetic:true + provenance{generatedBy,voice,sessionId}. Operator/Platform: durable machine-readable marking (C2PA manifest) of the live media stream is server-side; stamp client-captured screenshots/recordings with an "AI-generated" assertion. |
| Deepfake disclosure (deployer) | EU AI Act Art. 50(4); Recital 134 | Operator (deployer) discloses manipulated content; SDK's disclosure supports it. |
| Bot identification | CA SB 1001 §17940 | SDK disclosure + persistent getDisclosure(). |
| Biometric notice + consent | BIPA 740 ILCS 14/15; TX CUBI §503.001; WA RCW 19.375 | The mic uplink may capture a "voiceprint." SDK: requireDisclosureAck can gate the mic until acknowledged. Operator: capture written-equivalent consent in IL/TX/WA before mic start. The avatar face/voice handling is operator/platform. |
| Voice/likeness clone consent | NO FAKES Act; TN ELVIS Act; CA AB 1836/2602; FTC impersonation rule | SDK: Management voice/visual provisioning accepts optional consentRef stored in data._consent on the returned catalog item. A clone.consent audit event is emitted. Operator: obtain and retain the source individual's consent. |
| Emotion / biometric categorisation notice | EU AI Act Art. 50(3) | Operator, if such features are enabled. |
Secure production baseline (CM-6)
Copy-paste hardened configuration for a regulated deployment:
// Server: Management
const mgmt = new Management({
partnerId, getAdminSecret: () => vault.fetch('kaltura-admin'), // or adminSecret
onAuditEvent: (e) => siem.write(e),
});
const token = await mgmt.sessions.createConversationToken({
configId, ttlSeconds: 1800, // short-lived (RFC 9700)
userId: opaqueUserId, // per-user identity: reaches only this user's threads
restrictions: { sessionGroupId: caseId }, // revocable family
});
// Browser: Experience
new KalturaAvatarSession({
token, conversationManagerUrl, srsBaseUrl, turnServerUrl, // all https/wss
turnCredentials, // ephemeral (RFC 7635), not the SDK default
allowInsecureTransport: false, // never true in production
requireDisclosureAck: true, // EU AI Act / biometric jurisdictions
idleTimeoutMs: 900000, // HIPAA auto-logoff
subjectId: opaqueUserId, // audit only: HIPAA unique-user-id (never PHI)
maxTurnsPerMinute: 30, // LLM10 valve
onBeforeSend: (t) => myGuardrail(t), // LLM01 input filter
onAgentAction: (a) => myPolicy(a), // LLM06 / Agentic action gate
onAuditEvent: (e) => siem.write(e),
});
Plus: pin the injected socket.io with SRI; set a strict CSP (connect-src your control-plane, WHEP and TURN hosts; media-src blob:; no inline script/unsafe-eval) and frame-ancestors on the embedding page; render the disclosure accessibly.