Auth & session tokens
Every partner integration ends up calling kernel-api with a short-lived
cbv1.* session token. There are two ways to get that token into the
browser, and most partners only need one of them:
| Flow | Use when | Where the partner’s identity lives | Refresh |
|---|---|---|---|
Browser flow (keyId) | Pure-frontend deploys: GitHub Pages, S3+CloudFront, Vercel static, Netlify, partner storefronts that don’t have a backend. | The public half of the partner key (<keyId>) is in the bundle. The secret half stays out of the browser entirely. | The SDK auto-mints + auto-refreshes. |
Server flow (sessionToken) | Partners that already have a backend they trust to hold secrets. | The full cbsdk_<keyId>_<secret> is on the partner’s server. | The partner’s server mints, the partner manages refresh. |
The two flows hit the same POST /api/v1/sdk/session-tokens endpoint
and produce the same cbv1.* token. The only difference is how the
caller proves it may mint.
Provisioning a partner key
In hub, open Settings → SDK API keys → Generate key:
- Label — human-readable, e.g. “Acme storefront — prod”.
- Allowed origins — exact match list. No wildcards. Add every
origin you plan to embed from, including
http://localhost:3007for local dev. The browser-sentOriginheader is checked against this list both at mint time and against the cbv1originclaim on every kernel-api request. - Allowed projects — slugs from your CADbuildr project list. Tokens can only be minted for projects in this list.
- Default / max TTL — defaults to 30min / 2h.
- Per-token render budget — optional integer cap on renders
served per
jti. Enforced via Redis. Null = unlimited.
The plaintext key is shown once at create time as
cbsdk_<keyId>_<secret>. The two halves serve different purposes:
<keyId>(e.g.7ef7b4133ef1e6c2) — publishable. Safe to ship in browser bundles; it identifies the partner but cannot mint a token by itself. It’s the half the browser flow uses._<secret>— server-only. Combined with<keyId>proves the caller IS the partner. Used by the server flow.
Browser flow (keyId)
Best for purely static deployments. The SDK contacts hub on mount, mints a token, refreshes it before expiry.
import { CadbuildrProvider, CadbuildrViewer } from "@cadbuildr/sdk-react";
export function App() {
return (
<CadbuildrProvider
keyId="7ef7b4133ef1e6c2" // publishable half — safe in the bundle
projectKey="lego"
>
<CadbuildrViewer dag={dag} />
</CadbuildrProvider>
);
}Under the hood the SDK fires:
POST https://hub.cadbuildr.com/api/v1/sdk/session-tokens
Origin: https://store.acme.com ← attached by the browser
Content-Type: application/json
{ "keyId": "7ef7b4133ef1e6c2", "projectId": "lego" }Hub looks up the SDK key by keyId, verifies the Origin is in the
key’s allowed_origins, and mints a cbv1.* scoped to that origin.
The request has no Authorization header — origin allowlisting is
the only proof of identity the browser can offer, and it’s enough
because:
- The browser auto-attaches
Originon every cross-origin fetch and JS can’t forge it. - The allowlist is per-key; another site embedding your
keyIdwould fail the Origin check unless its origin is also on your allowlist. - Per-token TTL + per-
jtibudget + per-keyId rate limits keep scrape attempts cheap.
The <CadbuildrProvider> exposes the current mint state via
useCadbuildrContext().tokenStatus:
type SessionTokenStatus =
| { state: "loading" } // first mount, or mid-refresh
| { state: "ready"; token: string; expiresAt: number }
| { state: "error"; error: string } // hub unreachable / 403 / etc.
| { state: "provided"; token: string } // partner passed sessionToken insteadUse it to render a loading skeleton or surface a helpful error to the
end-user. Call refreshSessionToken() to force a re-mint (e.g. after a
401 token_expired from kernel-api in your own fetch path).
Configuration
| Prop | Default | Notes |
|---|---|---|
keyId | required for browser flow | The <keyId> half of your cbsdk_*. |
projectKey | optional | Sent as projectId on mint; must be in the key’s allowed_projects. |
hubBaseUrl | https://hub.cadbuildr.com | Override to point at a staging hub if CADbuildr has provided one. |
baseUrl | https://kernel-api.cadbuildr.com | kernel-api endpoint the cbv1 is sent to. |
Server flow (sessionToken)
Best when you already have a backend you want to keep CADbuildr auth on. Your backend mints, your bundle just receives the token.
POST https://hub.cadbuildr.com/api/v1/sdk/session-tokens
Authorization: Bearer cbsdk_acme_5f8a… ← full secret
Content-Type: application/json
{
"projectId": "lego",
"origin": "https://store.acme.com",
"endUserId": "anon-7a3c…", // optional, for analytics
"ttlSeconds": 1800
}{
"token": "cbv1.eyJzdWIiOiJhbm9uLTdhM2Mi…",
"expiresAt": 1779433200,
"mode": "secret"
}The browser passes the token through to the SDK:
<CadbuildrProvider
sessionToken={tokenFromYourBackend}
projectKey="lego"
>
<CadbuildrViewer dag={dag} />
</CadbuildrProvider>In server-flow mode the SDK does not refresh tokens — your backend
owns the lifecycle. When kernel-api returns 401 token_expired, ask
your backend for a fresh token and pass the new value.
The cbv1.* payload
{
"sub": "anon-7a3c",
"iss": "hub",
"aud": "kernel-api",
"partner": "<partner-id>",
"project": "lego",
"origin": "https://store.acme.com",
"jti": "01HZ7C…",
"budget": 500,
"iat": 1779431400,
"exp": 1779433200
}aud is hardcoded to kernel-api; tokens minted for other audiences are
rejected. origin must exactly match the browser’s Origin header on
every request. jti is unique per token and is the rate-limit /
revocation key in Redis.
Error responses
| HTTP | code | Meaning |
|---|---|---|
| 400 | — | Browser flow with no Origin header (likely a cURL call missing the flag). |
| 401 | — | Missing both auth modes, or the cbsdk secret didn’t match the key hash. |
| 403 | — | origin not in allowed_origins, or projectId not in allowed_projects. |
| 422 | — | ttlSeconds out of bounds, or browser flow’s body.origin disagrees with the Origin header. |
Local development
For the browser flow, keep your keyId in an env var and let the SDK
talk to the default production hub:
# .env.local
VITE_CADBUILDR_SDK_KEY_ID=7ef7b4133ef1e6c2then in code:
<CadbuildrProvider
keyId={import.meta.env.VITE_CADBUILDR_SDK_KEY_ID}
projectKey="lego"
>
…
</CadbuildrProvider>Add your local dev origin — e.g. http://localhost:3000 (or whatever
port your dev server uses) — to the SDK key’s allowed_origins so the
mint call accepts the request. If CADbuildr has provided you a staging
hub, point at it with the hubBaseUrl prop.
For the server flow, mint a token once and bake it as
VITE_CADBUILDR_SESSION_TOKEN. This is fine for short iterations but
the token expires every 30 min; the browser flow doesn’t have that
limitation.