new Roadie({ apiKey: 'rd_sk_…' }) in a
browser-like environment throws immediately.
What a client token carries
A client token is signed by Roadie (ES256) and encodes the tenant and the end user it acts as:sub— the end-user id. Every request the token makes is attributed to this user and enforced against that user’s limits.plan— the end user’s effective entitlement tier, which drives per-plan policy.scopes— which endpoints the token may call:ai.chat,ai.embed,ai.images,feedback. A token requests a subset of these at mint (the default is all four).org/proj/env— the tenant it belongs to.
The SDK surface
A client app never handles JWTs directly. You give the SDK atokenProvider and it caches and
refreshes the token (single-flighting concurrent refreshes, refreshing at ~80% of the token’s
TTL):
Path A — mint from your backend
If you already have any backend, mint tokens there with your secret key and hand them to your app. Your server calls the mint endpoint:scopes (a subset of the four) and an attributes object (≤4 KB) stored on the
end user. Your tokenProvider fetches from your route; the SDK does the rest.
Path B — federated identity, no backend (the differentiator)
If your app already authenticates users with Firebase, Supabase, Auth0, Cognito, or any OIDC provider, Roadie can exchange that provider’s ID token for a client token — no customer backend required. You configure the issuer, JWKS URL, audience, and claim mapping per project in the dashboard; the app sends its existing ID token plus your publishable key to the same mint endpoint:federatedTokenProvider helper for exactly this exchange:
A known-public issuer (Firebase, Auth0, Cognito, …) requires an audience in its federated
config. Without one, any token that shared IdP mints for any app would be accepted — the
confused-deputy trap — so the dashboard refuses to save such a config. For a full walkthrough,
see the zero-backend federated identity tutorial.
Device attestation — no accounts at all
For iOS apps with no user accounts, a device can mint a client token by proving it is a genuine build on real hardware via Apple App Attest — using a publishable key and a three-step flow:POST /v1/device/challenge— the gateway issues a single-use challenge (valid ~5 minutes).POST /v1/device/attest— first launch: the app returns its App Attest attestation over the challenge. The gateway verifies it against Apple’s pinned root, stores the device’s public key, and mints a token for a per-device end user (device:<hash>).POST /v1/device/assert— a returning device proves liveness with an assertion (a signature over a fresh challenge, plus a strictly increasing counter that defeats replay), and mints again.
The plan is decided at the server, not the client
Whatever plan a client or IdP asserts, the mint uses the effective entitlement Roadie holds for that end user when one exists — the developer-authoritative tier always wins. A client or device can never raise its own tier by asserting a higher plan; the asserted value is only a fallback when no entitlement is on record. This is enforced in the one shared mint path that all three routes flow through. See End users & entitlements.Why this is safe
- Tokens are short-lived and scoped to one end user’s limits — the blast radius of a leaked token is exactly that user’s quota.
- A leaked publishable key alone mints nothing: minting requires a valid federated identity token (Path B) or a genuine device attestation.
- A client token can never mint another token — the mint routes reject client-token callers.
- Minting is rate-limited per key and per client IP.
- Per-end-user and per-IP rate limits apply regardless of token validity.
Cutting off a compromised session
To stop a compromised session immediately, block the end user: the gateway’s fast denylist rejects that user’s requests within a second, across every gateway instance. Because a client token lives at most an hour, access also lapses on its own as the token expires. There is no need to track or revoke individual tokens.Client tokens are available now. If you are coming from a server-only integration, Path A is
three lines; Path B and device attestation let you ship with no backend at all.