Skip to content

Add a TokenSource crate to the Rust SDKs - #1274

Merged
ladvoc merged 25 commits into
mainfrom
max/token-source
Aug 10, 2026
Merged

Add a TokenSource crate to the Rust SDKs #1274
ladvoc merged 25 commits into
mainfrom
max/token-source

Conversation

@MaxHeimbrock

Copy link
Copy Markdown
Contributor

Before you submit your PR

Make sure the following is true before submitting your PR:

  • [ x ] I have read the contributing guidelines and validated that this PR will be accepted.
  • [ x ] I have read and followed the principles regarding breaking changes, testing, and code quality.

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.

@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.

Left some initial comments, will return for a more thorough review when this is taken out of draft.

Comment thread livekit-token-source/src/request.rs
Comment thread livekit-token-source/Cargo.toml Outdated
Comment thread livekit-token-source/src/token_source.rs Outdated

@1egoman 1egoman 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.

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.

Comment thread livekit-token-source/src/request.rs
Comment thread livekit-token-source/src/token_source.rs Outdated
Comment thread livekit-token-source/src/token_source.rs Outdated
Comment thread livekit-token-source/src/token_source.rs Outdated
Comment thread livekit-token-source/src/token_source.rs Outdated
Comment thread examples/token_source/src/main.rs Outdated
@MaxHeimbrock

Copy link
Copy Markdown
Contributor Author

Potential follow up: Mint tokens based on API secret

@MaxHeimbrock

Copy link
Copy Markdown
Contributor Author

@ladvoc this thing is already minting tokens from api key and api secret right? Are we using this in the rust-dev-client?

@MaxHeimbrock
MaxHeimbrock force-pushed the max/token-source branch 4 times, most recently from f690b60 to e63f0ef Compare August 3, 2026 10:09
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Changeset ✓

This PR includes a changeset covering all affected packages:

Package Bump
livekit-token-source minor

@MaxHeimbrock
MaxHeimbrock marked this pull request as ready for review August 5, 2026 11:35
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@1egoman 1egoman 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.

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.

Comment thread examples/token_source/src/main.rs Outdated
Comment thread examples/token_source/src/main.rs
Comment thread examples/token_source/src/main.rs Outdated
Comment thread livekit-token-source/src/request.rs
Comment thread livekit-token-source/src/response.rs Outdated
Comment on lines +131 to +133
let response = http_client.post(self.endpoint_url.clone(), headers, body).await?;

if !(200..300).contains(&response.status) {

@1egoman 1egoman Aug 5, 2026

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.

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.

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.

Maybe you can draft it?

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.

I can do it as a follow up, np.

Comment thread livekit-token-source/tests/mock_http.rs
Comment thread livekit-token-source/README.md Outdated
Comment thread examples/token_source/src/main.rs
Comment on lines +105 to +118
/// 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);

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.

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.

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.

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.

@MaxHeimbrock MaxHeimbrock Aug 6, 2026

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.

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.

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.

I stacked the .cached() implementation against this PR to keep things separate for easier reviews: #1318

@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.

Looking clean overall! Just some minor comments and suggestions.

Comment thread livekit-token-source/tests/mock_http.rs
Comment thread examples/token_source/src/main.rs Outdated
Comment thread livekit-token-source/src/response.rs Outdated
Comment thread livekit-token-source/src/request.rs Outdated
Comment thread livekit-token-source/src/request.rs
Comment thread livekit-token-source/src/token_source.rs
Comment thread livekit-token-source/src/token_source.rs
Comment thread livekit-token-source/src/token_source.rs Outdated
@ladvoc

ladvoc commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@1egoman 1egoman 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!

Comment thread examples/token_source/token.json
Comment thread livekit-token-source/README.md Outdated
@MaxHeimbrock
MaxHeimbrock force-pushed the max/token-source branch 2 times, most recently from 1837f99 to 084ea8d Compare August 7, 2026 10:01
@ladvoc

ladvoc commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@ladvoc this thing is already minting tokens from api key and api secret right? Are we using this in the rust-dev-client?

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
MaxHeimbrock and others added 23 commits August 10, 2026 10:52
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>
@ladvoc
ladvoc merged commit c5f92db into main Aug 10, 2026
24 of 25 checks passed
@ladvoc
ladvoc deleted the max/token-source branch August 10, 2026 18:30
@knope-bot knope-bot Bot mentioned this pull request Aug 10, 2026
ladvoc pushed a commit that referenced this pull request Aug 10, 2026
> [!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>
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.

3 participants