跳转到内容

ak.profile.media_service_binding.v1

← Profiles

ak.profile.media_service_binding.v1

role

"server"

description

"Transport-agnostic media backend binding framework. Requires ak.realm.media_service to declare a multi-focus list with backend type discriminator; defines normative token exchange, session focus selection, participant binding, E2EE key injection via MLS-Exporter label ak.rtc-frame-key/v1, and recording artifact pipeline. Specific backends (livekit / arkret_native) declare an additional sub-profile. See zh/crypto-media/media-service-binding.md §2 / §3 / §5–§8 and zh/crypto-media/call-state.md §4."

enforcement_phases

[
  "conformance",
  "startup_claim_guard"
]

operation_requirements

[
  {
    "direction": "provide",
    "operation_id": "ak.self.call.media.exchange.issue_token.v1",
    "binding_kind": "http_json"
  }
]

depends_on

[
  "ak.profile.webrtc_media.v1"
]

required_profiles

[
  "ak.profile.webrtc_media.v1"
]

required_event_kinds

[
  "ak.realm.media_service",
  "ak.call.state"
]

rejected_event_kinds

[]

required_schemas

[
  "ak.schema.event.v1"
]

required_fixtures

[
  "privacy-security-fixture.json"
]

optional_extensions

[
  "focus_health_endpoint",
  "cascade_group",
  "recording_via_blob_pipeline"
]

feature_discovery

{
  "required": [
    "media_service_foci",
    "focus_token_exchange_ttl",
    "participant_binding_scheme",
    "session_focus_persistence",
    "mls_exporter_label_ak_rtc_frame_key_v1",
    "recording_exporter_label_ak_rtc_recording_key_v1"
  ],
  "unsupported_optional": "MUST fail closed with unknown_focus_type for unsupported foci[].focus_kind values; MUST NOT infer a focus when foci[] is missing"
}

additional_requirements

{
  "token_ttl_must": "Token issuer MUST cap backend_token / participant_binding expires_at at 600s after issuance; SHOULD use ≤ 300s.",
  "issuer_did_binding_must": "service_signature.kid and participant_binding.issuer_kid MUST resolve to a service DID declared in ak.realm.media_service.service_id of the current MLS epoch; otherwise reject with token_issuer_unauthorised.",
  "session_focus_must": "ak.call.state.session_focus is write-once per call; in-session focus migration is not supported in v1. Token issuer MUST reject focus_id != committed session_focus (focus_mismatch).",
  "participant_binding_must": "ak.call.state.roster_delta join participant_binding MUST be verified by both reducer (on event acceptance) and client (on backend ParticipantConnected event). Failure surfaces as participant_binding_invalid (reducer) or participant_id_unrecognised (client).",
  "e2ee_key_source_must": "Client binding adapter MUST derive frame keys from Arkret MLS-Exporter with label ak.rtc-frame-key/v1 (length=19 bytes, Context=canonical_json({realm_id, call_id, focus_id, epoch_id, participant_id, device_id}), KDF.Nh=32 bytes), where participant_id/device_id come from the verified call roster participant value and participant_binding. Empty Context, missing sender fields, or epoch-only binding MUST be rejected with e2ee_key_source_unauthorised. MUST NOT accept keys from backend cloud key escrow; reject with e2ee_key_source_unauthorised.",
  "recording_pipeline_must": "Backend-generated recording artifacts MUST strand through Arkret authenticated blob upload (media-and-blob.md) with recording_initiator_capability_ref. Direct backend-to-storage paths are forbidden (recording_artifact_pipeline_bypassed).",
  "recording_key_source_must": "Recording artifact encryption keys MUST derive from Arkret MLS-Exporter label ak.rtc-recording-key/v1 with Context=canonical_json({realm_id, call_id, focus_id, recording_id, media_service_id, recording_start_event_id}) and 32 output bytes. The SFrame label ak.rtc-frame-key/v1 and empty Context MUST be rejected for recording artifacts."
}

Source