Skip to content

livekit-token: the agent grant, the kind claim, and no default claims - #1419

Open
theomonnom wants to merge 2 commits into
mainfrom
theo/token-agent-kind
Open

livekit-token: the agent grant, the kind claim, and no default claims#1419
theomonnom wants to merge 2 commits into
mainfrom
theo/token-agent-kind

Conversation

@theomonnom

@theomonnom theomonnom commented Sep 9, 2026

Copy link
Copy Markdown
Member

VideoGrants gains the agent grant and Claims gains the kind claim, with AccessToken::with_kind. The Go, Python and JS SDKs already carry both. livekit-uniffi exposes them too: TokenOptions.kind, Claims.kind, and agent on its VideoGrants record.

Why: an agent worker authenticates to /agent with VideoGrants(agent=True), and a simulated job's participant token is kind: "agent". Neither could be minted from Rust, so livekit/agents-rust was about to hand-roll these claims over jsonwebtoken instead of depending on this crate.

The inferred grants become Option<bool> (breaking). The server infers four grants when absent (protocol/auth/grants.go): canPublish and canSubscribe → true, canUpdateOwnMetadata → false, and canPublishData → whatever canPublish is. The Go and JS SDKs model these as optional; this crate had them as bool with a default_true on the read side, which cannot say "unset" and read an absent canPublishData as true rather than as canPublish. Now None leaves the decision to the server and the getters (can_publish(), can_subscribe(), can_publish_data(), can_update_own_metadata()) read a token the way the server does. Every other default checked against the server's getters matches. No crate in this workspace sets or reads those four fields directly.

No default claims. With the four as Option, VideoGrants derives Default, and nothing at its default is written into the token: every claim and grant carries skip_serializing_if = "is_default", one predicate, no hand-written Serialize. Unset claims (sub, name, kind, metadata, sha256, attributes, roomConfig) and grants at their default (video, sip, and every field in them) are omitted, as the server's own omitempty grants are. Verification of existing tokens is unchanged.

Changeset: major for livekit-token (pre-1.0, so the actual bump is semver-minor), livekit-uniffi (its record shape changed) and livekit-api (it re-exports the crate as livekit_api::access_token, so ^0.1.2 would otherwise miss the release); patch for the internal users livekit, livekit-ffi, livekit-signaling.

Tests: the two new claims round-trip through to_jwt/verify; a token with nothing set carries exactly exp, iss, nbf; {} and VideoGrants::default()/SIPGrants::default()/a bare Claims are the same thing in both directions; an absent canPublishData reads as canPublish. livekit-uniffi, livekit-api and livekit-token-source compile.

🤖 Generated with Claude Code

@theomonnom
theomonnom requested a review from ladvoc as a code owner September 9, 2026 19:33
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Changeset ✓

This PR includes a changeset covering all affected packages:

Package Bump
livekit patch
livekit-api major
livekit-ffi patch
livekit-signaling patch
livekit-token major
livekit-uniffi major

devin-ai-integration[bot]

This comment was marked as resolved.

@theomonnom theomonnom changed the title livekit-token: the agent grant and the kind claim livekit-token: the agent grant, the kind claim, and no empty claims Sep 9, 2026
@theomonnom
theomonnom force-pushed the theo/token-agent-kind branch 2 times, most recently from 74d675c to 90527d4 Compare September 9, 2026 19:44
@theomonnom theomonnom changed the title livekit-token: the agent grant, the kind claim, and no empty claims livekit-token: the agent grant, the kind claim, and no default claims Sep 9, 2026
@theomonnom
theomonnom force-pushed the theo/token-agent-kind branch 2 times, most recently from 5e0f9d8 to 1a5147a Compare September 9, 2026 19:48
devin-ai-integration[bot]

This comment was marked as resolved.

@theomonnom
theomonnom force-pushed the theo/token-agent-kind branch 2 times, most recently from 08186b6 to 8f812bd Compare September 9, 2026 19:55

@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, a few points to address:

  • Since we expose token generation/verification through livekit-uniffi now, you'll need to update there to use the new API.
  • Changeset should specify major as the release type (this is necessary because it is a pre-1.0 release; the actual bump will be semver minor)
  • Changeset also needs to list all crates that depend on this crate (release manager is not dependency aware right now unfortunately)

`VideoGrants` gains `agent` and `Claims` gains `kind` (with `AccessToken::with_kind`),
both of which the Go, Python and JS SDKs already carry. An agent worker authenticates
to `/agent` with `VideoGrants { agent: true }` and a simulated job's participant token
is `kind: "agent"`; neither could be minted from Rust before, so livekit/agents-rust
was about to hand-roll the claims over jsonwebtoken instead of using this crate.

The four grants the server infers when absent -- canPublish, canSubscribe,
canPublishData, canUpdateOwnMetadata (protocol/auth/grants.go) -- become `Option<bool>`,
as in the Go and JS SDKs: `None` leaves the decision to the server, and getters read a
token the way the server does, `canPublishData` falling back to `canPublish` included.
With that, `VideoGrants` derives `Default` and nothing at its default is written into the
token: every claim and grant carries `skip_serializing_if = "is_default"`, the one
predicate, and the token holds only what was set -- as the server's own `omitempty`
grants are. Verification of existing tokens is unchanged; a round-trip test pins that
`{}` and the defaults are the same thing.

`livekit-uniffi` follows: its `VideoGrants` record mirrors the struct field for field,
and `kind` is exposed on `TokenOptions` and `Claims`.
@theomonnom
theomonnom force-pushed the theo/token-agent-kind branch from 8f812bd to ebd80e5 Compare September 9, 2026 20:01
@theomonnom
theomonnom requested a review from ladvoc September 9, 2026 20:12
The livekit integration tests and the data_track_benchmark and local_video
examples still set canPublish / canSubscribe / canPublishData as plain bools;
they are Some(..) now. Cargo.lock picks up the serde_json dev-dependency and
the two new unit tests are rustfmt'd.
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.

2 participants