# End-to-end encryption (Beta) (/en/realtime-media/broadcast-streaming/build/secure-and-protect-channels/end-to-end-encryption)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

**Supported platform:** Web.

<_PlatformTabsGroup groupMode="structured" canonicalPlatform="web" platforms="[&#x22;web&#x22;]" showTabs="false">
  <_PlatformPanel platform="web">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />

    To enhance data security, Agora provides media stream encryption and end-to-end encryption. End-to-end encryption encrypts the media stream on the sending device and decrypts it on the receiving device, ensuring that third parties cannot access the data during transmission.

    The end-to-end encryption is based on the [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/) standard. However, since WebRTC Encoded Transform is still maturing and may have compatibility or performance issues, there are some restrictions on its use:

    * Only VP8 video and Opus audio are supported.
    * It supports publishing and receiving audio and video streams between Web clients only, with no cross-platform compatibility or bypass streaming services.
    * The SEI information transmission feature cannot be enabled due to conflicts with browser components.

    ## Understand the tech [#understand-the-tech]

    The end-to-end encryption technology is based on the following key concepts:

    * **WebRTC Encoded Transform**: A technology for encoding transformations in WebRTC. It provides a read and write interface for encoded WebRTC data, enabling developers to convert encoding formats, adjust bit rates, or apply filters.
    * **WebRTC Encoded Transform processing pipeline**: A sequence of operations used to process WebRTC encoded data.
    * **RTCRtpSender and RTCRtpReceiver**: Standard WebRTC components. `RTCRtpSender` handles sending encoded data, while `RTCRtpReceiver` is responsible for receiving it. Together, they form the WebRTC Encoded Transform processing pipeline.
    * **RTCRtpTransceiver**: A WebRTC API for managing both audio and video streams. `RTCRtpSender` and `RTCRtpReceiver` are accessible through the `.sender` and `.receiver` properties of the `RTCRtpTransceiver` object.

    For each audio or video track, the Web SDK offers the `getRTCRtpTransceiver` method to retrieve the `RTCRtpTransceiver`. After obtaining `RTCRtpSender` and `RTCRtpReceiver` from `RTCRtpTransceiver`, set up the WebRTC Encoded Transform processing pipeline to encrypt and decrypt audio or video data.

    ## Prerequisites [#prerequisites]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * A supported browser:
      * Chrome (desktop and mobile): version 87 or later
      * Safari (desktop and mobile): version 15.4 or later
    * Contact [support@agora.io](mailto\:support@agora.io) to enable end-to-end encryption.

    ## Implement end-to-end encryption [#implement-end-to-end-encryption]

    This section shows you how to implement video encryption on the sender side and decryption on the receiver side using Chrome as an example. The steps for Safari are slightly different.

    The example uses the AES encryption algorithm from the Web Crypto API. To implement a custom encryption and decryption algorithm, use `WebAssembly`.

    1. **Publish the encrypted media stream**

       To establish a WebRTC Encoded Transform processing pipeline on the publishing end:

       1. After publishing the local video track, call the `getRTCRtpTransceiver` method provided by the SDK to obtain the local video track's `RTCRtpTransceiver` instance. Then, retrieve the `RTCRtpSender` instance through the `sender` property.

       2. Use the browser's native Streams API to encrypt the video stream.

       Refer to the following sample code:

       ```javascript
       async function publish() {
         // Publish the local video track
         await client.publish([localVideoTrack]);

         // Get the RTCRtpTransceiver instance for the local video track
         const transceiver = localVideoTrack.getRTCRtpTransceiver();
         if (!transceiver || !transceiver.sender) {
           return;
         }
         // Get the RTCRtpSender to control the media data being sent
         const sender = transceiver.sender;

         // Define the encryption method for the Chrome browser
         if (isChrome) {
           // Create an encoded stream for encryption transformation
           const streams = sender.createEncodedStreams();
           // Create the encryption transformer
           const transformer = new TransformStream({
             transform(chunk, controller) {
               // The first 7 bytes of VP8 video key frames and the first 3 bytes of non-key frames need to be preserved and not encrypted; otherwise, media transmission will fail
               const vp8ReservedSize = chunk.type === 'key' ? 7 : 3;
               // Implement the encrypt method yourself according to your encryption requirements
               // The example uses the AES encryption algorithm provided by the Web Crypto API
               const encryptedChunk = encrypt(chunk, key, vp8ReservedSize);
               controller.enqueue(encryptedChunk);
             },
           });

           // Connect the encoded stream and the encryption transformer and write the result to the data stream
           streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
         }
       }
       ```

    2. **Subscribe to the encrypted media stream**

       To establish the WebRTC Encoded Transform processing pipeline on the receiving end:

       1. When subscribing to a remote video stream, call the `getRTCRtpTransceiver` method provided by the SDK to obtain an instance of the remote video track's `RTCRtpTransceiver`.

       2. Call the browser's native Streams API to decrypt the video stream.

       Refer to the following sample code:

       ```javascript
       async function subscribe(user, mediaType) {
         // Get the RTCRtpTransceiver instance of the remote video track
         const transceiver = user.videoTrack.getRTCRtpTransceiver();
         if (!transceiver || !transceiver.receiver) {
           return;
         }

         // Get the RTCRtpReceiver to control the received media data
         const receiver = transceiver.receiver;

         // Define the decryption method for Chrome browser
         if (isChrome) {
           // Create an encoded stream for decryption
           const streams = receiver.createEncodedStreams();
           // Create a decryption transformer
           const transformer = new TransformStream({
             transform(chunk, controller) {
               // The first 7 bytes of VP8 video keyframes and the first 3 bytes of non-keyframes do not need decryption
               const vp8ReservedSize = chunk.type === 'key' ? 7 : 3;
               // Implement the decrypt method yourself according to your decryption requirements
               const decryptedChunk = decrypt(chunk, key, vp8ReservedSize);
               controller.enqueue(decryptedChunk);
             },
           });

           // Connect the encoded stream and the decryption transformer, and write the results to the data stream
           streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
         }
       }
       ```

    ## Reference [#reference]

    This section contains content that completes the information on this page, or points you to documentation that explains other aspects of this product.

    ### Development considerations [#development-considerations]

    If the SDK disconnects and then reconnects, the `RTCRtpTransceiver` instance corresponding to the current track may change. Obtain the new `RTCRtpTransceiver` object through the following callbacks:

    * **Local track**: `transceiver-updated`
    * **Remote track**: `transceiver-updated`

    After enabling end-to-end encryption, browser restrictions require creating an empty encryption pipeline for local tracks created using the Web SDK, even if encryption isn't needed. Without this, media data cannot be sent. Use the following code to create the empty encryption pipeline:

    ```javascript
    async function publish() {
      // Publish the local video track
      await client.publish([localVideoTrack]);

      // Get the RTCRtpTransceiver instance of the local video track
      const transceiver = localVideoTrack.getRTCRtpTransceiver();
      if (!transceiver || !transceiver.sender) {
        return;
      }
      // Get the RTCRtpSender to control the media being sent
      const sender = transceiver.sender;

      // Define the encryption mechanism for Chrome
      if (isChrome) {
        // Create encoded streams for transformation
        const streams = sender.createEncodedStreams();
        // Create a transform stream for handling the data
        const transformer = new TransformStream({
          transform(chunk, controller) {
            // Directly write back the data
            controller.enqueue(chunk);
          },
        });
        // Pipe the encoded stream through the transformer and write it to the data stream
        streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
      }
    }
    ```

    ### API reference [#api-reference]

    * Provided by the Web SDK
      * [`ILocalTrack.getRTCRtpTransceiver`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/ilocalaudiotrack.html#getrtcrtptransceiver)
      * [`IRemoteTrack.getRTCRtpTransceiver`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iremoteaudiotrack.html#getrtcrtptransceiver)
    * Browser Native Streams API
      * [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream/TransformStream)
      * [`pipeThrough`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeThrough)
      * [`pipeTo`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeTo)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>
</_PlatformTabsGroup>
