many-forest-60185
12/26/2025, 10:57 AMtall-airline-16756
12/26/2025, 10:57 AMtall-airline-16756
12/26/2025, 10:57 AMmany-forest-60185
12/26/2025, 10:58 AMtall-airline-16756
12/26/2025, 10:59 AMlk cloud auth).
• agent-starter-python cloned locally.
• A GCP project with a storage bucket and a service account JSON key that has "Storage Object Creator" for that bucket.
Steps
1) Add server SDK to the project
• In your agent project, install the server SDK and dotenv:
uv add livekit-api python-dotenv
2) Add GCP credentials and LiveKit secrets
• Place your service account JSON in the project (e.g., ./gcp-sa.json) or ensure Cloud Run/Cloud Run service has workload identity set.
• Create a .env (local dev) with:
LIVEKIT_URL=wss://<your-project>.livekit.cloud
LIVEKIT_API_KEY=<your_api_key>
LIVEKIT_API_SECRET=<your_api_secret>
GCP_PROJECT_ID=<your-gcp-project-id>
GCP_BUCKET_NAME=<your-bucket-name>
GOOGLE_APPLICATION_CREDENTIALS=./gcp-sa.json
• For production on LiveKit Cloud, inject these as agent secrets using the LiveKit CLI instead of committing files.
3) Create a standalone egress trigger script (not inside the agent runtime)
• Example start_egress.py (async Python using livekit-api):
import os, asyncio
from dotenv import load_dotenv
from livekit import api
load_dotenv()
async def start_room_egress(room_name: str):
lk = api.LiveKitAPI(os.getenv("LIVEKIT_URL"),
api_key=os.getenv("LIVEKIT_API_KEY"),
api_secret=os.getenv("LIVEKIT_API_SECRET"))
req = api.StartRoomCompositeEgressRequest(
room_name=room_name,
file_output=api.EncodedFileOutput(
file_type=api.EncodedFileType.MP4,
filepath=f"{room_name}_recording.mp4",
output=api.GoogleCloudStorageUpload(
bucket_name=os.getenv("GCP_BUCKET_NAME"),
project_id=os.getenv("GCP_PROJECT_ID"),
),
),
layout="speaker-dark"
)
resp = await lk.egress.start_room_composite(request=req)
print("egress_id:", resp.egress_id)
await lk.close()
return resp.egress_id
if __name__ == "__main__":
asyncio.run(start_room_egress("test-room"))
• Replace room_name with the active room you want to record.
4) Optionally trigger egress automatically via webhook
• Deploy a small webhook endpoint (Flask/FastAPI) to receive LiveKit events and call the script when room_started occurs.
• Register the webhook URL in your LiveKit Cloud project.
• Use livekit.api.WebhookReceiver to validate incoming webhook payloads and then call the same start_room_egress(...) logic.
5) Run and test locally
• Start your agent locally (dev mode) so a room can be created:
uv run src/agent.py dev
• Join the room from your client.
• Run python start_egress.py to start egress to your GCS bucket.
• Check GCS for test-room_recording.mp4.
6) Deploy agent and webhook to LiveKit Cloud
• Use lk agent create (or lk agent update) to deploy your agent and webhook.
• Inject secrets via the CLI rather than `.env`:
lk agent update --secrets-file secrets.txt
where secrets.txt contains the environment values (omit the local JSON file — use Cloud secret management or workload identity in GCP).
7) Monitor and control egress
• Use lk agent logs and lk agent status to view agent health.
• Programmatically list/stop egress via lk_api.egress.list_egress() and lk_api.egress.stop_egress(egress_id=...) from the same server SDK.
Notes & tips
• The egress call runs from a server-side process (your control plane). The agent itself participates in the room but the egress start is initiated via the server SDK or CLI.
• Ensure the service account key or runtime identity used has permissions to write to the target GCS bucket.
• For production, prefer GCP workload identity or secret injection rather than bundling JSON keys.
• File outputs (MP4/WebM/HLS) support GCS uploads. RTMP uses different output configuration.
If you want, I can:
• Provide a complete webhook server example (Flask) wired to call egress on room_started.
• Show how to configure LiveKit agent secrets for Cloud deployment.
• Switch the example to track egress or RTMP output.
Which follow-up would you like—webhook example, deployment secrets steps, or a track/RTMP example?many-forest-60185
12/26/2025, 11:00 AMtall-airline-16756
12/26/2025, 11:00 AMlk cloud auth).
• agent-starter-node cloned and Node installed.
• GCP bucket created. Create an HMAC key for the bucket (S3-compatible) or use a service account + S3-compatible settings. HMAC keys give GCS_ACCESS_KEY and GCS_SECRET.
• livekit-server-sdk available in your project.
Steps
1) Install dependencies
• In the agent project:
pnpm install
pnpm add livekit-server-sdk
2) Add environment variables
• Local `.env`:
LIVEKIT_URL=wss://<your-project>.livekit.cloud
LIVEKIT_API_KEY=<api_key>
LIVEKIT_API_SECRET=<api_secret>
GCS_ACCESS_KEY=<hmac_access_key>
GCS_SECRET=<hmac_secret>
GCS_BUCKET_NAME=<your-bucket>
• In production, inject secrets via the LiveKit CLI (don’t commit .env).
3) Start Room Composite egress from Node (example)
• Use EgressClient to start egress (S3 output configured for GCS endpoint). Example snippet to run when you want to start recording:
import { EgressClient } from "livekit-server-sdk";
const egressClient = new EgressClient(process.env.LIVEKIT_URL!, {
apiKey: process.env.LIVEKIT_API_KEY!,
secret: process.env.LIVEKIT_API_SECRET!,
});
const req = {
roomName: "my-room",
layout: "speaker-dark",
file: {
fileType: "mp4",
output: {
s3: {
accessKey: process.env.GCS_ACCESS_KEY!,
secret: process.env.GCS_SECRET!,
endpoint: "<https://storage.googleapis.com>",
bucket: process.env.GCS_BUCKET_NAME!,
region: "auto",
forcePathStyle: false
}
}
}
};
try {
const info = await egressClient.startRoomCompositeEgress(req);
console.log("egress started:", info.egressId);
} catch (err) {
console.error("egress error:", err);
}
4) Where to trigger egress
• Option A (inline): start egress from your agent code when the agent joins the room.
• Option B (recommended): run egress from an external control service (webhook or small backend) to decouple recording from agent lifecycle.
5) Webhook-based automatic start (recommended)
• Create a webhook endpoint to receive LiveKit events and validate using WebhookReceiver.
• On room_started, call the egress client with the same request above.
• Deploy that webhook service and register its URL in your LiveKit Cloud project.
6) Test locally
• Start the agent:
uv run src/agent.ts dev
• Join the room from a client.
• Trigger egress (either via your webhook or run the egress call).
• Check GCS bucket for the MP4 file.
7) Deploy to LiveKit Cloud
• Use LiveKit CLI:
lk cloud auth
lk agent create
• Inject secrets instead of shipping `.env`:
lk agent update --secrets-file secrets.txt
where secrets.txt contains the env variables (omit local JSON keys; use HMAC or GCP secret patterns).
8) Monitor & control
• Use lk egress list / lk egress info <id> or the SDK’s listEgress() / stopEgress() to manage runs.
• Use webhooks for egress_started and egress_ended events for post-processing.
Notes & tips
• GCS via S3 endpoint requires HMAC keys or appropriate S3-compatible credentials. If you prefer direct GCS uploads (via service account), you must ensure the runtime has credentials and LiveKit supports GoogleCloudStorageUpload in server SDK patterns (S3 is commonly used via endpoint).
• For production, prefer workload identity or secret injection rather than storing keys in files.
• If you want automatic retention rules, post-processing, or custom layouts, I can show examples.
Next step — which would you like:
• A complete webhook example (Express) that validates events and starts egress?
• A ready-to-deploy agent code patch (where to place the egress call inside agent-starter-node)?
• Track or RTMP egress example instead of Room Composite? Sources: server-sdk-js | livekit/livekit_composite | DeepWiki | Deploying to production | LiveKit Docs | Universal Egressmany-forest-60185
12/26/2025, 11:03 AMtall-airline-16756
12/26/2025, 11:03 AMmany-forest-60185
12/26/2025, 11:15 AMtall-airline-16756
12/26/2025, 11:16 AMimport { EgressClient } from "livekit-server-sdk";
const egressClient = new EgressClient(process.env.LIVEKIT_URL!, {
apiKey: process.env.LIVEKIT_API_KEY!,
secret: process.env.LIVEKIT_API_SECRET!,
});
const req = {
roomName: "my-room",
// For audio-only, use track egress or room composite with audio-only file type
file: {
fileType: "mp4", // or "webm", "ogg"
output: {
s3: {
accessKey: process.env.GCS_ACCESS_KEY!,
secret: process.env.GCS_SECRET!,
endpoint: "<https://storage.googleapis.com>",
bucket: process.env.GCS_BUCKET_NAME!,
region: "auto",
}
}
},
audioOnly: true, // if supported by your SDK request shape; otherwise use track egress
};
const res = await egressClient.startRoomCompositeEgress(req);
console.log("egress id", res.egressId);
Python example — track egress to OGG/Opus in GCS (preferred for voice)
from livekit import api
import os, asyncio
async def start_track_egress(participant_identity):
lk = api.LiveKitAPI(os.getenv("LIVEKIT_URL"),
api_key=os.getenv("LIVEKIT_API_KEY"),
api_secret=os.getenv("LIVEKIT_API_SECRET"))
req = api.StartTrackEgressRequest(
room_name="my-room",
participant_identity=participant_identity,
output=api.TrackEgressRequestFileOutput(
file=api.EncodedFileOutput(
file_type=api.EncodedFileType_OGG, # OGG/Opus
filepath=f"{participant_identity}_audio.ogg",
output=api.GoogleCloudStorageUpload(
bucket_name=os.getenv("GCP_BUCKET_NAME"),
project_id=os.getenv("GCP_PROJECT_ID"),
)
)
)
)
resp = await lk.egress.start_track_egress(request=req)
print("egress id", resp.egress_id)
await lk.close()
# call with asyncio.run(start_track_egress("user123"))
Notes and tips
• Use Track Egress when you only need a single participant’s raw audio. Use Room Composite/Track Composite when you want mixed audio of the whole room.
• Choose Ogg/Opus for voice quality and size. Choose MP4/AAC for maximum playback compatibility.
• Keep egress control on the server with LIVEKIT_API_KEY/SECRET (don’t add egress params to client tokens).
• Ensure GCS credentials (HMAC or service account) used by the server have write access to the bucket.
Would you like a full Express/Flask webhook example that auto-starts audio-only egress on room_started?