diff --git a/public/realtime/static/realtime-api-2024-05-21.yaml b/public/realtime/static/realtime-api-2024-05-21.yaml index a86e40fac6b..56d6e9e4928 100644 --- a/public/realtime/static/realtime-api-2024-05-21.yaml +++ b/public/realtime/static/realtime-api-2024-05-21.yaml @@ -684,6 +684,7 @@ paths: value: datachannels: - location: remote + waitForAck: true sessionId: "2a45361d5fd7cc14eface0587c276c94" dataChannelName: 1a037563-c35c-4bf6-a9ee-2b474cbb9a51 security: @@ -1163,6 +1164,15 @@ components: dataChannelName: type: string description: Given name for the data channel + waitForAck: + type: boolean + default: false + description: > + For remote DataChannels only. When true, the SFU holds delivery to this subscriber + until the subscriber sends its first message on this DataChannel (the acknowledgment). + The acknowledgment is consumed by the SFU and is not forwarded. It must arrive within + 15 seconds of creating the remote DataChannel; otherwise the SFU tears down the gated + channel. Ignored for local DataChannels. id: type: number description: Data channel id diff --git a/src/content/docs/realtime/sfu/datachannels.mdx b/src/content/docs/realtime/sfu/datachannels.mdx index fb486465f76..6642b41e748 100644 --- a/src/content/docs/realtime/sfu/datachannels.mdx +++ b/src/content/docs/realtime/sfu/datachannels.mdx @@ -27,6 +27,58 @@ DataChannels on Cloudflare Realtime can scale up to many subscribers per publish 3. Create a DataChannel by calling /datachannels/new with the location set to "remote" and the sessionId set to the sessionId of the publisher. 4. Use the DataChannel to send data from the publisher to the subscribers. +### Subscriber acknowledgment gate (waitForAck) + +By default, a subscriber starts receiving messages as soon as it pulls a remote DataChannel. To avoid losing the first messages before your subscriber is ready to handle them, you can opt in to an acknowledgment gate for each subscriber. + +- Set `waitForAck: true` when you create a remote DataChannel with the [HTTPS API](/realtime/sfu/https-api/). While the gate is closed, the SFU does not forward any messages to that subscriber on that DataChannel. +- After the subscriber's DataChannel opens, have the subscriber send any message on it (for example, the string `"ack"`). The first message opens the gate, and the SFU starts forwarding messages to that subscriber. +- The acknowledgment is consumed by the SFU. It is not forwarded to the publisher or to other subscribers, so the channel stays [unidirectional](#unidirectional-datachannels). +- The acknowledgment must reach the SFU within 15 seconds of creating the remote DataChannel. If it does not, the SFU tears down the gated channel and forwards no messages; create the remote DataChannel again to retry. +- `waitForAck` applies only to `location: "remote"` DataChannels and defaults to `false`, so existing behavior is unchanged. + +Create a remote DataChannel with the gate enabled by calling `POST /apps/{appId}/sessions/{sessionId}/datachannels/new` on the subscriber session: + +```json +{ + "dataChannels": [ + { + "location": "remote", + "sessionId": "", + "dataChannelName": "my-channel", + "waitForAck": true + } + ] +} +``` + +Then, on the subscriber, send the acknowledgment once the DataChannel is open: + +```ts +const resp = await fetch(`${API_BASE}/sessions/${subscriberId}/datachannels/new`, { + method: "POST", + headers, + body: JSON.stringify({ + dataChannels: [ + { + location: "remote", + sessionId: publisherId, + dataChannelName: "my-channel", + waitForAck: true, + }, + ], + }), +}).then((r) => r.json()); + +const dc = pc.createDataChannel("my-channel-subscribed", { + negotiated: true, + id: resp.dataChannels[0].id, +}); + +await waitForOpen(dc); +dc.send("ack"); // The first frame opens the gate; later frames are your application data. +``` + ### Unidirectional DataChannels Cloudflare Realtime SFU DataChannels are one way only. This means that you can only send data from the publisher to the subscribers. Subscribers cannot send data back to the publisher. While regular MediaStream WebRTC DataChannels are bidirectional, this introduces a problem for Cloudflare Realtime because the SFU does not know which session to send the data back to. This is especially problematic for scenarios where you have multiple subscribers and you want to send data from the publisher to all subscribers at scale, such as distributing game score updates to all players in a multiplayer game. diff --git a/src/content/docs/realtime/sfu/limits.mdx b/src/content/docs/realtime/sfu/limits.mdx index b6885914223..23686371b93 100644 --- a/src/content/docs/realtime/sfu/limits.mdx +++ b/src/content/docs/realtime/sfu/limits.mdx @@ -26,6 +26,7 @@ Understanding the limits and timeouts of Cloudflare Realtime is crucial for opti ## Inactivity Timeout * **Track Timeout**: Tracks will automatically timeout and be garbage collected after 30 seconds of inactivity, where inactivity is defined as no media packets being received by Cloudflare. This mechanism ensures efficient use of resources and session cleanliness across all Sessions that use a track. +* **DataChannel acknowledgment timeout**: When `waitForAck` is enabled on a remote DataChannel, the subscriber must send its first message (the acknowledgment) within 15 seconds of creating the channel. If it does not, the SFU tears down the gated channel and forwards no messages. Create the remote DataChannel again to retry. ## PeerConnection Requirements @@ -58,4 +59,3 @@ Cloudflare Realtime supports the following codecs: :::note For external 48kHz PCM support refer to the [WebSocket adapter](/realtime/sfu/media-transport-adapters/websocket-adapter/) ::: - diff --git a/src/content/release-notes/realtime.yaml b/src/content/release-notes/realtime.yaml index d41132436a0..87017d1538b 100644 --- a/src/content/release-notes/realtime.yaml +++ b/src/content/release-notes/realtime.yaml @@ -3,6 +3,15 @@ link: "/realtime/changelog/" productName: Realtime productLink: "/realtime/" entries: + - publish_date: "2026-06-10" + title: DataChannels subscriber acknowledgment gate (waitForAck) + description: |- + DataChannels now support an opt-in subscriber acknowledgment gate. When a subscriber pulls a remote DataChannel with `waitForAck: true`, the SFU holds delivery to that subscriber until it sends its first message (the acknowledgment). This avoids losing the first messages before the subscriber is ready to handle them. + + - Opt-in per subscriber; defaults to `false`, so existing behavior is unchanged. + - The acknowledgment is consumed by the SFU and is not forwarded, so the channel stays unidirectional. + - Send the acknowledgment within 15 seconds of creating the remote DataChannel. + - Docs: [DataChannels](/realtime/sfu/datachannels/), [Limits, timeouts and quotas](/realtime/sfu/limits/) - publish_date: "2025-11-21" title: WebSocket adapter video (JPEG) support description: |-