Add a TokenSource crate to the Rust SDKs - #1274
Conversation
ladvoc
left a comment
There was a problem hiding this comment.
Left some initial comments, will return for a more thorough review when this is taken out of draft.
1egoman
left a comment
There was a problem hiding this comment.
I saw you opened this so I left some initial thoughts, feel free to ignore them for now if it's still early and not yet ready for a review.
|
Potential follow up: Mint tokens based on API secret |
|
@ladvoc this thing is already minting tokens from api key and api secret right? Are we using this in the rust-dev-client? |
f690b60 to
e63f0ef
Compare
dea4dce to
1d0d082
Compare
Changeset ✓This PR includes a changeset covering all affected packages:
|
81e7a4d to
30aaa7e
Compare
1egoman
left a comment
There was a problem hiding this comment.
Really nice work! I've left a bunch of small / nitpicky comments which I noticed when going through, but I think this generally makes sense to me.
One higher level / larger question: I see you decided to punt the TokenSourceMinter question to the future. Totally fine with me, but I am just raising that because I think @ladvoc might have a use case for it in livekit-capture. It might be worth chatting with him about it.
| let response = http_client.post(self.endpoint_url.clone(), headers, body).await?; | ||
|
|
||
| if !(200..300).contains(&response.status) { |
There was a problem hiding this comment.
thought: This isn't in this code, but seeing this makes me realize we really should have a .ok() method on response which encapsulates this (200..300).contains(&response.status) check. Is that something you'd be willing to add to livekit-net? Or I could maybe do it as a follow up if you want.
There was a problem hiding this comment.
Maybe you can draft it?
There was a problem hiding this comment.
I can do it as a follow up, np.
| /// The return type of [`TokenSource::endpoint`]. | ||
| pub struct TokenSourceEndpoint { | ||
| endpoint_url: String, | ||
| headers: Vec<(String, String)>, | ||
| } | ||
|
|
||
| #[async_trait] | ||
| impl TokenSourceConfigurable for TokenSourceEndpoint { | ||
| async fn fetch( | ||
| &self, | ||
| options: &TokenSourceFetchOptions, | ||
| ) -> TokenSourceResult<TokenSourceResponse> { | ||
| let request = TokenSourceRequest::from(options); | ||
|
|
There was a problem hiding this comment.
thought: In the web token source implementation, there is caching which is done to avoid re-fetching a new token when the previous token is valid. IMO, this is really useful, and can be a significant slowdown when a user is initially connecting to a room to not have this. It also allows for pre-emptive token fetching (either on app load, or after a disconnect to make repeated connect / disconnect cycles fast).
I know how exactly this caching is done differs between the implementations (Web does it within the TokenSourceEndpoint via an internal abstract class it extends, swift has a TokenSourceCached, etc) but IMO it would be worth coming up with a strategy of some sort on how to handle this (I don't have a strong preference how it should work).
t's somewhat arguable whether adding it would be a breaking change in the interface IMO, adding caching later on obviously wouldn't break the existing interface but also on the other hand, a user may build around assuming that a token will always be regenerated for each room connection and adding caching would break that assumption. That's why I would be in favor of adding this now, because then there's no risk of a user accidentally building around this assumption and making adding it later in a fully backwards compatible way challenging.
There was a problem hiding this comment.
Thanks for your opinion, I wanted to implement it later. Let me see how I can add it now already though.
One thing I want to note is that for agent use cases, I have experienced caching causing issues. If you leave a room and there was an agent dispatched, calling again with caching active will put you in the same room. But because you left before, the agent is being torn down and is also not joining again. So for agent use cases, I think caching should be discouraged with our current logic on dispatch.
There was a problem hiding this comment.
I think the way the syntax we mostly use is the livekit_token_source.development_token_server("id").cached(). I don't see this as a breaking change if we add that later. I will still see if I can add the caching in this PR already.
There was a problem hiding this comment.
I stacked the .cached() implementation against this PR to keep things separate for easier reviews: #1318
ladvoc
left a comment
There was a problem hiding this comment.
Looking clean overall! Just some minor comments and suggestions.
|
Note: before publishing this crate, I need to setup trusted publishing to prevent a recurrence of last week's release issue; on my list, just haven't gotten around to it yet. |
38d82ab to
0ba1275
Compare
0ba1275 to
69e463a
Compare
c5a0f83 to
fc4e8ff
Compare
1egoman
left a comment
There was a problem hiding this comment.
Generally looks good to me!
1837f99 to
084ea8d
Compare
Yes, I definitely think we should integrate it. I am also planning to integrate this in the LiveKit Capture CLI I am currently working on (more details on that to come). |
# This is the 1st commit message: Started with token source # This is the commit message #2: Before http
Before http Moved everything into a modular crate Without borrow seems to work Token source with tokio async Move reqwest into workspace Token fetching from sandbox works TokenSourceEndpoint works the same as sandbox Using composition for sandbox Error status codes are working Remove room mod change Version 1 done, works like Unity First round of review changes done Switching to livekit-net crate instead of reqwest Revert putting reqwest dependency in workspace Remove test errors Renaming to development token server
The versioned_files dependency entry named livekit-net, so releasing livekit-token-source would have rewritten the livekit-net version pin in the workspace Cargo.toml instead of its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…traits Mirrors the JS SDK's TokenSourceFixed / TokenSourceConfigurable split: fixed sources fetch without options, configurable sources take TokenSourceFetchOptions. Endpoint and DevelopmentTokenServer implement Configurable; Literal implements Fixed and now stores the response directly, removing the never-constructed Err arm. This gives generic call sites (a future Room::connect) and custom credential backends a common interface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Matches the JS SDK's TokenSourceFetchOptions field name; the wire mapping (room_config.agents[0].deployment) is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tolerate camelCase responses The documented endpoint contract is snake_case; serde aliases add the same leniency the JS SDK gets from proto3 fromJson, which also accepts camelCase field names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the JS SDK doc comments, including the production-use warning on the development token server, and adds crate-level docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repo convention (livekit, livekit-api) is to ship no TLS in defaults and let consumers opt in; the example now enables rustls-tls-native-roots explicitly and uses the workspace dependency like the other examples. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes the committed live sandbox id in favor of LIVEKIT_SANDBOX_ID and tidies the output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cargo.toml declares readme = "README.md" but the file was empty. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… sandbox id These belong to the base token-source work rather than the caching layer: README and doc links updated for the free-function factories, the README example fixed to the current endpoint() signature, a stale camelCase-alias comment removed, and the real sandbox id scrubbed from the example. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
084ea8d to
0631484
Compare
> [!IMPORTANT] > Merging this pull request will create these releases # livekit-ffi 0.12.75 (2026-08-10) ## Features - Add `other_sdks` field to propagate additional SDK metadata to the server. ## Fixes - Only advertise internal H264 decode formats if the decoder works - #1313 (@MaxHeimbrock) # webrtc-sys 0.3.42 (2026-08-10) ## Fixes - Only advertise internal H264 decode formats if the decoder works - #1313 (@MaxHeimbrock) # livekit-api 0.6.3 (2026-08-10) ## Features - Add `other_sdks` field to propagate additional SDK metadata to the server. # livekit 0.8.3 (2026-08-10) ## Features - Add `other_sdks` field to propagate additional SDK metadata to the server. ## Fixes - Only advertise internal H264 decode formats if the decoder works - #1313 (@MaxHeimbrock) # livekit-token-source 0.1.1 (2026-08-10) ## Features - Add the `livekit-token-source` crate: token sources for procuring LiveKit credentials, mirroring the JS SDK's `TokenSource` — `literal`, `endpoint` (standard token endpoint format), and `development_token_server`, plus `TokenSourceFixed` / `TokenSourceConfigurable` traits for custom backends. ## Fixes - Add a TokenSource crate to the Rust SDKs - #1274 (@MaxHeimbrock) # libwebrtc 0.3.45 (2026-08-10) ## Fixes - Only advertise internal H264 decode formats if the decoder works - #1313 (@MaxHeimbrock) Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com>
Before you submit your PR
Make sure the following is true before submitting your PR:
PR description
This PR adds the Token Source concept as an independent crate.
Testing
Ideally, unit test the code you add, but ensure you're not repeating existing test cases. Use as many already written scaffolding, utilities as possible; write your own, when needed. If external services, APIs, tokens are required (e.g., running an LK server instance), provide the necessary information. Make sure your tests perform useful, context-aware assertions and do not simply emulate "happy paths".
Async
We want the project to be runtime-agnostic, so please reuse what's already in livekit-runtime and feel free to add anything missing. It's ok to use Tokio directly, when writing unit tests, if necessary. When testing, do not use artificial delays for the state to "catch up"; instead, respect the event flow and subscribe properly using channels or other mechanisms.