Skip to Content
DocumentationServer SDK

Server SDK

The @guidekit/server package provides server-side token generation, LLM/STT/TTS proxy routes, and session key storage. API keys never appear in JWT payloads or client bundles.

Installation

npm install @guidekit/server

Quick setup (Next.js App Router)

// app/api/guidekit/token/route.ts import { createNextAppRouterRoutes } from '@guidekit/server/next'; const routes = createNextAppRouterRoutes({ signingSecret: process.env.GUIDEKIT_SECRET!, createTokenOptions: () => ({ llmApiKey: process.env.LLM_API_KEY!, sttApiKey: process.env.STT_API_KEY, ttsApiKey: process.env.TTS_API_KEY, expiresIn: '15m', }), siteKnowledge: { documents: [ { id: 'pricing', title: 'Pricing', content: 'The Pro plan includes guided autonomy and priority support.', metadata: { url: '/pricing' }, }, ], }, }); export const POST = routes.POST_token; // app/api/guidekit/llm/route.ts export const POST = routes.POST_llm; // app/api/guidekit/health/route.ts export const GET = routes.GET_health; // app/api/guidekit/site-search/route.ts export const POST = routes.POST_siteSearch;

Client configuration (zero browser keys)

<GuideKitProvider tokenEndpoint="/api/guidekit/token" proxy={{ llm: '/api/guidekit/llm', health: '/api/guidekit/health' }} llm={{ provider: 'gemini', model: 'gemini-2.5-flash' }} siteKnowledge={{ endpoint: '/api/guidekit/site-search', topK: 5 }} />

Session storage

Provider keys are stored server-side keyed by sessionId (not jti). The JWT contains sessionId, permissions, and expiry — never API keys.

import { InMemorySessionStore, getSessionKeys } from '@guidekit/server'; // Default in-memory store (single instance) const keys = await getSessionKeys('my-session-id'); // Production: Redis adapter import { RedisSessionStore } from '@guidekit/server/redis';

In-memory vs Redis (production guidance)

  • InMemorySessionStore: good for local dev and single-instance deployments.
    • Not suitable for multi-instance or serverless deployments where requests can land on different processes.
  • RedisSessionStore: recommended for production when you run multiple instances or need durability across restarts.

The reference example app (apps/example-nextjs) uses in-memory storage by default. Set REDIS_URL and install ioredis in that app to exercise the Redis path locally.

Example:

import Redis from 'ioredis'; import { RedisSessionStore } from '@guidekit/server/redis'; import { createNextAppRouterRoutes } from '@guidekit/server/next'; const redis = new Redis(process.env.REDIS_URL!); const sessionStore = new RedisSessionStore({ redis }); const routes = createNextAppRouterRoutes({ signingSecret: process.env.GUIDEKIT_SECRET!, sessionStore, createTokenOptions: () => ({ llmApiKey: process.env.LLM_API_KEY!, expiresIn: '15m', allowedOrigins: ['https://yourapp.com'], }), });

Framework-agnostic handler

For non-Next.js runtimes, use createGuideKitHandler:

import { createGuideKitHandler } from '@guidekit/server'; const handler = createGuideKitHandler({ signingSecret: process.env.GUIDEKIT_SECRET!, createTokenOptions: () => ({ llmApiKey: process.env.LLM_API_KEY!, expiresIn: '15m', }), }); // Route dispatch: 'token' | 'llm' | 'health' | 'stt' | 'tts' | 'site-search' export async function onRequest(request: Request, route: string) { return handler(request, route as 'token'); }

Proxy routes

RouteMethodPurpose
/tokenPOSTMint session JWT + store provider keys
/llmPOSTStream LLM requests with server-side key
/sttPOSTMint STT credentials for voice
/ttsPOSTMint TTS credentials for voice
/site-searchPOSTSearch server-backed website knowledge
/healthGETHealth check

All proxy routes require Authorization: Bearer <session-token> except /token. The default token permissions are stt, tts, llm, and site:read.

Rate limiting

createGuideKitHandler applies a sliding-window rate limiter keyed by session ID (when authenticated) or client IP.

Defaults

SettingDefaultMeaning
windowMs60_0001-minute window
maxRequests60Max requests per window per key

When exceeded, the handler returns 429 with a Retry-After header (seconds).

Production recommendations

DeploymentSuggested maxRequestsNotes
Single-tenant internal app60 (default)Fine for most dashboards
Public consumer app30–40Tighten if you see abuse
High-traffic B2B80–120Monitor 429 rates; add CDN/WAF in front

Tune via handler options:

const handler = createGuideKitHandler({ signingSecret: process.env.GUIDEKIT_SECRET!, rateLimit: { windowMs: 60_000, maxRequests: 40, }, createTokenOptions: () => ({ llmApiKey: process.env.LLM_API_KEY!, expiresIn: '15m' }), });

The example app reads GUIDEKIT_RATE_LIMIT_WINDOW_MS and GUIDEKIT_RATE_LIMIT_MAX for local tuning.

Set allowedOrigins when minting tokens. Proxy routes (/llm, /stt, /tts) reject requests when the token includes an aud claim and the Origin header does not match.

createTokenOptions: () => ({ llmApiKey: process.env.LLM_API_KEY!, expiresIn: '15m', allowedOrigins: ['https://yourapp.com'], }),

In the example app, set GUIDEKIT_ALLOWED_ORIGINS=https://yourapp.com,https://staging.yourapp.com.

createSessionToken

import { createSessionToken } from '@guidekit/server'; const result = await createSessionToken({ signingSecret: process.env.GUIDEKIT_SECRET!, sessionId: 'optional-custom-id', llmApiKey: process.env.LLM_API_KEY!, expiresIn: '15m', allowedOrigins: ['https://yourapp.com'], }); // result.token — send to client // result.expiresIn — seconds

Secret rotation

  1. Generate: npx @guidekit/cli generate-secret
  2. Sign with [newSecret, oldSecret] array during rotation window
  3. Validate accepts either secret until old tokens expire
Last updated on