Conversation
The crate will soon hold two different FFI surfaces. Put the current surface in its own module first, before the second surface comes in. This commit moves four items into `src/room_apis/`: the C ABI entry points, the generated protobuf module, the request server and the type conversions. The content of these files does not change. The new `room_apis/mod.rs` holds the module declarations that were in `lib.rs`. A glob re-export in `lib.rs` keeps the public API the same. The crate-internal paths `crate::proto` and `crate::server` also stay correct. Thus no other file changes.
The crate root still held six items that belong to one surface only: `FfiError`, `FfiResult`, `FfiCallbackFn`, `FfiHandleId`, `INVALID_HANDLE` and `FFI_SERVER`. Move them to `room_apis/mod.rs`, with the four `use` statements that they need. The glob re-export from the previous commit republishes these items at the crate root. Their paths thus do not change, for other crates or for the code in this crate. This commit adds no feature gates. The gates come in the next commit.
…feature A second surface will follow. It must not pull in libwebrtc. Make the current surface optional first, so that a build can leave it out. Each dependency that only the `room_apis` module tree uses becomes optional. The new `room-apis` feature selects them. No source file changes, because each of these dependencies is already isolated in that module tree. The base `tokio` feature set becomes `sync` and `rt-multi-thread`. The `room-apis` feature adds `full` and `parking_lot` again. `default` keeps `room-apis` on. A build that does not give feature flags thus gets the same dependency tree as before this commit. IMPORTANT: the TLS features must select `room-apis`, because they only configure `livekit`. The file .github/workflows/ffi-builds.yml builds three targets with `--no-default-features --features "<tls>"`. Without this rule, those targets would build a crate with no surface in it.
The build script always downloaded libwebrtc, configured the linker and generated the protobuf code. The second surface needs none of these steps. Put the four steps behind the `room-apis` feature. The `DOCS_RS` check stays before the gate, so the behaviour on docs.rs does not change. IMPORTANT: this must be a `cfg` attribute and not a `CARGO_FEATURE_ROOM_APIS` lookup. The crates `prost-build` and `webrtc-sys-build` are now optional build dependencies with the same gate. Their paths do not resolve when the feature is off, so a check at run time is too late. This gate is also what makes the Apple targets possible. The function `configure_linker` stops with a panic on an unknown target operating system. visionOS, tvOS and Mac Catalyst are all unknown to it, and the Swift package builds all three.
The crate tools/bindgens already holds the shared uniffi-bindgen CLI. The Dart generator belongs with it. Move `bindgen-dart.rs` there without a change to its content. Put it behind a new `dart-bindgen` feature, which is off by default, so that a plain `cargo build --workspace` does not fetch the git dependency it needs. Delete `bindgen.rs`, because tools/bindgens has the same program already. Do this before the merge of the two crates. If these binaries stay in livekit-ffi, then `cargo run` in that crate turns on its default features and builds all of libwebrtc to generate bindings.
…dules module Move the six modules of livekit-uniffi into `livekit-ffi/src/core_modules/`: access tokens, log forwarding, data tracks, data streams and the shared runtime. The content of these files does not change. The crate root of livekit-uniffi becomes the module root `core_modules/mod.rs`, also without a change to its content. The next commit removes the items in it that only a crate root can hold. Delete `build_info.rs`. The file livekit-ffi/src/build_info.rs holds the same function, and both surfaces share it. Nothing declares the new module yet, so no build uses this code.
Declare the moved modules and add the dependencies that they need. The new `core-modules` feature selects all of them. This feature does not use `livekit`, `libwebrtc`, `soxr-sys` or `imgproc`. Remove from `core_modules/mod.rs` the two items that only a crate root can hold: the `build_info` module and the scaffolding call. Keep the statement `extern crate livekit_net`, which makes the linker put the UniFFI scaffolding of livekit-net into the shared library. IMPORTANT: the two features are mutually exclusive, and a `compile_error` stops a build that selects both. The cause is the UniFFI scaffolding: a crate can have only one such call, and it must be at the crate root. A second `compile_error` stops a build that selects neither feature. There is one scaffolding call for each surface. The core-modules call keeps the namespace `livekit_uniffi`. The generated Swift, Kotlin, Dart and Python modules thus keep their current names, and no downstream SDK must change. A later commit renames them.
The two surfaces need different versions of prost. The crates livekit-protocol and livekit-data-stream use prost 0.12, and the core modules call the `Message` trait on the types that these crates generate. The protobuf code of livekit-ffi comes from prost-build 0.14 and writes `::prost::` paths, so the name `prost` must stay on 0.14. Add prost 0.12 a second time under the name `prost_012` and change the seven imports in the core modules. All seven are in the moved code. No file in the room-apis surface changes. IMPORTANT: this is safe only because the two surfaces are mutually exclusive. The two versions of prost never go into the same build. No existing code calls a prost trait on a livekit-protocol type, thus the two versions did not meet before this change either. Remove this when the workspace uses one version of prost.
Move the files that build and package the UniFFI bindings: the cargo-make tasks, the UniFFI configuration, the Swift, Android and Dart support files, the Python test script and the agent notes. The content of these files does not change. Later commits change them. Move the ignore rules for the `generated/` and `packages/` directories into the livekit-ffi ignore file. Both directories hold build output. Do not move the Node support files. The next commit deletes them.
Delete the Node package of the UniFFI bindings: the support templates, the test project and the cargo-make tasks that build them. The crate livekit-ffi-node-bindings gives the Node bindings. It builds the room-apis surface with napi-rs. Two Node packages from one crate is more than the project needs now. This commit only deletes. Nothing else uses these files. A later change can add the package again.
All the code and all the packaging of livekit-uniffi are now in livekit-ffi. Delete what is left: the manifest, the changelog and the readme. Remove the crate from the workspace members and regenerate the lock file. Give the changeset scopes of livekit-uniffi to livekit-ffi, so that a changeset with an old scope still finds a package. IMPORTANT: remove the workspace member and the knope package in one commit. The script .github/scripts/changeset_detect.py compares the two lists. A knope package with no crate, or a crate with no knope package, stops the build. The changelog of livekit-uniffi is in the git history. The changelog of livekit-ffi continues from its own version.
Two changesets name livekit-uniffi. That package no longer exists. The script .github/scripts/changeset_detect.py rejects a bump for an unknown package and stops the build. In the first changeset, the livekit-ffi patch bump already covers the change. Delete the livekit-uniffi line only. IMPORTANT: in the second changeset, the bump was major. Do not make the livekit-ffi bump major. The version of livekit-ffi is below 1.0, so a major bump gives 1.0.0 and not 0.13.0. Use a minor bump and write the breaking change in the text.
Add a table to livekit-ffi/Cargo.toml that gives the cargo flags for each downstream package build, and a script that reads it. There is now one place that shows what Python, Node, Unity, Swift, Android and Dart each get. The script is the single source of truth for CI and for the cargo-make tasks. A platform thus cannot move to the wrong surface without a change to the table. The script also refuses a core-modules entry that does not turn the default features off, because the default features select room-apis and the two are mutually exclusive. The option `--surface` answers for the build sites that build no artifact for a platform: the per-commit gates and the shared host build that the bindings generator reads. It answers only when every platform on that surface agrees. This commit is larger than the others. It is one new file and the table that it reads, which are of little use apart.
Every cargo command in Makefile.toml uses the working directory and gives no `-p` flag. After the merge, each one thus builds the default surface. IMPORTANT: without this change the Swift, Android and Dart packages all build livekit-ffi/room-apis and pull in libwebrtc. Take the flags from the platform table instead of writing them here, so that the packaging and the table cannot disagree. The duckscript `exec` command does not divide an expanded variable into separate arguments, thus those two tasks go through `sh -c`. Run the two bindings generators from the tools/bindgens crate. A `cargo run` in this crate turns on its default features. Point the download address for the prebuilt libraries at the livekit-ffi release tag.
The crate now builds `liblivekit_ffi` and not `liblivekit_uniffi`. The crate name sets this, so the merge cannot prevent it. Change the two places that name the library: the UniFFI configuration, which the generated Kotlin and Dart code uses to load it, and the Dart build hook, which names the native asset. IMPORTANT: this is a small change with a late failure. The package names do not change, so the Dart package still builds and still goes to pub.dev. It then fails on the machine of the user, when the asset does not resolve. A version on pub.dev cannot be removed. The Swift, Kotlin, Maven and pub.dev package names stay the same. No user of these packages must change an import.
Point the four workflows that build the UniFFI packages at the merged crate. The cdylib workflow builds `-p livekit-ffi` with the flags that the platform table gives for Dart, and its artifacts now carry the livekit-ffi library name. The Swift, Android and Dart test workflows change their working directory to livekit-ffi. The linker workaround for 64-bit Linux stays. The file .cargo/config.toml still selects lld for that target to link libwebrtc, which this surface does not use. The package names do not change here. The Swift package keeps its current name until the rename commit.
The publishing on pub.dev needs a workflow that a tag push starts. This is part of its trusted publishing configuration. The knope configuration for livekit-ffi makes a draft release. A draft release has no git tag. The tag comes into existence only when the workflow publishes the release, and the default token does that. GitHub does not let an event from the default token start another workflow, because this would make a loop. No workflow downstream would therefore start. IMPORTANT: create the tag with a token from the livekit-repo-writer GitHub App, before the release becomes public. An event from an App does start other workflows. The step first makes sure that the tag agrees with the version in the manifest at that commit. This workflow runs for every push to the main branch and takes whatever draft release it finds, which can be older than the commit. If the tag is already there, the step gives a warning and stops with success. The release files are already uploaded at that point, and a failure would leave the release as a draft.
This workflow started on a published release event. That worked only because knope-bot, which is a GitHub App, published each livekit-uniffi release directly, with its tag. IMPORTANT: livekit-ffi uses the draft release flow, so the same event now comes from the default token and starts nothing. Without this change, the Swift and Android packages would stop being published, and no error would show. Start on the push of the tag that the previous commit creates. The release job makes that tag only after the build matrix is good, so a tag push still means that all targets built. This is the same condition that the release event gave. The old condition on the tag prefix is no longer necessary. The tag filter does that work, and a check in the script rejects any other tag.
Follow the tag prefix of the merged crate. The workflow now starts on a `livekit-ffi/v*` tag, takes the version from that prefix, and compares it with the version in livekit-ffi/Cargo.toml. The staging directory and the guard against a native library in the package keep their paths correct for the new crate. The name of the package on pub.dev does not change in this commit. Users of the package therefore need no change.
Before the merge, livekit-uniffi was a separate crate. A build of the workspace thus compiled it, and a test run of the workspace ran its tests. That is no longer true. The `core-modules` feature is not a default feature, so a build of the workspace does not reach this code, and its thirteen tests would stop running without any message. Add one build step and one test step that select the feature. The two features are mutually exclusive, so these cannot join the workspace commands and need their own invocation. Both steps take their flags from the platform table.
This workflow generates the Node protobuf code from the files in livekit-ffi/protocol. Its path filter matched all of livekit-ffi, the two root manifests and all of .github. After the merge that filter also matches every change to the core modules, which have no protobuf files. The workflow writes a commit back to the branch, so it should run only when it has work to do. Match the protobuf directory, the Node bindings crate and this workflow file. This is less than the filter matched before the merge.
The readme described one FFI interface for Python, Node and Unity. The crate now has two surfaces, and the file must say which SDK uses which. Give each surface its section: what it contains, which SDKs consume it, where its code is and the command that builds it. Point at the platform table for the full list. Add the notes that save time later: the two features cannot be selected together, the cargo-make tasks need explicit feature flags, the build script is gated, and prost has two versions. The file AGENTS.md is a symbolic link to this file, so both are correct.
Correct the places that describe two FFI crates: the agent notes, the root readme, the notes of three other crates, the comments that point at files in the deleted crate, and the note about which crates the feature job checks. Add the changeset for the merge. It records that the two surfaces are now one crate with two features, that everything which built livekit-ffi builds the same way, and that what built livekit-uniffi now selects the core-modules feature. The changeset also records the one visible difference for a downstream SDK: the native library is now liblivekit_ffi. No package name and no import changes.
…FI identity Every commit before this one keeps the names that the Swift, Android and Flutter SDKs use today. This commit changes them, and it is the only commit that does. Hold it back or move it to its own pull request if the rename is not wanted now. The Swift package and product become LiveKitFFI and RustLiveKitFFI. The Maven artifact becomes io.livekit:livekit-ffi-android with the Kotlin package io.livekit.ffi. The package on pub.dev becomes livekit_ffi. Both surfaces now use the crate name as their UniFFI namespace. Rename the podspec template with the package. The cargo-make task builds its file name from the package name, so the old name would stop the Swift release with a missing template. The repository that holds the Swift framework keeps its current name. Only the package and the product inside it change. BREAKING CHANGE: imports, dependency coordinates and generated module names all change together for the Swift, Android and Flutter SDKs. Before the first release, publish livekit_ffi to pub.dev by hand and set its tag pattern, because pub.dev automates only a package that exists. Also remove the old source directory from the framework repository.
1egoman
added this pull request to stack #1440
September 18, 2026 17:14
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TODO: write more thorough description