跳转到内容

LiveKit Backend Binding

0. 规范语言

本文中的规范关键字(MUST / SHOULD / MAY 等)按 conformance/normative-language.md 解释;仅大写形式具规范约束力。

1. 范围

本附录定义 ak.realm.media_service.foci[].focus_kind = "livekit" 的 backend binding 细节。它 不替代 ../media-service-binding.md 与 ../call-state.md——参见 media-service-binding §2(multi-focus schema)、§3(token exchange 通用契约)、§5(focus selection)、§7(participant identity 校验)、§8.1(E2EE key injection 通用契约),及 call-state §4(ak.call.state 字段)。

声明 ak.profile.media_service_binding.livekit.v1 的部署 MUST 同时声明上游 ak.profile.media_service_binding.v1。本 binding 在 v1 周期内为 optional sub-profile;core conformance 不强制实现 LiveKit binding。

2. Token Claims

backend_token 是 LiveKit JWT(LiveKit Authentication),由 Arkret-side token issuer 用 LiveKit API Key/Secret 派生。Issuer MUST 注入下列 LiveKit standard claims:

LiveKit claimArkret 字段映射约束
issLiveKit API Keyissuer 标识;MUST 与 Realm 声明的 LiveKit deployment 一致
subparticipant_idLiveKit participant identity;MUST 与响应顶层 participant_id 一致
nbf / iattoken 签发时刻—
expexpires_at Unix epochMUST ≤ 600s after iat(media-service-binding §3 TTL 上限)
nameoptional display labelMUST NOT 携带可关联 actor 身份信息(与 ../media-service-binding.md §3 pairwise pseudonym 对齐);推荐留空或使用 participant_id
video.roombackend_room_idMUST 是 issuer 从 (realm_id, call_id, focus_id) 派生的稳定后端 room handle;MUST 与请求的 call_id 唯一绑定,但 MUST NOT 等于 raw call_id 或暴露 raw Realm/call id(建议 ak_call_<sha256(realm_id || 0x00 || call_id || 0x00 || focus_id)> 的短截断形式)
video.roomJointruejoin 权限
video.canPublishdesired_media.audio ∨ video ∨ screenissuer 按 capability 派生
video.canPublishSources[]["microphone","camera","screen_share"] 子集与 ak.call.screen_share 等子 capability 对齐
video.canSubscribetrue接收者权限
video.hiddenfalseArkret 不使用 LiveKit hidden participant
video.recorderfalserecording 走 Arkret blob pipeline(§5),不通过 LiveKit recorder claim

Issuer MUST NOT 注入:

  • metadata:LiveKit 允许任意 JSON 字符串,但携带 metadata 会绕过 Arkret participant_binding 真源。Arkret participant metadata MUST 通过 ak.call.state.roster_delta 写入。
  • video.canUpdateOwnMetadata:禁止 client 改写 LiveKit metadata。

3. Connect Handshake

