Skip to content

Remove livekit-runtime - #1375

Merged
1egoman merged 12 commits into
mainfrom
remove-livekit-runtime
Aug 31, 2026
Merged

1egoman merged 12 commits into
mainfrom
remove-livekit-runtime

Conversation

@1egoman

@1egoman 1egoman commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Background

The livekit-runtime crate was introduced in ~2024 by the folks at zed so they could use livekit in zed, which wasn't using a tokio runtime (my understanding is, a completely custom runtime!). The general design patterns haven't really changed since then.

A few pain points that have been encountered recently touch on livekit-runtime:

  1. The migration wasn't done completely, and there are quite a few tokio runtime specific calls which were missed (or introduced later inadvertently). One example I know of offhand is the tokio::fs usage in the send_file data stream method. These could be fixed piecemeal, but we'd need to extend the abstractions further to deal with a bunch of different async operations which is fairly burdensome.
  2. Because of 1, today, the async and dispatcher features (what zed was using specifically) don't actually build on main properly 😞 .
  3. We should be testing to ensure that all feature combinations build in ci, but in order to do this with the current design of livekit-runtime (which isn't super well encapsulated - higher level features in the livekit crate sometimes imply certain runtimes), it would be challenging. We'd have to effectively do a build for each platform x runtime x ssl configuration x any ad hoc features which is impractical.
  4. async-std (which the async feature enables) has recently been deprecated in favor of smol, which we don't support today.

From what I can tell looking through zed's code, they seem to have since migrated from the custom runtime approach they were using to a more typical looking tokio backed approach. It also looks like they might be using a rust-sdks fork, not actually mainline. I've done a search across all of github, and the only places that dispatcher seems to be used is by stale zed forks, so I'm fairly confident this can be dropped with little to no consequence.

Summary

Given this context and socializing this decision with the client team, we have decided to drop support for non tokio async runtimes. livekit-runtime will be deprecated and all non tokio shim code will be removed. This will address all points above (fewer features making testing easier, no need to support async-std, and ci validates the crate will always compile on tokio) and lead to a much simpler to maintain project.

If we decide eventually that it's worth reintroducing this behavior, then #1369 would be a good place to start, along with taking some learnings from https://webrtc.rs/blog/2026/01/31/async-friendly-webrtc-architecture.html.

TODO

  • Publish a final livekit-runtime release marking it as deprecated.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Changeset ✓

This PR includes a changeset covering all affected packages:

Package Bump
libwebrtc minor
livekit minor
livekit-api minor
livekit-datatrack minor
livekit-ffi minor
livekit-net minor
livekit-signaling minor
livekit-token-source minor
livekit-uniffi minor

@1egoman
1egoman force-pushed the remove-livekit-runtime branch from 5501fdd to 3aac1fe Compare August 28, 2026 17:23
@1egoman
1egoman force-pushed the remove-livekit-runtime branch from 3aac1fe to b4b0143 Compare August 28, 2026 17:35
Comment thread livekit-api/Cargo.toml
Comment on lines +17 to -26
# deprecated.
signal-client = ["dep:livekit-signaling"]
signal-client-native = ["signal-client", "livekit-signaling/native"]

services = ["access-token", "dep:livekit-region", "dep:reqwest", "dep:tokio", "tokio/time"]

services-tokio = ["access-token", "dep:livekit-region", "dep:reqwest", "dep:tokio", "tokio/time", "dep:livekit-runtime", "livekit-runtime/tokio"]
services-async = ["access-token", "dep:livekit-region", "dep:isahc", "dep:livekit-runtime", "livekit-runtime/async"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers - there is a small breaking change here in a public crate. signal-client-tokio / signal-client-async / signal-client-dispatcher are no longer exposed, but these were never advertised publicly and acted as features that livekit used to bring in a subset of livekit-api prior to breaking out relevant bits of functionality into livekit-signaling.

Comment thread livekit/Cargo.toml
Comment on lines -14 to -16
async = ["livekit-signaling/native-async", "livekit-runtime/async"]
tokio = ["livekit-signaling/native-tokio", "livekit-runtime/tokio"]
dispatcher = ["livekit-signaling/native-dispatcher", "livekit-runtime/dispatcher"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers - another small feature level change in livekit. async / dispatcher no longer exist and tokio was kept around as an alias for native as one probably implies the other in hte vast majority of uses (the big exception would be livekit-ffi / livekit-uniffi)

@1egoman
1egoman marked this pull request as ready for review August 28, 2026 18:46

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Devin Review: 2 flags

Not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

@xianshijing-lk xianshijing-lk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment thread .changeset/remove_livekit_runtime.md Outdated

@ladvoc ladvoc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good to me, been a while since I've reviewed a PR that isn't an addition! A few minor questions/suggestions:

  • I would consider this a minor breaking change since crate feature are being removed/renamed.
  • Remove "[...] however we plan to make the async executor runtime agnostic" from the top-level README.

@1egoman
1egoman merged commit f803fca into main Aug 31, 2026
24 checks passed
@1egoman
1egoman deleted the remove-livekit-runtime branch August 31, 2026 16:00
@knope-bot knope-bot Bot mentioned this pull request Aug 31, 2026
@1egoman

1egoman commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

(I will wait until this is released and add a TODO item later to Publish a final livekit-runtime release marking it as deprecated)

alan-george-lk pushed a commit that referenced this pull request Sep 8, 2026
> [!IMPORTANT]
> Merging this pull request will create these releases

# livekit-datatrack 0.1.15 (2026-09-08)
## Features

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
# livekit-uniffi 0.1.10 (2026-09-08)
## Features

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Lower the Android UniFFI AAR minSdk from 24 to 21
- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
- Add `self_test_http_get` / `self_test_ws_echo` / `has_http_client` /
`has_ws_client` UniFFI exports so foreign hosts can exercise the
transport seam end-to-end.
- Attach Dart/Flutter cdylib assets to releases and prepare
livekit_uniffi for pub.dev publishing
- Update Android's JNA dependency version to 5.19.1 to support 16KB page
sizes
# livekit-token-source 0.1.3 (2026-09-08)
## Features

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
# livekit-protocol 0.7.13 (2026-09-08)
## Features

- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
# livekit-common 0.1.3 (2026-09-08)
## Fixes

- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
# livekit-signaling 0.1.1 (2026-09-08)
## Features

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)

### Moves the signalling client into a new `livekit-signaling` crate.
livekit-api

re-exports it under the historical `livekit_api::signal_client` path,
now marked
deprecated: it is internal SDK API, and dependents should use
livekit-signaling
directly. livekit-api no longer depends on livekit-net.

Also drops two dependencies that were declared but never used:
`scopeguard` and
`bytes`.
# livekit 0.9.0 (2026-09-08)
## Breaking Changes

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
- Fix pre-encoded frame segfault on macOS
- Handle capture of dmabuf using existing capture path
- Add `self_test_http_get` / `self_test_ws_echo` / `has_http_client` /
`has_ws_client` UniFFI exports so foreign hosts can exercise the
transport seam end-to-end.

### Make AdmProxy worker-thread-affine: all platform ADM access now
happens on the WebRTC worker thread, matching the ADM threading
contract.

- The platform ADM is now created lazily on the first PlatformAudio
acquire on all platforms, so apps that never use platform audio never
construct it.
- Fixes Android platform recording delivering no audio: the audio
transport was never registered on the lazily created ADM.
- Fixes a shutdown race by keeping the runtime threads alive as long as
Rust can reach the audio device controller.
- Adds a `platform_audio` example exercising the PlatformAudio API and
the worker-thread marshaling.

### Add agent guidance for detecting and preventing memory-lifecycle
regressions in

Rust, FFI, and native WebRTC code.

### Close peer connections before awaiting signal teardown

`SessionInner::close` released the peer connections only after two
awaits that can block
indefinitely, so cancelling `close()` — for example by wrapping it in a
timeout — left the
transports open and their ICE UDP sockets bound for the lifetime of the
process. Long-lived
clients eventually exhausted their file descriptors. The transports are
now closed before
the first await, which makes the teardown safe to cancel.

### Moves the internal region-discovery cache into a new
`livekit-region` crate. No

public API or behaviour change.

### Moves the signalling client into a new `livekit-signaling` crate.
livekit-api

re-exports it under the historical `livekit_api::signal_client` path,
now marked
deprecated: it is internal SDK API, and dependents should use
livekit-signaling
directly. livekit-api no longer depends on livekit-net.

Also drops two dependencies that were declared but never used:
`scopeguard` and
`bytes`.

### Fix CUDA and FFI resource cleanup during SDK shutdown.

NVIDIA encoder and decoder factories now share a reference-counted CUDA
context
and destroy it when the final factory is dropped. FFI shutdown now
releases
leftover handles one at a time so nested `drop_handle` calls do not
re-enter
`DashMap::clear()`. Adds regression coverage for FFI-handle, watcher,
and
configuration cleanup during disposal.

### Expose network_type on IceCandidateStats

Chromium's local `RTCIceCandidateStats` carries a non-standard
`networkType` field (WiFi,
cellular, ethernet, etc.), but `IceCandidateStats` had no place to put
it, so it was silently
dropped during `get_stats()` deserialization. Adds `network_type:
Option<String>` to the struct;
non-breaking since it already derives `#[serde(default)]`.

### Fix room-session and data-channel leaks across connect/disconnect
cycles.

The E2EE manager callback now captures `RoomSession` weakly so the
session can
drop after disconnect. Data-channel observer callbacks are cleared
during RTC
teardown so the observer/callback cycle cannot keep peer connections
alive.
Adds regression coverage for room-session destruction and data-channel
callback
cleanup.

### Fix native video-source lifecycle and NVENC initialization failure
handling.

The raw-video keepalive task now uses a weak liveness check and defers
its
black I420 buffer allocation until source liveness is confirmed, so
dropping
an unused source releases its resources. `nvEncInitializeEncoder`
failures now
propagate instead of leaving the encoder half-initialized.
# livekit-ffi 0.12.77 (2026-09-08)
## Features

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
- Fix pre-encoded frame segfault on macOS
- Handle capture of dmabuf using existing capture path
- Add `self_test_http_get` / `self_test_ws_echo` / `has_http_client` /
`has_ws_client` UniFFI exports so foreign hosts can exercise the
transport seam end-to-end.

### Make AdmProxy worker-thread-affine: all platform ADM access now
happens on the WebRTC worker thread, matching the ADM threading
contract.

- The platform ADM is now created lazily on the first PlatformAudio
acquire on all platforms, so apps that never use platform audio never
construct it.
- Fixes Android platform recording delivering no audio: the audio
transport was never registered on the lazily created ADM.
- Fixes a shutdown race by keeping the runtime threads alive as long as
Rust can reach the audio device controller.
- Adds a `platform_audio` example exercising the PlatformAudio API and
the worker-thread marshaling.

### Add agent guidance for detecting and preventing memory-lifecycle
regressions in

Rust, FFI, and native WebRTC code.

### Close peer connections before awaiting signal teardown

`SessionInner::close` released the peer connections only after two
awaits that can block
indefinitely, so cancelling `close()` — for example by wrapping it in a
timeout — left the
transports open and their ICE UDP sockets bound for the lifetime of the
process. Long-lived
clients eventually exhausted their file descriptors. The transports are
now closed before
the first await, which makes the teardown safe to cancel.

### Moves the internal region-discovery cache into a new
`livekit-region` crate. No

public API or behaviour change.

### Moves the signalling client into a new `livekit-signaling` crate.
livekit-api

re-exports it under the historical `livekit_api::signal_client` path,
now marked
deprecated: it is internal SDK API, and dependents should use
livekit-signaling
directly. livekit-api no longer depends on livekit-net.

Also drops two dependencies that were declared but never used:
`scopeguard` and
`bytes`.

### Fix CUDA and FFI resource cleanup during SDK shutdown.

NVIDIA encoder and decoder factories now share a reference-counted CUDA
context
and destroy it when the final factory is dropped. FFI shutdown now
releases
leftover handles one at a time so nested `drop_handle` calls do not
re-enter
`DashMap::clear()`. Adds regression coverage for FFI-handle, watcher,
and
configuration cleanup during disposal.

### Expose network_type on IceCandidateStats

Chromium's local `RTCIceCandidateStats` carries a non-standard
`networkType` field (WiFi,
cellular, ethernet, etc.), but `IceCandidateStats` had no place to put
it, so it was silently
dropped during `get_stats()` deserialization. Adds `network_type:
Option<String>` to the struct;
non-breaking since it already derives `#[serde(default)]`.

### Fix room-session and data-channel leaks across connect/disconnect
cycles.

The E2EE manager callback now captures `RoomSession` weakly so the
session can
drop after disconnect. Data-channel observer callbacks are cleared
during RTC
teardown so the observer/callback cycle cannot keep peer connections
alive.
Adds regression coverage for room-session destruction and data-channel
callback
cleanup.

### Fix native video-source lifecycle and NVENC initialization failure
handling.

The raw-video keepalive task now uses a weak liveness check and defers
its
black I420 buffer allocation until source liveness is confirmed, so
dropping
an unused source releases its resources. `nvEncInitializeEncoder`
failures now
propagate instead of leaving the encoder half-initialized.
# livekit-net 0.1.3 (2026-09-08)
## Features

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Add `self_test_http_get` / `self_test_ws_echo` / `has_http_client` /
`has_ws_client` UniFFI exports so foreign hosts can exercise the
transport seam end-to-end.
# livekit-data-stream 0.1.4 (2026-09-08)
## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
# libwebrtc 0.3.47 (2026-09-08)
## Features

- Handle capture of dmabuf using existing capture path
- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

### Expose network_type on IceCandidateStats

Chromium's local `RTCIceCandidateStats` carries a non-standard
`networkType` field (WiFi,
cellular, ethernet, etc.), but `IceCandidateStats` had no place to put
it, so it was silently
dropped during `get_stats()` deserialization. Adds `network_type:
Option<String>` to the struct;
non-breaking since it already derives `#[serde(default)]`.

## Fixes

- Fix pre-encoded frame segfault on macOS

### Make AdmProxy worker-thread-affine: all platform ADM access now
happens on the WebRTC worker thread, matching the ADM threading
contract.

- The platform ADM is now created lazily on the first PlatformAudio
acquire on all platforms, so apps that never use platform audio never
construct it.
- Fixes Android platform recording delivering no audio: the audio
transport was never registered on the lazily created ADM.
- Fixes a shutdown race by keeping the runtime threads alive as long as
Rust can reach the audio device controller.
- Adds a `platform_audio` example exercising the PlatformAudio API and
the worker-thread marshaling.

### Add agent guidance for detecting and preventing memory-lifecycle
regressions in

Rust, FFI, and native WebRTC code.

### Fix CUDA and FFI resource cleanup during SDK shutdown.

NVIDIA encoder and decoder factories now share a reference-counted CUDA
context
and destroy it when the final factory is dropped. FFI shutdown now
releases
leftover handles one at a time so nested `drop_handle` calls do not
re-enter
`DashMap::clear()`. Adds regression coverage for FFI-handle, watcher,
and
configuration cleanup during disposal.

### Fix room-session and data-channel leaks across connect/disconnect
cycles.

The E2EE manager callback now captures `RoomSession` weakly so the
session can
drop after disconnect. Data-channel observer callbacks are cleared
during RTC
teardown so the observer/callback cycle cannot keep peer connections
alive.
Adds regression coverage for room-session destruction and data-channel
callback
cleanup.

### Fix native video-source lifecycle and NVENC initialization failure
handling.

The raw-video keepalive task now uses a weak liveness check and defers
its
black I420 buffer allocation until source liveness is confirmed, so
dropping
an unused source releases its resources. `nvEncInitializeEncoder`
failures now
propagate instead of leaving the encoder half-initialized.
# webrtc-sys 0.3.44 (2026-09-08)
## Features

- Handle capture of dmabuf using existing capture path

## Fixes

- Fix pre-encoded frame segfault on macOS

### Make AdmProxy worker-thread-affine: all platform ADM access now
happens on the WebRTC worker thread, matching the ADM threading
contract.

- The platform ADM is now created lazily on the first PlatformAudio
acquire on all platforms, so apps that never use platform audio never
construct it.
- Fixes Android platform recording delivering no audio: the audio
transport was never registered on the lazily created ADM.
- Fixes a shutdown race by keeping the runtime threads alive as long as
Rust can reach the audio device controller.
- Adds a `platform_audio` example exercising the PlatformAudio API and
the worker-thread marshaling.

### Add agent guidance for detecting and preventing memory-lifecycle
regressions in

Rust, FFI, and native WebRTC code.

### Fix CUDA and FFI resource cleanup during SDK shutdown.

NVIDIA encoder and decoder factories now share a reference-counted CUDA
context
and destroy it when the final factory is dropped. FFI shutdown now
releases
leftover handles one at a time so nested `drop_handle` calls do not
re-enter
`DashMap::clear()`. Adds regression coverage for FFI-handle, watcher,
and
configuration cleanup during disposal.

### Fix native video-source lifecycle and NVENC initialization failure
handling.

The raw-video keepalive task now uses a weak liveness check and defers
its
black I420 buffer allocation until source liveness is confirmed, so
dropping
an unused source releases its resources. `nvEncInitializeEncoder`
failures now
propagate instead of leaving the encoder half-initialized.
# livekit-region 0.1.1 (2026-09-08)
## Fixes

### Moves the internal region-discovery cache into a new
`livekit-region` crate. No

public API or behaviour change.
# livekit-api 0.7.0 (2026-09-08)
## Breaking Changes

- Removes livekit-runtime and converts this package to be tokio only
again - #1375 (@1egoman)

## Fixes

- Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol
- Add `self_test_http_get` / `self_test_ws_echo` / `has_http_client` /
`has_ws_client` UniFFI exports so foreign hosts can exercise the
transport seam end-to-end.
- Update sip_busy test GRPc code: failed_precondition ->
failed_precondition

### Moves the internal region-discovery cache into a new
`livekit-region` crate. No

public API or behaviour change.

### Moves the signalling client into a new `livekit-signaling` crate.
livekit-api

re-exports it under the historical `livekit_api::signal_client` path,
now marked
deprecated: it is internal SDK API, and dependents should use
livekit-signaling
directly. livekit-api no longer depends on livekit-net.

Also drops two dependencies that were declared but never used:
`scopeguard` and
`bytes`.
# livekit-token 0.1.2 (2026-09-08)
## Fixes

- Add the `PASSTHROUGH` encoding preset and remove the unused
`UpdateEgressRequest` from the generated protocol

Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants