This file contains important patterns and guidelines for AI agents working on this codebase.
apps/
web/ # TanStack Start frontend (Vite + TanStack Router)
server/ # Convex backend
The app uses TanStack Router with file-based routing in apps/web/src/routes/:
| Route | File | Purpose |
|---|---|---|
/ |
index.tsx |
Home / New chat |
/c/$chatId |
c/$chatId.tsx |
Chat conversation |
/settings |
settings.tsx |
User settings |
/auth/sign-in |
auth/sign-in.tsx |
Sign in page |
/auth/callback |
auth/callback.tsx |
OAuth callback |
/openrouter/callback |
openrouter/callback.tsx |
OpenRouter OAuth |
/api/chat |
api/chat.ts |
Chat streaming API |
- Frontend: TanStack Start (Vite), TanStack Router, React 19, TypeScript, Tailwind CSS
- Backend: Convex (real-time database)
- Auth: Better Auth with GitHub OAuth, synced to Convex
- AI: OpenRouter (via AI SDK 5) with streaming responses
- Analytics: PostHog, Vercel Analytics
- UI: shadcn/ui components
- User signs in with GitHub via Better Auth
- Better Auth session stored in cookies
users.ensuremutation syncs user to Convex (by externalId)- All queries use Convex user ID (not Better Auth ID)
// Get Better Auth user
const { user } = useAuth()
// Get Convex user ID for queries
const convexUser = useQuery(api.users.getByExternalId, { externalId: user?.id })
const convexUserId = convexUser?._id- OSSChat Cloud (default): Free tier using server's
OPENROUTER_API_KEY, 10¢/day limit - Personal OpenRouter: User's own API key for unlimited access
Uses AI SDK 5 with OpenRouter:
// apps/web/src/routes/api/chat.ts
import { createOpenRouter } from '@openrouter/ai-sdk-provider'
import { streamText } from 'ai'
const openrouter = createOpenRouter({ apiKey })
const result = streamText({ model: openrouter(modelId), messages })Models fetched from OpenRouter API (https://openrouter.ai/api/v1/models) with 4-hour cache.
Pricing loaded dynamically for cost tracking.
// apps/web/src/providers/convex.tsx
const convexUrl = import.meta.env.VITE_CONVEX_URL
const convexClient = convexUrl ? new ConvexReactClient(convexUrl) : null// apps/web/src/lib/auth-client.ts
import { createAuthClient } from 'better-auth/react'
export const authClient = createAuthClient({
baseURL: import.meta.env.VITE_CONVEX_SITE_URL,
})TanStack Start uses Vite, so client-side env vars use VITE_ prefix:
VITE_CONVEX_URL- Convex deployment URLVITE_CONVEX_SITE_URL- Convex HTTP actions URL (for auth)VITE_POSTHOG_KEY- PostHog analytics key
Server-side env vars (in API routes):
OPENROUTER_API_KEY- Server's OpenRouter key for free tierVALYU_API_KEY- Web search API key
stores/model.ts- Model selection, favorites, cached model liststores/provider.ts- Active provider (osschat/openrouter), usage trackingstores/openrouter.ts- Personal OpenRouter API key (encrypted in localStorage)stores/ui.ts- Sidebar state, theme
// stores/provider.ts
const { dailyUsageCents, addUsage, isOverLimit } = useProviderStore()