Overview
A LiveKit agent is just another participant in the room, so it needs the shared encryption key to publish and subscribe to encrypted media. The same key encrypts the agent's outbound audio (such as TTS) and decrypts inbound media from clients (such as user speech for STT). Pass an E2EEOptions to ctx.connect inside your job entrypoint, using the same shared key the clients use.
See Key distribution for guidance on delivering the shared key to your agent and clients securely.
Enable E2EE in your agent
Connect to the room with ctx.connect before starting your AgentSession. If you let AgentSession.start connect for you, there's no place to pass encryption options.
import osfrom livekit import rtcfrom livekit.agents import JobContext, AgentSession@server.rtc_session(agent_name="my-agent")async def entrypoint(ctx: JobContext):# Connect with E2EE before starting the sessionawait ctx.connect(encryption=rtc.E2EEOptions(key_provider_options=rtc.KeyProviderOptions(shared_key=os.environ["LIVEKIT_E2EE_KEY"].encode(),),),)session = AgentSession(# ... configure your session ...)await session.start(agent=MyAgent(), room=ctx.room)
import { JobContext, defineAgent, voice } from '@livekit/agents';import type { E2EEOptions } from '@livekit/rtc-node';export default defineAgent({entry: async (ctx: JobContext) => {const e2ee: E2EEOptions = {keyProviderOptions: {sharedKey: new TextEncoder().encode(process.env.LIVEKIT_E2EE_KEY!),},};// Connect with E2EE before starting the sessionawait ctx.connect(e2ee);const session = new voice.AgentSession({ /* ... configure your session ... */ });await session.start({ agent: new MyAgent(), room: ctx.room });},});
If the agent connects to a room with E2EE enabled but doesn't pass a matching key, it can't decrypt inbound media. The agent joins, but STT never produces transcripts, so the LLM never receives input and the agent never speaks. Check the agent logs for decryption errors and confirm the shared key matches the one used by clients.
Distributing the key to your agent
Treat the shared key like any other secret. Common patterns:
- Load it from an environment variable or secret manager at worker startup.
- Pass it through job metadata when dispatching the agent, so different rooms can use different keys.
- Don't hardcode keys in source.
Per-participant keys and key rotation
This page only covers the shared-key case. If your application requires unique keys per participant or key rotation during a room's lifetime, you'll need a custom key provider. See Using a custom key provider for details.