Docs

KeyLM Project Documentation

KeyLM is a hybrid free-tier and BYOK multi-provider chat app built with Next.js App Router, Prisma, and Postgres. This page documents the product flow, backend APIs, and data model in one place.

Overview

A single workspace where users can start on a shared Groq free pool, then move to their own OpenAI, Gemini, or Anthropic keys.

Hybrid Access

New accounts get a shared Groq fallback before switching to personal keys.

Model Catalog

Models are normalized across providers and cached for 24 hours per user.

Streaming Chat

Server-sent events deliver token deltas with stop and retry safety.

Threaded History

Threads persist provider, model, settings, message history, and token usage.

Quick Start

1. Install dependencies

npm install

2. Create the environment file

cp .env.example .env
# set DATABASE_URL, APP_AUTH_SECRET, APP_ENCRYPTION_KEY, GROQ_API_KEY

3. Generate an encryption key

node -e "console.log(Buffer.from(require('crypto').randomBytes(32)).toString('base64'))"

4. Run database migrations

npm run prisma:migrate

5. Start the dev server

npm run dev

Environment

Required variables for local development and production.

DATABASE_URL
Postgres connection string used by Prisma.
APP_AUTH_SECRET
HMAC secret for signing session tokens.
APP_ENCRYPTION_KEY
32-byte base64 key for encrypting provider secrets.
GROQ_API_KEY
Server-only API key used for the shared KeyLM Free Groq pool.
GROQ_BASE_URL
Groq base URL, defaults to https://api.groq.com/openai/v1.
GROQ_FREE_MODEL
Fixed shared free model, defaults to moonshotai/kimi-k2-instruct-0905.
GROQ_FREE_FALLBACK_MODELS
Optional comma-separated Groq fallback models if the primary free model is unavailable.
FREE_USER_DAILY_LIMIT
Per-user daily free request limit, defaults to 50.
FREE_GLOBAL_DAILY_LIMIT
Global daily free request limit, defaults to 1000.
RATE_LIMIT_PER_MINUTE
Optional request limit for chat and password reset endpoints.
PASSWORD_RESET_TTL_MINUTES
Optional TTL for password reset tokens (defaults to 60).

User Flow

  1. Create an account or sign in.
  2. Use KeyLM Free immediately if daily user/global Groq quota is still available.
  3. Add a provider key and validate it with a lightweight request when you want BYOK mode.
  4. Load the model list for connected providers and create BYOK threads.
  5. Send a message and stream responses via SSE.
  6. Persist assistant output, token usage, and continue the thread.
Streaming responses use server-sent events from the messages endpoint, with idempotency on requestId.

Architecture

The app is split into route handlers under src/app/api and reusable services undersrc/lib.

Auth and sessions

Email and password auth with signed, httpOnly session cookies.

Key management

Provider keys are stored encrypted, masked in UI, and audited.

Provider adapters

OpenAI, Gemini, Anthropic, and Groq adapters normalize models, streaming, and usage.

Model service

Model lists are cached per key and refreshed on demand.

Thread service

Threads and messages are persisted with idempotent request IDs.

Free-tier quotas

Per-user and global daily counters gate the shared Groq fallback.

Project Structure

src/app

App Router pages and API route handlers.

src/lib

Core services, providers, crypto, auth, and utilities.

prisma

Database schema and migrations.

src/app/globals.css

Shared theme and component styles.

API Endpoints

Auth

POST

/api/auth/register

Create an account and start a session.

POST

/api/auth/login

Authenticate and start a session.

POST

/api/auth/logout

Clear the session cookie.

GET

/api/auth/me

Return the current session user.

POST

/api/auth/password-reset/request

Create a password reset token.

POST

/api/auth/password-reset/confirm

Finish a password reset.

Provider keys

POST

/api/providers/:provider/keys

Validate and store a new key.

GET

/api/providers/:provider/keys

List keys for a provider.

POST

/api/providers/:provider/keys/:keyId/validate

Re-validate a stored key.

DELETE

/api/providers/:provider/keys/:keyId

Revoke a key.

Models

GET

/api/providers/:provider/models

Return cached models, with optional refresh=true.

POST

/api/providers/:provider/models/refresh

Force a model refresh and update cache.

Free usage

GET

/api/usage/free

Return the current user/global Groq free quota snapshot.

Threads and messages

POST

/api/threads

Create a BYOK or KeyLM Free thread.

GET

/api/threads

List threads for the user.

GET

/api/threads/:threadId

Get a thread and its messages.

DELETE

/api/threads/:threadId

Delete a thread.

POST

/api/threads/:threadId/messages

Send a message, stream SSE deltas, and persist token usage.

Data Model

User

id, email, passwordHash, createdAt

ProviderKey

provider, keyCiphertext, keyMask, status, lastValidatedAt, lastUsedAt

ProviderModelCache

provider, keyId, models, fetchedAt, expiresAt

Thread

provider, model, systemPrompt, settings, status, updatedAt

Message

threadId, role, content, providerMessageId, clientRequestId, metadata.usage

AuditLog

action, provider, keyId, metadata, createdAt

PasswordResetToken

tokenHash, expiresAt, usedAt

UserDailyFreeUsage

userId, day, count

GlobalDailyFreeUsage

day, count

UX Behavior

Security

Edge Cases

Testing

Roadmap