客户端用 connect_url(典型 wss://livekit-<region>.example.com)与 backend_token 建立 WebSocket。LiveKit SDK 处理 SDP 协商、ICE、SFrame 协商等内部协议;Arkret 协议层不规定具体 wire 形态。

约束:

  • 客户端 SDK 接到 LiveKit ParticipantConnected 事件时,MUST 按 ../media-service-binding.md §7 做 participant identity 交叉校验:以 LiveKit participant.identity 为索引在 call_roster 已确认活跃集合 中找匹配项,验证 participant_binding 签名。未匹配或签名失败 → 拒绝建立媒体流,错误码 participant_id_unrecognised。
  • 客户端 MUST NOT 信任 LiveKit SDK 透传的 participant.name、metadata 或其它字段作为 actor 身份判定来源;唯一权威来源是 call_roster 已确认活跃集合 + participant_binding。

4. E2EE Key Injection

LiveKit 通过 SFrame 实现 frame-level E2EE。Arkret-LiveKit binding 的 key 注入按 ../media-service-binding.md §8.1 通用契约:

  1. 客户端 binding adapter 从 Arkret MLS exporter 为每个 sender 派生 key_bytes(label "ak.rtc-frame-key/v1",Context=canonical_json({realm_id, call_id, focus_id, epoch_id, participant_id, device_id}),KDF.Nh=32)。
  2. 调 LiveKit SDK 的 Room.setE2EEEnabled(true) 并通过 keyProvider 注入 key_bytes。
  3. MLS epoch 或 participant set 变化 → 调 keyProvider.setKey(keyBytes, keyIndex=<sender-bound-key-index>) 触发 LiveKit SFrame ratchet。keyIndex MUST 是当前 active (epoch_id, participant_id) 集合内无冲突的 adapter-local 映射;MUST NOT 仅用 epoch_id % 256。
  4. backend SDK 若试图通过 LiveKit Cloud 的 internal key distribution(如 LiveKit Cloud E2EE Token Service)注入 key,客户端 MUST 拒绝,错误码 e2ee_key_source_unauthorised。

media_service_decrypts=true 时(少数合规部署):客户端按 ../media-service-binding.md §8.2 完成三层校验,且 MUST 通过 Arkret-controlled keying path 把 key_bytes 提交给 LiveKit decryption oracle;不得使用 LiveKit Cloud 自动 key escrow。

5. Capability Mapping

Arkret desired_mediaLiveKit claim
audio: truevideo.canPublishSources 含 microphone
video: truevideo.canPublishSources 含 camera
screen: true + ak.call.screen_sharevideo.canPublishSources 含 screen_share
ak.call.recordrecording 走 §6 不签 LiveKit recorder claim
ak.call.moderatev1 不向参与者签发 video.roomAdmin=true:该后端权限含 remote mute,不能与已暂缓的强制静音分开授权。kick/ban 的 backend disconnect 由 Arkret 服务在 durable moderation 接纳后执行,不借参与者的 roomAdmin claim。

6. Recording

Arkret-LiveKit 部署 MAY 使用 LiveKit Egress 触发录制,但 Egress endpoint MUST 是 Arkret-side proxy;录制 artifact 流向严格按 ../call-state.md §5 与 ../media-service-binding.md §8.1:

任何 backend-generated recording 或 transcript 都意味着 media service 获得明文媒体。其启动与产物发布 MUST 以 media_service_decrypts=true 为前置,并完整通过 ../media-service-binding.md §8.2 的三层校验(policy component、plaintext_visible_services 授权、MLS governance binding 覆盖);缺任一条件 MUST fail closed。该明文可见事实 MUST 进入 governance binding 覆盖的成员可见 metadata。

  • Egress destination MUST 是 Arkret media service 的 authenticated upload endpoint;不得 LiveKit Cloud 直传 S3 / GCS。
  • 录制加密 key 来自 MLS exporter,label 固定为 ASCII 字符串 "ak.rtc-recording-key/v1"(与 SFrame "ak.rtc-frame-key/v1" 区分;Context=canonical_json({realm_id, call_id, focus_id, recording_id, media_service_id, recording_start_event_id}),KDF.Nh=32)。实现若复用 SFrame label、空 Context,或接受 LiveKit/KMS 自行生成的 recording key,MUST fail closed e2ee_key_source_unauthorised;LiveKit 不持久化明文。
  • 录制完成后通过 ak.call.state 发布 recording_transition.to="ready" 及 recording_transition.result content digest。
  • 客户端检测到 LiveKit Egress 配置指向非 Arkret endpoint → fail closed recording_artifact_pipeline_bypassed。
  • 转写(capture_kind="transcript")走同一 Egress / Arkret blob 路径,但加密 key label 固定为 "ak.rtc-transcript-key/v1"(Context=canonical_json({realm_id, call_id, focus_id, recording_id, media_service_id, transcript_start_event_id}),KDF.Nh=32),与录制 / SFrame label 区分;复用其它 label 或空 Context MUST fail closed e2ee_key_source_unauthorised;backend 自生成 transcript key 或绕过 Arkret artifact pipeline 才使用 transcription_artifact_pipeline_bypassed。转写完成后通过 ak.call.state 发布 transcript_transition.to="ready" 及独立 result。见 ../call-state.md §5.1。

7. Cascading

LiveKit Cloud SFU mesh 是 backend-internal 概念;Arkret 通过 foci[].cascade_group 仅向用户披露 “跨区域会议由 backend 内部级联,媒体延迟取决于 backend 配置”。协议层不规范 cascading wire 形态,也不暴露 LiveKit internal node 路由信息。

8. Failure Mode 映射

LiveKit 失败Arkret 错误码
LiveKit JWT signature invalid / expiredproof_invalid(token exchange 阶段)/ token_expired
video.room mismatchfocus_mismatch(issuer 派生出的 backend_room_id 与该 (realm_id, call_id, focus_id) 不一致时返回;否则客户端在 connect 阶段 fail closed)
LiveKit ConnectionState.Disconnected (auth)客户端 MUST 重新走 media-service-binding §3 token exchange,不得复用旧 token
LiveKit SFU not reachable按 ../media-service-binding.md §5 session_focus 持久化规则,不静默切 focus;暴露为 focus_unavailable_for_client

9. Participant Identity 验证

按 §3 / ../media-service-binding.md §7。LiveKit Participant.identity 即 Arkret participant_id,由 token issuer 在 media-service-binding §3 响应里给出,并已被 participant_binding 签名覆盖。客户端 MUST 在 connect / track-published 事件上完整校验该 binding;MUST NOT 信任 LiveKit Participant.metadata 字段中可能携带的任何身份字符串。

10. Conformance Vectors

实现声明 ak.profile.media_service_binding.livekit.v1 时,适用的 active normative vector 集合与数量以 vector-registry.json 中 active 的 ak.vector.media_binding.* profile 登记为唯一权威,本节不复述清单或计数。

LiveKit-specific vectors (JWT claim shape conformance、SFrame key injection cross-check) 在 v1 cycle 内非 normative;录制 exporter label 已由 ak.vector.media_binding.recording_exporter_label.v1 固定,任何 label/context 变更都必须开新 profile。

具体向量 fixture 与脚本由 ../../../artifacts/registry/vector-registry.json 编排。