Channel Encryption
Media encryption vs transport encryption, configuration, key rotation and supported modes.
"Encryption" has two completely different layers in real-time audio and video, and mixing them up is the easiest way to get it wrong. This page separates them first, then explains what we do at each layer.
The two layers
| Layer | What it protects | Who is responsible | Our status |
|---|---|---|---|
| Transport encryption | The link between the client and the media server | The platform (DTLS-SRTP + HTTPS/WSS) | ✅ On by default, no configuration needed |
| Media encryption (E2EE) | End to end, so even the media server cannot decrypt | Application-layer key agreement + SDK encryption | ➖ Not implemented, explicitly NOT_SUPPORTED |
Transport encryption (default)
- Signalling uses
https:///wss://; media uses SRTP (DTLS-SRTP by default in LiveKit). - You do not need to call any API; your reverse proxy must allow WebSocket upgrades, otherwise connections degrade into failures (see the troubleshooting manual).
AgoraRTC.on('security-policy-violation')fires when browser security policy (mixed content, permissions policy and so on) blocks something, which makes diagnosis easier.
Media encryption: in the interface definition, explicitly rejected at runtime
The Agora Web SDK surface includes a setEncryptionConfig:
client.setEncryptionConfig('aes-256-gcm2', secret, salt, true);Our implementation branches on it explicitly:
// Explicitly declaring "no encryption" is allowed, which makes it easy to move Agora code over unchanged (secret must not be an empty string, otherwise INVALID_PARAMS is reported first)
client.setEncryptionConfig('none', 'no-secret-used');
// Any encryption mode other than 'none' throws instead of silently succeeding
try {
client.setEncryptionConfig('aes-256-gcm2', 'a-32-byte-secret-material........', undefined, true);
} catch (error) {
// error.code === 'NOT_SUPPORTED'
// error.message === 'end-to-end encryption is not supported by the ni-meet media adapter yet'
}Behaviour details (taken from the implementation, not inferred):
| Input | Behaviour |
|---|---|
encryptionMode empty / secret not a string or empty | Throws INVALID_PARAMS |
encryptionMode === 'none' | Accepted, configuration recorded, no encryption applied |
Any other mode (aes-128-xts, aes-256-gcm2, sm4-128-ecb…) | Throws NOT_SUPPORTED (AgoraRTCError.code === 'NOT_SUPPORTED') |
The EncryptionMode type in the interface definition is complete (9 values), because the public surface must match Agora exactly; whether a mode is supported is a runtime capability, not a type.
Viable paths when you need E2EE
- Application-layer encryption (recommended for most teams): media stays plain SRTP, but sensitive content (whiteboard data, files) is encrypted end to end at the application layer and sent over the data channel.
- Build your own E2EE media layer: plug an engine that supports SFrame/Insertable Streams into the media adapter layer, and wire
setEncryptionConfigto it while keeping the SDK interface definition. This is engineering investment, not a configuration option. - Choose another solution: if compliance hard-requires media-level E2EE and in-house development is not acceptable, this requirement does not match the current capabilities of this project and should be surfaced early rather than worked around.
Key management (applies to every path)
- Keys never go into client bundles or front-end environment variables;
- Use a separate key per session and discard it when the session ends;
- Key rotation does not affect issued tokens (tokens govern authorization, keys govern encryption; the two are independent);
- Audit logs record only "enabled or not" and "rotation time", never the key itself.
Common misconceptions
| Misconception | Fact |
|---|---|
"The token contains salt, so it is encrypted" | salt is only a randomiser for the token signature and has nothing to do with media encryption |
"setEncryptionConfig returned void, so it worked" | We throw on unsupported modes; not throwing only means 'none' or that parameter validation passed |
| "HTTPS is end-to-end encryption" | HTTPS only reaches the server; beyond the server the traffic is plaintext or another TLS segment |
| "Turning encryption on makes it laggier" | E2EE has overhead, but we do not provide it, so this trade-off does not exist here |