diff --git a/docs-site/ai-tools/claude-code.mdx b/docs-site/ai-tools/claude-code.mdx
deleted file mode 100644
index bdc4e04b..00000000
--- a/docs-site/ai-tools/claude-code.mdx
+++ /dev/null
@@ -1,76 +0,0 @@
----
-title: "Claude Code setup"
-description: "Configure Claude Code for your documentation workflow"
-icon: "asterisk"
----
-
-Claude Code is Anthropic's official CLI tool. This guide will help you set up Claude Code to help you write and maintain your documentation.
-
-## Prerequisites
-
-- Active Claude subscription (Pro, Max, or API access)
-
-## Setup
-
-1. Install Claude Code globally:
-
- ```bash
- npm install -g @anthropic-ai/claude-code
-```
-
-2. Navigate to your docs directory.
-3. (Optional) Add the `CLAUDE.md` file below to your project.
-4. Run `claude` to start.
-
-## Create `CLAUDE.md`
-
-Create a `CLAUDE.md` file at the root of your documentation repository to train Claude Code on your specific documentation standards:
-
-````markdown
-# Mintlify documentation
-
-## Working relationship
-- You can push back on ideas-this can lead to better documentation. Cite sources and explain your reasoning when you do so
-- ALWAYS ask for clarification rather than making assumptions
-- NEVER lie, guess, or make up information
-
-## Project context
-- Format: MDX files with YAML frontmatter
-- Config: docs.json for navigation, theme, settings
-- Components: Mintlify components
-
-## Content strategy
-- Document just enough for user success - not too much, not too little
-- Prioritize accuracy and usability of information
-- Make content evergreen when possible
-- Search for existing information before adding new content. Avoid duplication unless it is done for a strategic reason
-- Check existing patterns for consistency
-- Start by making the smallest reasonable changes
-
-## Frontmatter requirements for pages
-- title: Clear, descriptive page title
-- description: Concise summary for SEO/navigation
-
-## Writing standards
-- Second-person voice ("you")
-- Prerequisites at start of procedural content
-- Test all code examples before publishing
-- Match style and formatting of existing pages
-- Include both basic and advanced use cases
-- Language tags on all code blocks
-- Alt text on all images
-- Relative paths for internal links
-
-## Git workflow
-- NEVER use --no-verify when committing
-- Ask how to handle uncommitted changes before starting
-- Create a new branch when no clear branch exists for changes
-- Commit frequently throughout development
-- NEVER skip or disable pre-commit hooks
-
-## Do not
-- Skip frontmatter on any MDX file
-- Use absolute URLs for internal links
-- Include untested code examples
-- Make assumptions - always ask for clarification
-````
diff --git a/docs-site/ai-tools/cursor.mdx b/docs-site/ai-tools/cursor.mdx
deleted file mode 100644
index fbb77616..00000000
--- a/docs-site/ai-tools/cursor.mdx
+++ /dev/null
@@ -1,420 +0,0 @@
----
-title: "Cursor setup"
-description: "Configure Cursor for your documentation workflow"
-icon: "arrow-pointer"
----
-
-Use Cursor to help write and maintain your documentation. This guide shows how to configure Cursor for better results on technical writing tasks and using Mintlify components.
-
-## Prerequisites
-
-- Cursor editor installed
-- Access to your documentation repository
-
-## Project rules
-
-Create project rules that all team members can use. In your documentation repository root:
-
-```bash
-mkdir -p .cursor
-```
-
-Create `.cursor/rules.md`:
-
-````markdown
-# Mintlify technical writing rule
-
-You are an AI writing assistant specialized in creating exceptional technical documentation using Mintlify components and following industry-leading technical writing practices.
-
-## Core writing principles
-
-### Language and style requirements
-
-- Use clear, direct language appropriate for technical audiences
-- Write in second person ("you") for instructions and procedures
-- Use active voice over passive voice
-- Employ present tense for current states, future tense for outcomes
-- Avoid jargon unless necessary and define terms when first used
-- Maintain consistent terminology throughout all documentation
-- Keep sentences concise while providing necessary context
-- Use parallel structure in lists, headings, and procedures
-
-### Content organization standards
-
-- Lead with the most important information (inverted pyramid structure)
-- Use progressive disclosure: basic concepts before advanced ones
-- Break complex procedures into numbered steps
-- Include prerequisites and context before instructions
-- Provide expected outcomes for each major step
-- Use descriptive, keyword-rich headings for navigation and SEO
-- Group related information logically with clear section breaks
-
-### User-centered approach
-
-- Focus on user goals and outcomes rather than system features
-- Anticipate common questions and address them proactively
-- Include troubleshooting for likely failure points
-- Write for scannability with clear headings, lists, and white space
-- Include verification steps to confirm success
-
-## Mintlify component reference
-
-### Callout components
-
-#### Note - Additional helpful information
-
-
-Supplementary information that supports the main content without interrupting flow
-
-
-#### Tip - Best practices and pro tips
-
-
-Expert advice, shortcuts, or best practices that enhance user success
-
-
-#### Warning - Important cautions
-
-
-Critical information about potential issues, breaking changes, or destructive actions
-
-
-#### Info - Neutral contextual information
-
-
-Background information, context, or neutral announcements
-
-
-#### Check - Success confirmations
-
-
-Positive confirmations, successful completions, or achievement indicators
-
-
-### Code components
-
-#### Single code block
-
-Example of a single code block:
-
-```javascript config.js
-const apiConfig = {
- baseURL: 'https://api.example.com',
- timeout: 5000,
- headers: {
- 'Authorization': `Bearer ${process.env.API_TOKEN}`
- }
-};
-```
-
-#### Code group with multiple languages
-
-Example of a code group:
-
-
-```javascript Node.js
-const response = await fetch('/api/endpoint', {
- headers: { Authorization: `Bearer ${apiKey}` }
-});
-```
-
-```python Python
-import requests
-response = requests.get('/api/endpoint',
- headers={'Authorization': f'Bearer {api_key}'})
-```
-
-```curl cURL
-curl -X GET '/api/endpoint' \
- -H 'Authorization: Bearer YOUR_API_KEY'
-```
-
-
-#### Request/response examples
-
-Example of request/response documentation:
-
-
-```bash cURL
-curl -X POST 'https://api.example.com/users' \
- -H 'Content-Type: application/json' \
- -d '{"name": "John Doe", "email": "john@example.com"}'
-```
-
-
-
-```json Success
-{
- "id": "user_123",
- "name": "John Doe",
- "email": "john@example.com",
- "created_at": "2024-01-15T10:30:00Z"
-}
-```
-
-
-### Structural components
-
-#### Steps for procedures
-
-Example of step-by-step instructions:
-
-
-
- Run `npm install` to install required packages.
-
-
- Verify installation by running `npm list`.
-
-
-
-
- Create a `.env` file with your API credentials.
-
- ```bash
- API_KEY=your_api_key_here
- ```
-
-
- Never commit API keys to version control.
-
-
-
-
-#### Tabs for alternative content
-
-Example of tabbed content:
-
-
-
- ```bash
- brew install node
- npm install -g package-name
- ```
-
-
-
- ```powershell
- choco install nodejs
- npm install -g package-name
- ```
-
-
-
- ```bash
- sudo apt install nodejs npm
- npm install -g package-name
- ```
-
-
-
-#### Accordions for collapsible content
-
-Example of accordion groups:
-
-
-
- - **Firewall blocking**: Ensure ports 80 and 443 are open
- - **Proxy configuration**: Set HTTP_PROXY environment variable
- - **DNS resolution**: Try using 8.8.8.8 as DNS server
-
-
-
- ```javascript
- const config = {
- performance: { cache: true, timeout: 30000 },
- security: { encryption: 'AES-256' }
- };
- ```
-
-
-
-### Cards and columns for emphasizing information
-
-Example of cards and card groups:
-
-
-Complete walkthrough from installation to your first API call in under 10 minutes.
-
-
-
-
- Learn how to authenticate requests using API keys or JWT tokens.
-
-
-
- Understand rate limits and best practices for high-volume usage.
-
-
-
-### API documentation components
-
-#### Parameter fields
-
-Example of parameter documentation:
-
-
-Unique identifier for the user. Must be a valid UUID v4 format.
-
-
-
-User's email address. Must be valid and unique within the system.
-
-
-
-Maximum number of results to return. Range: 1-100.
-
-
-
-Bearer token for API authentication. Format: `Bearer YOUR_API_KEY`
-
-
-#### Response fields
-
-Example of response field documentation:
-
-
-Unique identifier assigned to the newly created user.
-
-
-
-ISO 8601 formatted timestamp of when the user was created.
-
-
-
-List of permission strings assigned to this user.
-
-
-#### Expandable nested fields
-
-Example of nested field documentation:
-
-
-Complete user object with all associated data.
-
-
-
- User profile information including personal details.
-
-
-
- User's first name as entered during registration.
-
-
-
- URL to user's profile picture. Returns null if no avatar is set.
-
-
-
-
-
-
-### Media and advanced components
-
-#### Frames for images
-
-Wrap all images in frames:
-
-
-
-
-
-
-
-
-
-#### Videos
-
-Use the HTML video element for self-hosted video content:
-
-
-
-Embed YouTube videos using iframe elements:
-
-
-
-#### Tooltips
-
-Example of tooltip usage:
-
-
-API
-
-
-#### Updates
-
-Use updates for changelogs:
-
-
-## New features
-- Added bulk user import functionality
-- Improved error messages with actionable suggestions
-
-## Bug fixes
-- Fixed pagination issue with large datasets
-- Resolved authentication timeout problems
-
-
-## Required page structure
-
-Every documentation page must begin with YAML frontmatter:
-
-```yaml
----
-title: "Clear, specific, keyword-rich title"
-description: "Concise description explaining page purpose and value"
----
-```
-
-## Content quality standards
-
-### Code examples requirements
-
-- Always include complete, runnable examples that users can copy and execute
-- Show proper error handling and edge case management
-- Use realistic data instead of placeholder values
-- Include expected outputs and results for verification
-- Test all code examples thoroughly before publishing
-- Specify language and include filename when relevant
-- Add explanatory comments for complex logic
-- Never include real API keys or secrets in code examples
-
-### API documentation requirements
-
-- Document all parameters including optional ones with clear descriptions
-- Show both success and error response examples with realistic data
-- Include rate limiting information with specific limits
-- Provide authentication examples showing proper format
-- Explain all HTTP status codes and error handling
-- Cover complete request/response cycles
-
-### Accessibility requirements
-
-- Include descriptive alt text for all images and diagrams
-- Use specific, actionable link text instead of "click here"
-- Ensure proper heading hierarchy starting with H2
-- Provide keyboard navigation considerations
-- Use sufficient color contrast in examples and visuals
-- Structure content for easy scanning with headers and lists
-
-## Component selection logic
-
-- Use **Steps** for procedures and sequential instructions
-- Use **Tabs** for platform-specific content or alternative approaches
-- Use **CodeGroup** when showing the same concept in multiple programming languages
-- Use **Accordions** for progressive disclosure of information
-- Use **RequestExample/ResponseExample** specifically for API endpoint documentation
-- Use **ParamField** for API parameters, **ResponseField** for API responses
-- Use **Expandable** for nested object properties or hierarchical information
-````
diff --git a/docs-site/ai-tools/windsurf.mdx b/docs-site/ai-tools/windsurf.mdx
deleted file mode 100644
index fce12bfd..00000000
--- a/docs-site/ai-tools/windsurf.mdx
+++ /dev/null
@@ -1,96 +0,0 @@
----
-title: "Windsurf setup"
-description: "Configure Windsurf for your documentation workflow"
-icon: "water"
----
-
-Configure Windsurf's Cascade AI assistant to help you write and maintain documentation. This guide shows how to set up Windsurf specifically for your Mintlify documentation workflow.
-
-## Prerequisites
-
-- Windsurf editor installed
-- Access to your documentation repository
-
-## Workspace rules
-
-Create workspace rules that provide Windsurf with context about your documentation project and standards.
-
-Create `.windsurf/rules.md` in your project root:
-
-````markdown
-# Mintlify technical writing rule
-
-## Project context
-
-- This is a documentation project on the Mintlify platform
-- We use MDX files with YAML frontmatter
-- Navigation is configured in `docs.json`
-- We follow technical writing best practices
-
-## Writing standards
-
-- Use second person ("you") for instructions
-- Write in active voice and present tense
-- Start procedures with prerequisites
-- Include expected outcomes for major steps
-- Use descriptive, keyword-rich headings
-- Keep sentences concise but informative
-
-## Required page structure
-
-Every page must start with frontmatter:
-
-```yaml
----
-title: "Clear, specific title"
-description: "Concise description for SEO and navigation"
----
-```
-
-## Mintlify components
-
-### Callouts
-
-- `` for helpful supplementary information
-- `` for important cautions and breaking changes
-- `` for best practices and expert advice
-- `` for neutral contextual information
-- `` for success confirmations
-
-### Code examples
-
-- When appropriate, include complete, runnable examples
-- Use `` for multiple language examples
-- Specify language tags on all code blocks
-- Include realistic data, not placeholders
-- Use `` and `` for API docs
-
-### Procedures
-
-- Use `` component for sequential instructions
-- Include verification steps with `` components when relevant
-- Break complex procedures into smaller steps
-
-### Content organization
-
-- Use `` for platform-specific content
-- Use `` for progressive disclosure
-- Use `` and `` for highlighting content
-- Wrap images in `` components with descriptive alt text
-
-## API documentation requirements
-
-- Document all parameters with ``
-- Show response structure with ``
-- Include both success and error examples
-- Use `` for nested object properties
-- Always include authentication examples
-
-## Quality standards
-
-- Test all code examples before publishing
-- Use relative paths for internal links
-- Include alt text for all images
-- Ensure proper heading hierarchy (start with h2)
-- Check existing patterns for consistency
-````
diff --git a/docs-site/api-reference/endpoint/create.mdx b/docs-site/api-reference/endpoint/create.mdx
deleted file mode 100644
index 5689f1b6..00000000
--- a/docs-site/api-reference/endpoint/create.mdx
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'Create Plant'
-openapi: 'POST /plants'
----
diff --git a/docs-site/api-reference/endpoint/delete.mdx b/docs-site/api-reference/endpoint/delete.mdx
deleted file mode 100644
index 657dfc87..00000000
--- a/docs-site/api-reference/endpoint/delete.mdx
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'Delete Plant'
-openapi: 'DELETE /plants/{id}'
----
diff --git a/docs-site/api-reference/endpoint/get.mdx b/docs-site/api-reference/endpoint/get.mdx
deleted file mode 100644
index 56aa09ec..00000000
--- a/docs-site/api-reference/endpoint/get.mdx
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'Get Plants'
-openapi: 'GET /plants'
----
diff --git a/docs-site/api-reference/endpoint/webhook.mdx b/docs-site/api-reference/endpoint/webhook.mdx
deleted file mode 100644
index 32913402..00000000
--- a/docs-site/api-reference/endpoint/webhook.mdx
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'New Plant'
-openapi: 'WEBHOOK /plant/webhook'
----
diff --git a/docs-site/api-reference/introduction.mdx b/docs-site/api-reference/introduction.mdx
deleted file mode 100644
index c835b78b..00000000
--- a/docs-site/api-reference/introduction.mdx
+++ /dev/null
@@ -1,33 +0,0 @@
----
-title: 'Introduction'
-description: 'Example section for showcasing API endpoints'
----
-
-
- If you're not looking to build API reference documentation, you can delete
- this section by removing the api-reference folder.
-
-
-## Welcome
-
-There are two ways to build API documentation: [OpenAPI](https://mintlify.com/docs/api-playground/openapi/setup) and [MDX components](https://mintlify.com/docs/api-playground/mdx/configuration). For the starter kit, we are using the following OpenAPI specification.
-
-
- View the OpenAPI specification file
-
-
-## Authentication
-
-All API endpoints are authenticated using Bearer tokens and picked up from the specification file.
-
-```json
-"security": [
- {
- "bearerAuth": []
- }
-]
-```
diff --git a/docs-site/api-reference/openapi.json b/docs-site/api-reference/openapi.json
deleted file mode 100644
index da5326ef..00000000
--- a/docs-site/api-reference/openapi.json
+++ /dev/null
@@ -1,217 +0,0 @@
-{
- "openapi": "3.1.0",
- "info": {
- "title": "OpenAPI Plant Store",
- "description": "A sample API that uses a plant store as an example to demonstrate features in the OpenAPI specification",
- "license": {
- "name": "MIT"
- },
- "version": "1.0.0"
- },
- "servers": [
- {
- "url": "http://sandbox.mintlify.com"
- }
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "paths": {
- "/plants": {
- "get": {
- "description": "Returns all plants from the system that the user has access to",
- "parameters": [
- {
- "name": "limit",
- "in": "query",
- "description": "The maximum number of results to return",
- "schema": {
- "type": "integer",
- "format": "int32"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "Plant response",
- "content": {
- "application/json": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Plant"
- }
- }
- }
- }
- },
- "400": {
- "description": "Unexpected error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Error"
- }
- }
- }
- }
- }
- },
- "post": {
- "description": "Creates a new plant in the store",
- "requestBody": {
- "description": "Plant to add to the store",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/NewPlant"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "plant response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Plant"
- }
- }
- }
- },
- "400": {
- "description": "unexpected error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Error"
- }
- }
- }
- }
- }
- }
- },
- "/plants/{id}": {
- "delete": {
- "description": "Deletes a single plant based on the ID supplied",
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "description": "ID of plant to delete",
- "required": true,
- "schema": {
- "type": "integer",
- "format": "int64"
- }
- }
- ],
- "responses": {
- "204": {
- "description": "Plant deleted",
- "content": {}
- },
- "400": {
- "description": "unexpected error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Error"
- }
- }
- }
- }
- }
- }
- }
- },
- "webhooks": {
- "/plant/webhook": {
- "post": {
- "description": "Information about a new plant added to the store",
- "requestBody": {
- "description": "Plant added to the store",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/NewPlant"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Return a 200 status to indicate that the data was received successfully"
- }
- }
- }
- }
- },
- "components": {
- "schemas": {
- "Plant": {
- "required": [
- "name"
- ],
- "type": "object",
- "properties": {
- "name": {
- "description": "The name of the plant",
- "type": "string"
- },
- "tag": {
- "description": "Tag to specify the type",
- "type": "string"
- }
- }
- },
- "NewPlant": {
- "allOf": [
- {
- "$ref": "#/components/schemas/Plant"
- },
- {
- "required": [
- "id"
- ],
- "type": "object",
- "properties": {
- "id": {
- "description": "Identification number of the plant",
- "type": "integer",
- "format": "int64"
- }
- }
- }
- ]
- },
- "Error": {
- "required": [
- "error",
- "message"
- ],
- "type": "object",
- "properties": {
- "error": {
- "type": "integer",
- "format": "int32"
- },
- "message": {
- "type": "string"
- }
- }
- }
- },
- "securitySchemes": {
- "bearerAuth": {
- "type": "http",
- "scheme": "bearer"
- }
- }
- }
-}
\ No newline at end of file
diff --git a/docs-site/development.mdx b/docs-site/development.mdx
deleted file mode 100644
index ac633bad..00000000
--- a/docs-site/development.mdx
+++ /dev/null
@@ -1,94 +0,0 @@
----
-title: 'Development'
-description: 'Preview changes locally to update your docs'
----
-
-
- **Prerequisites**:
- - Node.js version 19 or higher
- - A docs repository with a `docs.json` file
-
-
-Follow these steps to install and run Mintlify on your operating system.
-
-
-
-
-```bash
-npm i -g mint
-```
-
-
-
-
-Navigate to your docs directory where your `docs.json` file is located, and run the following command:
-
-```bash
-mint dev
-```
-
-A local preview of your documentation will be available at `http://localhost:3000`.
-
-
-
-
-## Custom ports
-
-By default, Mintlify uses port 3000. You can customize the port Mintlify runs on by using the `--port` flag. For example, to run Mintlify on port 3333, use this command:
-
-```bash
-mint dev --port 3333
-```
-
-If you attempt to run Mintlify on a port that's already in use, it will use the next available port:
-
-```md
-Port 3000 is already in use. Trying 3001 instead.
-```
-
-## Mintlify versions
-
-Please note that each CLI release is associated with a specific version of Mintlify. If your local preview does not align with the production version, please update the CLI:
-
-```bash
-npm mint update
-```
-
-## Validating links
-
-The CLI can assist with validating links in your documentation. To identify any broken links, use the following command:
-
-```bash
-mint broken-links
-```
-
-## Deployment
-
-If the deployment is successful, you should see the following:
-
-
-
-
-
-## Code formatting
-
-We suggest using extensions on your IDE to recognize and format MDX. If you're a VSCode user, consider the [MDX VSCode extension](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) for syntax highlighting, and [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) for code formatting.
-
-## Troubleshooting
-
-
-
-
- This may be due to an outdated version of node. Try the following:
- 1. Remove the currently-installed version of the CLI: `npm remove -g mint`
- 2. Upgrade to Node v19 or higher.
- 3. Reinstall the CLI: `npm i -g mint`
-
-
-
-
- Solution: Go to the root of your device and delete the `~/.mintlify` folder. Then run `mint dev` again.
-
-
-
-Curious about what changed in the latest CLI version? Check out the [CLI changelog](https://www.npmjs.com/package/mintlify?activeTab=versions).
diff --git a/docs-site/docs.json b/docs-site/docs.json
index 46b44cc4..d7bca22f 100644
--- a/docs-site/docs.json
+++ b/docs-site/docs.json
@@ -1,68 +1,46 @@
{
"$schema": "https://mintlify.com/docs.json",
"theme": "mint",
- "name": "Mint Starter Kit",
+ "name": "OpenChat",
"colors": {
"primary": "#16A34A",
- "light": "#07C983",
+ "light": "#22C55E",
"dark": "#15803D"
},
"favicon": "/favicon.svg",
+ "logo": {
+ "light": "/logo/light.svg",
+ "dark": "/logo/dark.svg"
+ },
"navigation": {
"tabs": [
{
- "tab": "Guides",
+ "tab": "Documentation",
"groups": [
{
- "group": "Getting started",
+ "group": "Getting Started",
+ "icon": "rocket",
"pages": [
"index",
- "quickstart",
- "development"
- ]
- },
- {
- "group": "Customization",
- "pages": [
- "essentials/settings",
- "essentials/navigation"
- ]
- },
- {
- "group": "Writing content",
- "pages": [
- "essentials/markdown",
- "essentials/code",
- "essentials/images",
- "essentials/reusable-snippets"
+ "quickstart"
]
},
{
- "group": "AI tools",
+ "group": "Guides",
+ "icon": "book",
"pages": [
- "ai-tools/cursor",
- "ai-tools/claude-code",
- "ai-tools/windsurf"
- ]
- }
- ]
- },
- {
- "tab": "API reference",
- "groups": [
- {
- "group": "API documentation",
- "pages": [
- "api-reference/introduction"
+ "guides/architecture",
+ "guides/authentication",
+ "guides/ai-models",
+ "guides/contributing"
]
},
{
- "group": "Endpoint examples",
+ "group": "Self-Hosting",
+ "icon": "server",
"pages": [
- "api-reference/endpoint/get",
- "api-reference/endpoint/create",
- "api-reference/endpoint/delete",
- "api-reference/endpoint/webhook"
+ "self-hosting/docker",
+ "self-hosting/environment"
]
}
]
@@ -71,52 +49,44 @@
"global": {
"anchors": [
{
- "anchor": "Documentation",
- "href": "https://mintlify.com/docs",
- "icon": "book-open-cover"
+ "anchor": "GitHub",
+ "href": "https://github.com/tryosschat/openchat",
+ "icon": "github"
},
{
- "anchor": "Blog",
- "href": "https://mintlify.com/blog",
- "icon": "newspaper"
+ "anchor": "Try OpenChat",
+ "href": "https://osschat.io",
+ "icon": "arrow-up-right-from-square"
}
]
}
},
- "logo": {
- "light": "/logo/light.svg",
- "dark": "/logo/dark.svg"
- },
"navbar": {
"links": [
{
- "label": "Support",
- "href": "mailto:hi@mintlify.com"
+ "label": "GitHub",
+ "href": "https://github.com/tryosschat/openchat"
}
],
"primary": {
"type": "button",
- "label": "Dashboard",
- "href": "https://dashboard.mintlify.com"
+ "label": "Try OpenChat",
+ "href": "https://osschat.io"
}
},
"contextual": {
"options": [
- "copy",
- "view",
- "chatgpt",
- "claude",
- "perplexity",
- "mcp",
- "cursor",
- "vscode"
- ]
+ "copy",
+ "view",
+ "chatgpt",
+ "claude",
+ "cursor"
+ ]
},
"footer": {
"socials": {
- "x": "https://x.com/mintlify",
- "github": "https://github.com/mintlify",
- "linkedin": "https://linkedin.com/company/mintlify"
+ "github": "https://github.com/tryosschat/openchat"
}
- }
+ },
+ "mermaid": {}
}
diff --git a/docs-site/essentials/code.mdx b/docs-site/essentials/code.mdx
deleted file mode 100644
index ae2abbfe..00000000
--- a/docs-site/essentials/code.mdx
+++ /dev/null
@@ -1,35 +0,0 @@
----
-title: 'Code blocks'
-description: 'Display inline code and code blocks'
-icon: 'code'
----
-
-## Inline code
-
-To denote a `word` or `phrase` as code, enclose it in backticks (`).
-
-```
-To denote a `word` or `phrase` as code, enclose it in backticks (`).
-```
-
-## Code blocks
-
-Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language.
-
-```java HelloWorld.java
-class HelloWorld {
- public static void main(String[] args) {
- System.out.println("Hello, World!");
- }
-}
-```
-
-````md
-```java HelloWorld.java
-class HelloWorld {
- public static void main(String[] args) {
- System.out.println("Hello, World!");
- }
-}
-```
-````
diff --git a/docs-site/essentials/images.mdx b/docs-site/essentials/images.mdx
deleted file mode 100644
index 1144eb2c..00000000
--- a/docs-site/essentials/images.mdx
+++ /dev/null
@@ -1,59 +0,0 @@
----
-title: 'Images and embeds'
-description: 'Add image, video, and other HTML elements'
-icon: 'image'
----
-
-
-
-## Image
-
-### Using Markdown
-
-The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code
-
-```md
-
-```
-
-Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed.
-
-### Using embeds
-
-To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images
-
-```html
-
-```
-
-## Embeds and HTML elements
-
-
-
-
-
-
-
-Mintlify supports [HTML tags in Markdown](https://www.markdownguide.org/basic-syntax/#html). This is helpful if you prefer HTML tags to Markdown syntax, and lets you create documentation with infinite flexibility.
-
-
-
-### iFrames
-
-Loads another HTML page within the document. Most commonly used for embedding videos.
-
-```html
-
-```
diff --git a/docs-site/essentials/markdown.mdx b/docs-site/essentials/markdown.mdx
deleted file mode 100644
index a45c1d56..00000000
--- a/docs-site/essentials/markdown.mdx
+++ /dev/null
@@ -1,88 +0,0 @@
----
-title: 'Markdown syntax'
-description: 'Text, title, and styling in standard markdown'
-icon: 'text-size'
----
-
-## Titles
-
-Best used for section headers.
-
-```md
-## Titles
-```
-
-### Subtitles
-
-Best used for subsection headers.
-
-```md
-### Subtitles
-```
-
-
-
-Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right.
-
-
-
-## Text formatting
-
-We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it.
-
-| Style | How to write it | Result |
-| ------------- | ----------------- | --------------- |
-| Bold | `**bold**` | **bold** |
-| Italic | `_italic_` | _italic_ |
-| Strikethrough | `~strikethrough~` | ~strikethrough~ |
-
-You can combine these. For example, write `**_bold and italic_**` to get **_bold and italic_** text.
-
-You need to use HTML to write superscript and subscript text. That is, add `` or `` around your text.
-
-| Text Size | How to write it | Result |
-| ----------- | ------------------------ | ---------------------- |
-| Superscript | `superscript` | superscript |
-| Subscript | `subscript` | subscript |
-
-## Linking to pages
-
-You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com).
-
-Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section.
-
-Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily.
-
-## Blockquotes
-
-### Singleline
-
-To create a blockquote, add a `>` in front of a paragraph.
-
-> Dorothy followed her through many of the beautiful rooms in her castle.
-
-```md
-> Dorothy followed her through many of the beautiful rooms in her castle.
-```
-
-### Multiline
-
-> Dorothy followed her through many of the beautiful rooms in her castle.
->
-> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
-
-```md
-> Dorothy followed her through many of the beautiful rooms in her castle.
->
-> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
-```
-
-### LaTeX
-
-Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component.
-
-8 x (vk x H1 - H2) = (0,1)
-
-```md
-8 x (vk x H1 - H2) = (0,1)
-```
diff --git a/docs-site/essentials/navigation.mdx b/docs-site/essentials/navigation.mdx
deleted file mode 100644
index 60adeff2..00000000
--- a/docs-site/essentials/navigation.mdx
+++ /dev/null
@@ -1,87 +0,0 @@
----
-title: 'Navigation'
-description: 'The navigation field in docs.json defines the pages that go in the navigation menu'
-icon: 'map'
----
-
-The navigation menu is the list of links on every website.
-
-You will likely update `docs.json` every time you add a new page. Pages do not show up automatically.
-
-## Navigation syntax
-
-Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names.
-
-
-
-```json Regular Navigation
-"navigation": {
- "tabs": [
- {
- "tab": "Docs",
- "groups": [
- {
- "group": "Getting Started",
- "pages": ["quickstart"]
- }
- ]
- }
- ]
-}
-```
-
-```json Nested Navigation
-"navigation": {
- "tabs": [
- {
- "tab": "Docs",
- "groups": [
- {
- "group": "Getting Started",
- "pages": [
- "quickstart",
- {
- "group": "Nested Reference Pages",
- "pages": ["nested-reference-page"]
- }
- ]
- }
- ]
- }
- ]
-}
-```
-
-
-
-## Folders
-
-Simply put your MDX files in folders and update the paths in `docs.json`.
-
-For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`.
-
-
-
-You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted.
-
-
-
-```json Navigation With Folder
-"navigation": {
- "tabs": [
- {
- "tab": "Docs",
- "groups": [
- {
- "group": "Group Name",
- "pages": ["your-folder/your-page"]
- }
- ]
- }
- ]
-}
-```
-
-## Hidden pages
-
-MDX files not included in `docs.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them.
diff --git a/docs-site/essentials/reusable-snippets.mdx b/docs-site/essentials/reusable-snippets.mdx
deleted file mode 100644
index 376e27bd..00000000
--- a/docs-site/essentials/reusable-snippets.mdx
+++ /dev/null
@@ -1,110 +0,0 @@
----
-title: "Reusable snippets"
-description: "Reusable, custom snippets to keep content in sync"
-icon: "recycle"
----
-
-import SnippetIntro from '/snippets/snippet-intro.mdx';
-
-
-
-## Creating a custom snippet
-
-**Pre-condition**: You must create your snippet file in the `snippets` directory.
-
-
- Any page in the `snippets` directory will be treated as a snippet and will not
- be rendered into a standalone page. If you want to create a standalone page
- from the snippet, import the snippet into another file and call it as a
- component.
-
-
-### Default export
-
-1. Add content to your snippet file that you want to re-use across multiple
- locations. Optionally, you can add variables that can be filled in via props
- when you import the snippet.
-
-```mdx snippets/my-snippet.mdx
-Hello world! This is my content I want to reuse across pages. My keyword of the
-day is {word}.
-```
-
-
- The content that you want to reuse must be inside the `snippets` directory in
- order for the import to work.
-
-
-2. Import the snippet into your destination file.
-
-```mdx destination-file.mdx
----
-title: My title
-description: My Description
----
-
-import MySnippet from '/snippets/path/to/my-snippet.mdx';
-
-## Header
-
-Lorem impsum dolor sit amet.
-
-
-```
-
-### Reusable variables
-
-1. Export a variable from your snippet file:
-
-```mdx snippets/path/to/custom-variables.mdx
-export const myName = 'my name';
-
-export const myObject = { fruit: 'strawberries' };
-```
-
-2. Import the snippet from your destination file and use the variable:
-
-```mdx destination-file.mdx
----
-title: My title
-description: My Description
----
-
-import { myName, myObject } from '/snippets/path/to/custom-variables.mdx';
-
-Hello, my name is {myName} and I like {myObject.fruit}.
-```
-
-### Reusable components
-
-1. Inside your snippet file, create a component that takes in props by exporting
- your component in the form of an arrow function.
-
-```mdx snippets/custom-component.mdx
-export const MyComponent = ({ title }) => (
-
-
{title}
-
... snippet content ...
-
-);
-```
-
-
- MDX does not compile inside the body of an arrow function. Stick to HTML
- syntax when you can or use a default export if you need to use MDX.
-
-
-2. Import the snippet into your destination file and pass in the props
-
-```mdx destination-file.mdx
----
-title: My title
-description: My Description
----
-
-import { MyComponent } from '/snippets/custom-component.mdx';
-
-Lorem ipsum dolor sit amet.
-
-
-```
diff --git a/docs-site/essentials/settings.mdx b/docs-site/essentials/settings.mdx
deleted file mode 100644
index 884de13a..00000000
--- a/docs-site/essentials/settings.mdx
+++ /dev/null
@@ -1,318 +0,0 @@
----
-title: 'Global Settings'
-description: 'Mintlify gives you complete control over the look and feel of your documentation using the docs.json file'
-icon: 'gear'
----
-
-Every Mintlify site needs a `docs.json` file with the core configuration settings. Learn more about the [properties](#properties) below.
-
-## Properties
-
-
-Name of your project. Used for the global title.
-
-Example: `mintlify`
-
-
-
-
- An array of groups with all the pages within that group
-
-
- The name of the group.
-
- Example: `Settings`
-
-
-
- The relative paths to the markdown files that will serve as pages.
-
- Example: `["customization", "page"]`
-
-
-
-
-
-
-
- Path to logo image or object with path to "light" and "dark" mode logo images
-
-
- Path to the logo in light mode
-
-
- Path to the logo in dark mode
-
-
- Where clicking on the logo links you to
-
-
-
-
-
- Path to the favicon image
-
-
-
- Hex color codes for your global theme
-
-
- The primary color. Used for most often for highlighted content, section
- headers, accents, in light mode
-
-
- The primary color for dark mode. Used for most often for highlighted
- content, section headers, accents, in dark mode
-
-
- The primary color for important buttons
-
-
- The color of the background in both light and dark mode
-
-
- The hex color code of the background in light mode
-
-
- The hex color code of the background in dark mode
-
-
-
-
-
-
-
- Array of `name`s and `url`s of links you want to include in the topbar
-
-
- The name of the button.
-
- Example: `Contact us`
-
-
- The url once you click on the button. Example: `https://mintlify.com/docs`
-
-
-
-
-
-
-
-
- Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars.
-
-
- If `link`: What the button links to.
-
- If `github`: Link to the repository to load GitHub information from.
-
-
- Text inside the button. Only required if `type` is a `link`.
-
-
-
-
-
-
- Array of version names. Only use this if you want to show different versions
- of docs with a dropdown in the navigation bar.
-
-
-
- An array of the anchors, includes the `icon`, `color`, and `url`.
-
-
- The [Font Awesome](https://fontawesome.com/search?q=heart) icon used to feature the anchor.
-
- Example: `comments`
-
-
- The name of the anchor label.
-
- Example: `Community`
-
-
- The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in.
-
-
- The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color.
-
-
- Used if you want to hide an anchor until the correct docs version is selected.
-
-
- Pass `true` if you want to hide the anchor until you directly link someone to docs inside it.
-
-
- One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin"
-
-
-
-
-
-
- Override the default configurations for the top-most anchor.
-
-
- The name of the top-most anchor
-
-
- Font Awesome icon.
-
-
- One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin"
-
-
-
-
-
- An array of navigational tabs.
-
-
- The name of the tab label.
-
-
- The start of the URL that marks what pages go in the tab. Generally, this
- is the name of the folder you put your pages in.
-
-
-
-
-
- Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo).
-
-
- The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url
- options that the user can toggle.
-
-
-
-
-
- The authentication strategy used for all API endpoints.
-
-
- The name of the authentication parameter used in the API playground.
-
- If method is `basic`, the format should be `[usernameName]:[passwordName]`
-
-
- The default value that's designed to be a prefix for the authentication input field.
-
- E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`.
-
-
-
-
-
- Configurations for the API playground
-
-
-
- Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple`
-
- Learn more at the [playground guides](/api-playground/demo)
-
-
-
-
-
- Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file.
-
- This behavior will soon be enabled by default, at which point this field will be deprecated.
-
-
-
-
-
-
- A string or an array of strings of URL(s) or relative path(s) pointing to your
- OpenAPI file.
-
- Examples:
-
- ```json Absolute
- "openapi": "https://example.com/openapi.json"
- ```
- ```json Relative
- "openapi": "/openapi.json"
- ```
- ```json Multiple
- "openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"]
- ```
-
-
-
-
-
- An object of social media accounts where the key:property pair represents the social media platform and the account url.
-
- Example:
- ```json
- {
- "x": "https://x.com/mintlify",
- "website": "https://mintlify.com"
- }
- ```
-
-
- One of the following values `website`, `facebook`, `x`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news`
-
- Example: `x`
-
-
- The URL to the social platform.
-
- Example: `https://x.com/mintlify`
-
-
-
-
-
- Configurations to enable feedback buttons
-
-
-
- Enables a button to allow users to suggest edits via pull requests
-
-
- Enables a button to allow users to raise an issue about the documentation
-
-
-
-
-
- Customize the dark mode toggle.
-
-
- Set if you always want to show light or dark mode for new users. When not
- set, we default to the same mode as the user's operating system.
-
-
- Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example:
-
-
- ```json Only Dark Mode
- "modeToggle": {
- "default": "dark",
- "isHidden": true
- }
- ```
-
- ```json Only Light Mode
- "modeToggle": {
- "default": "light",
- "isHidden": true
- }
- ```
-
-
-
-
-
-
-
-
- A background image to be displayed behind every page. See example with
- [Infisical](https://infisical.com/docs) and [FRPC](https://frpc.io).
-
diff --git a/docs-site/guides/ai-models.mdx b/docs-site/guides/ai-models.mdx
new file mode 100644
index 00000000..8b1e74aa
--- /dev/null
+++ b/docs-site/guides/ai-models.mdx
@@ -0,0 +1,394 @@
+---
+title: AI Models & Chat
+description: How OpenChat integrates with OpenRouter for AI model access
+---
+
+# AI Models & Chat
+
+OpenChat uses **OpenRouter** as a unified gateway to access 100+ AI models from providers like OpenAI, Anthropic, Google, Meta, and more.
+
+## Overview
+
+```mermaid
+graph LR
+ A[User Message] --> B[OpenChat]
+ B --> C[OpenRouter API]
+ C --> D[Claude]
+ C --> E[GPT-4]
+ C --> F[Gemini]
+ C --> G[Llama]
+ C --> H[...100+ models]
+```
+
+OpenChat supports two modes for AI access:
+
+| Mode | Description | Cost |
+|------|-------------|------|
+| **OSSChat Cloud** | Uses server's OpenRouter key | Free (10¢/day limit) |
+| **BYOK** | Bring Your Own Key | Your OpenRouter credits |
+
+## Dual Provider System
+
+### OSSChat Cloud (Default)
+
+The free tier uses the server's OpenRouter API key with daily limits:
+
+```typescript
+// Automatically used when no personal key is set
+const provider = "osschat";
+const apiKey = process.env.OPENROUTER_API_KEY; // Server's key
+```
+
+- **Daily limit**: ~10¢ worth of usage
+- **Resets**: Daily at midnight UTC
+- **Models**: All OpenRouter models available
+
+### BYOK (Bring Your Own Key)
+
+Users can connect their own OpenRouter account for unlimited access:
+
+```typescript
+// User's personal API key
+const provider = "openrouter";
+const apiKey = userStore.openrouterApiKey; // User's key
+```
+
+- **No limits**: Uses your own OpenRouter credits
+- **All models**: Including premium models
+- **Privacy**: API key stored locally (encrypted if saved to Convex)
+
+## Connecting OpenRouter (BYOK)
+
+
+
+ Click your profile in the sidebar, then click **Settings**.
+
+
+
+ Select the **Providers** tab to see connection options.
+
+
+
+ Click **Connect OpenRouter**. You'll be redirected to OpenRouter to authorize.
+
+
+ OpenChat uses OAuth PKCE for secure key exchange. Your key is never exposed in URLs.
+
+
+
+
+ On OpenRouter, authorize the application. You'll be redirected back to OpenChat.
+
+
+
+ Your models are now available! The provider switches to "Personal OpenRouter" automatically.
+
+
+
+## Model Selection
+
+### Using the Model Selector
+
+Click the model name in the chat input to open the model selector:
+
+- **Search**: Type to filter models
+- **Favorites**: Star models for quick access
+- **Details**: See context window, pricing, capabilities
+
+### Model Capabilities
+
+Models vary in their capabilities:
+
+| Capability | Description | Example Models |
+|------------|-------------|----------------|
+| **Reasoning** | Extended thinking for complex tasks | Claude 3.5, o1, DeepSeek R1 |
+| **Vision** | Process images | Claude 3.5, GPT-4o, Gemini |
+| **Long Context** | Large context windows (100K+) | Claude 3.5, Gemini Pro |
+| **Fast** | Low latency responses | GPT-4o-mini, Claude Haiku |
+
+### Reasoning Mode
+
+For supported models, enable reasoning mode to see the AI's thinking process:
+
+```typescript
+// Configure reasoning effort
+const reasoningEffort = "medium"; // none, low, medium, high
+
+// Sent to OpenRouter
+providerOptions: {
+ openrouter: {
+ reasoning: {
+ effort: reasoningEffort
+ }
+ }
+}
+```
+
+Reasoning appears in a collapsible "Chain of Thought" section above the response.
+
+## Chat API
+
+### Endpoint
+
+```
+POST /api/chat
+```
+
+### Request Body
+
+```typescript
+{
+ messages: UIMessage[], // Conversation history
+ model: string, // e.g., "anthropic/claude-3.5-sonnet"
+ provider: "osschat" | "openrouter",
+ apiKey?: string, // Required for "openrouter" provider
+ enableWebSearch?: boolean, // Enable web search tool
+ reasoningEffort?: "none" | "low" | "medium" | "high",
+ maxSteps?: number // Max tool call iterations (default: 5)
+}
+```
+
+### Response
+
+Returns a Server-Sent Events stream compatible with AI SDK 5's `useChat` hook:
+
+```typescript
+// Stream events
+data: {"type":"text","content":"Hello"}
+data: {"type":"text","content":" there!"}
+data: {"type":"finish","usage":{"promptTokens":10,"completionTokens":5}}
+```
+
+### Example
+
+```typescript
+import { useChat } from "ai/react";
+
+function ChatComponent() {
+ const { messages, input, handleInputChange, handleSubmit } = useChat({
+ api: "/api/chat",
+ body: {
+ model: "anthropic/claude-3.5-sonnet",
+ provider: "osschat",
+ },
+ });
+
+ return (
+
+ );
+}
+```
+
+## Streaming Architecture
+
+OpenChat uses AI SDK 5 for streaming:
+
+```mermaid
+sequenceDiagram
+ participant C as Client
+ participant S as Server (TanStack)
+ participant O as OpenRouter
+
+ C->>S: POST /api/chat
+ S->>O: streamText()
+ loop Streaming
+ O-->>S: Token
+ S-->>C: SSE Event
+ C->>C: Update UI
+ end
+ O-->>S: Finish
+ S-->>C: Usage stats
+ C->>C: Save to Convex
+```
+
+### Cancellation Support
+
+Users can cancel in-progress responses:
+
+```typescript
+// Client
+const abortController = new AbortController();
+fetch("/api/chat", { signal: abortController.signal });
+
+// To cancel
+abortController.abort();
+
+// Server
+const abortSignal = request.signal;
+streamText({ model, messages, abortSignal });
+```
+
+## Web Search
+
+OpenChat can search the web for current information:
+
+```typescript
+// Enable web search
+const response = await fetch("/api/chat", {
+ method: "POST",
+ body: JSON.stringify({
+ messages,
+ model: "anthropic/claude-3.5-sonnet",
+ enableWebSearch: true, // Enable
+ maxSteps: 5 // Allow tool iterations
+ })
+});
+```
+
+Web search uses the Valyu AI SDK and requires `VALYU_API_KEY` to be configured.
+
+## Token Usage & Cost Tracking
+
+### Usage Display
+
+Token usage is tracked and displayed per message:
+
+```typescript
+// Response includes usage metadata
+{
+ inputTokens: 1234,
+ outputTokens: 567,
+ totalTokens: 1801
+}
+```
+
+### Cost Calculation
+
+For OSSChat Cloud, costs are tracked against the daily limit:
+
+```typescript
+// Provider store tracks usage
+const { dailyUsageCents, dailyLimitCents } = useProviderStore();
+
+// Display remaining budget
+const remaining = dailyLimitCents - dailyUsageCents; // e.g., 7¢ remaining
+```
+
+## File Attachments
+
+Upload images and documents to include in messages:
+
+```typescript
+// Generate upload URL
+const uploadUrl = await convex.mutation(api.files.generateUploadUrl, {
+ userId,
+ chatId
+});
+
+// Upload file
+await fetch(uploadUrl, {
+ method: "POST",
+ body: file
+});
+
+// Save metadata
+const { fileId, url } = await convex.mutation(api.files.saveFileMetadata, {
+ userId,
+ chatId,
+ storageId,
+ filename: file.name,
+ contentType: file.type,
+ size: file.size
+});
+```
+
+### Limits
+
+| Limit | Value |
+|-------|-------|
+| Max file size | 10 MB |
+| Files per user | 150 total |
+| Supported types | Images (jpg, png, gif, webp), PDF |
+
+## Prompt Templates
+
+Create custom slash commands for frequently used prompts:
+
+```typescript
+// Create template
+await convex.mutation(api.promptTemplates.create, {
+ name: "Code Review",
+ command: "/review",
+ template: "Review this code for bugs and improvements:\n\n$ARGUMENTS",
+ category: "coding"
+});
+
+// Use in chat
+/review function add(a, b) { return a + b; }
+```
+
+### Template Variables
+
+| Variable | Description |
+|----------|-------------|
+| `$ARGUMENTS` | Everything after the command |
+| `$1`, `$2`, ... | Positional arguments |
+
+## Configuration
+
+### Environment Variables
+
+```bash
+# Server-side
+OPENROUTER_API_KEY=sk-or-v1-... # For OSSChat Cloud
+VALYU_API_KEY=... # For web search
+
+# Client-side (if needed)
+# None - API keys should never be on client
+```
+
+### Rate Limits
+
+The chat API is rate-limited to prevent abuse:
+
+| Operation | Rate | Burst |
+|-----------|------|-------|
+| Message send | 30/min | 10 |
+| Stream upsert | 200/min | 50 |
+
+## Troubleshooting
+
+
+
+ Check:
+ 1. Model is available (some are intermittently down)
+ 2. API key is valid (try regenerating)
+ 3. You have credits (for BYOK) or daily budget (for OSSChat)
+
+
+
+ This can happen if:
+ 1. Network connection drops
+ 2. Model reaches max tokens
+ 3. Rate limit exceeded
+
+ Try sending a shorter message or switching models.
+
+
+
+ Verify:
+ 1. `VALYU_API_KEY` is set on the server
+ 2. `enableWebSearch: true` in request
+ 3. Model supports tool use
+
+
+
+ For OSSChat Cloud, the 10¢ daily limit resets at midnight UTC. Options:
+ 1. Wait for reset
+ 2. Connect your own OpenRouter key (BYOK)
+
+
+
+## Next Steps
+
+
+
+ Deploy with your own API keys
+
+
+ Add new model integrations
+
+
diff --git a/docs-site/guides/architecture.mdx b/docs-site/guides/architecture.mdx
new file mode 100644
index 00000000..daf4e956
--- /dev/null
+++ b/docs-site/guides/architecture.mdx
@@ -0,0 +1,272 @@
+---
+title: Architecture Overview
+description: How OpenChat's components work together
+---
+
+# Architecture Overview
+
+OpenChat uses a modern, three-tier architecture designed for real-time collaboration, streaming AI responses, and seamless cross-device sync.
+
+## System Overview
+
+```mermaid
+graph TB
+ subgraph "Frontend"
+ A[TanStack Start App]
+ B[React Components]
+ C[Zustand Stores]
+ end
+
+ subgraph "Backend"
+ D[Convex Functions]
+ E[Better Auth]
+ F[Real-time Sync]
+ end
+
+ subgraph "External Services"
+ G[OpenRouter API]
+ H[GitHub OAuth]
+ end
+
+ A --> D
+ A --> G
+ B --> C
+ D --> F
+ E --> H
+ E --> D
+```
+
+## Core Components
+
+
+
+ The frontend is built with **TanStack Start**, a full-stack React framework powered by Vite. It provides:
+
+ - **File-based routing** via TanStack Router
+ - **Server-side rendering** for fast initial loads
+ - **Type-safe navigation** with automatic route inference
+
+ Key directories:
+ - `apps/web/src/routes/` - Page components and API routes
+ - `apps/web/src/components/` - Reusable UI components
+ - `apps/web/src/stores/` - Zustand state management
+ - `apps/web/src/lib/` - Utilities and clients
+
+
+
+ **Convex** provides the real-time backend with:
+
+ - **Reactive queries** - UI updates automatically when data changes
+ - **Transactional mutations** - Atomic, consistent writes
+ - **Scheduled functions** - Background jobs and cron tasks
+ - **File storage** - Built-in blob storage for attachments
+
+ Key files:
+ - `apps/server/convex/schema.ts` - Database schema
+ - `apps/server/convex/chats.ts` - Chat CRUD operations
+ - `apps/server/convex/messages.ts` - Message handling
+ - `apps/server/convex/users.ts` - User management
+
+
+
+ **Better Auth** handles authentication with:
+
+ - **GitHub OAuth** for sign-in
+ - **Cross-domain sessions** between frontend and Convex
+ - **JWT tokens** for authenticated Convex queries
+
+ The auth flow syncs users to Convex via the `users.ensure` mutation.
+
+
+
+ **OpenRouter** provides unified access to AI models:
+
+ - **100+ models** from OpenAI, Anthropic, Google, Meta, etc.
+ - **Streaming responses** for real-time output
+ - **Usage tracking** for cost management
+
+ Supports two modes:
+ - **OSSChat Cloud** - Free tier using server's API key
+ - **BYOK** - Bring Your Own Key for unlimited access
+
+
+
+## Data Flow
+
+### Chat Message Flow
+
+```mermaid
+sequenceDiagram
+ participant U as User
+ participant W as Web App
+ participant C as Convex
+ participant O as OpenRouter
+
+ U->>W: Send message
+ W->>C: Create chat (if new)
+ W->>O: POST /api/chat (streaming)
+ O-->>W: Stream tokens
+ W-->>U: Display response
+ W->>C: Save messages
+ C-->>W: Real-time sync
+```
+
+
+
+ User types a message in the `PromptInput` component.
+
+
+
+ If it's a new chat, `usePersistentChat` calls the `chats.create` mutation to create a chat document in Convex.
+
+
+
+ The message is sent to `/api/chat` (server route), which forwards to OpenRouter with the selected model.
+
+
+
+ OpenRouter streams tokens back via Server-Sent Events. The UI updates in real-time as tokens arrive.
+
+
+
+ On completion, both user and assistant messages are saved to Convex via `messages.send`.
+
+
+
+ Convex's reactive queries automatically update the sidebar and any other connected clients.
+
+
+
+### Authentication Flow
+
+```mermaid
+sequenceDiagram
+ participant U as User
+ participant W as Web App
+ participant B as Better Auth
+ participant G as GitHub
+ participant C as Convex
+
+ U->>W: Click Sign In
+ W->>B: Initiate OAuth
+ B->>G: Redirect to GitHub
+ G->>U: Authorize app
+ G->>B: Callback with code
+ B->>B: Create session
+ B->>W: One-time token
+ W->>C: users.ensure()
+ C->>C: Create/update user
+```
+
+## Database Schema
+
+OpenChat uses 8 main tables in Convex:
+
+| Table | Purpose | Key Fields |
+|-------|---------|------------|
+| `users` | Auth data, ban status | `externalId`, `email`, `banned` |
+| `profiles` | User preferences | `name`, `avatarUrl`, `favoriteModels` |
+| `chats` | Conversations | `userId`, `title`, `messageCount` |
+| `messages` | Chat messages | `chatId`, `role`, `content`, `reasoning` |
+| `fileUploads` | Attachments | `storageId`, `filename`, `contentType` |
+| `promptTemplates` | Custom prompts | `command`, `template`, `category` |
+| `chatReadStatus` | Unread tracking | `userId`, `chatId`, `lastReadAt` |
+| `dbStats` | Aggregated stats | `key`, `value` |
+
+## State Management
+
+The frontend uses **Zustand** for client-side state with several specialized stores:
+
+| Store | Purpose |
+|-------|---------|
+| `model.ts` | Selected model, reasoning effort, favorites |
+| `provider.ts` | Active provider (osschat/openrouter), usage limits |
+| `openrouter.ts` | BYOK API key management |
+| `stream.ts` | Streaming state machine |
+| `ui.ts` | Sidebar, command palette state |
+
+All stores use the `persist` middleware for localStorage persistence and `devtools` for debugging.
+
+## Key Patterns
+
+### Optimistic Updates
+
+Convex queries are reactive, but OpenChat also uses optimistic updates for instant feedback:
+
+```typescript
+// Message appears immediately in UI
+const optimisticMessage = { id: 'temp', content: message };
+setMessages([...messages, optimisticMessage]);
+
+// Then persisted to Convex
+await sendMessage({ chatId, content: message });
+```
+
+### Rate Limiting
+
+Convex mutations are rate-limited using `@convex-dev/rate-limiter`:
+
+| Operation | Rate | Burst |
+|-----------|------|-------|
+| Chat create | 20/min | 5 |
+| Message send | 30/min | 10 |
+| File upload | 10/min | 3 |
+
+### Error Handling
+
+Errors are displayed inline as messages (like T3.chat), not as toast notifications:
+
+```typescript
+// Errors saved as messages with type 'error'
+await messages.send({
+ chatId,
+ role: 'assistant',
+ content: error.message,
+ messageType: 'error'
+});
+```
+
+## Directory Structure
+
+```
+openchat/
+├── apps/
+│ ├── web/ # Frontend (TanStack Start)
+│ │ ├── src/
+│ │ │ ├── routes/ # Pages and API routes
+│ │ │ │ ├── __root.tsx # Root layout
+│ │ │ │ ├── index.tsx # Home page
+│ │ │ │ ├── c/$chatId.tsx # Chat page
+│ │ │ │ └── api/chat.ts # Chat API route
+│ │ │ ├── components/ # React components
+│ │ │ │ ├── chat-interface.tsx
+│ │ │ │ ├── app-sidebar.tsx
+│ │ │ │ └── ui/ # shadcn/ui primitives
+│ │ │ ├── stores/ # Zustand stores
+│ │ │ ├── hooks/ # Custom hooks
+│ │ │ ├── lib/ # Utilities
+│ │ │ └── providers/ # React context providers
+│ │ └── .env.local # Environment variables
+│ └── server/ # Backend (Convex)
+│ └── convex/
+│ ├── schema.ts # Database schema
+│ ├── auth.ts # Better Auth config
+│ ├── chats.ts # Chat operations
+│ ├── messages.ts # Message operations
+│ ├── users.ts # User operations
+│ ├── streaming.ts # LLM streaming
+│ └── http.ts # HTTP endpoints
+├── docs-site/ # Documentation (Mintlify)
+└── docs/ # Internal docs
+```
+
+## Next Steps
+
+
+
+ Learn how the auth system works
+
+
+ Understand model selection and streaming
+
+
diff --git a/docs-site/guides/authentication.mdx b/docs-site/guides/authentication.mdx
new file mode 100644
index 00000000..1e036e7b
--- /dev/null
+++ b/docs-site/guides/authentication.mdx
@@ -0,0 +1,367 @@
+---
+title: Authentication
+description: How OpenChat handles user authentication with Better Auth and GitHub OAuth
+---
+
+# Authentication
+
+OpenChat uses **Better Auth** with GitHub OAuth for user authentication, integrated with Convex for real-time user data sync.
+
+## Overview
+
+```mermaid
+graph LR
+ A[User] --> B[Sign In Page]
+ B --> C[GitHub OAuth]
+ C --> D[Better Auth]
+ D --> E[Convex Session]
+ E --> F[User Synced]
+```
+
+The authentication flow involves three key components:
+
+1. **Better Auth** - Handles OAuth and session management
+2. **GitHub** - Identity provider for user authentication
+3. **Convex** - Stores user data and provides real-time sync
+
+## Authentication Flow
+
+
+
+ User clicks "Continue with GitHub" on the sign-in page. This calls:
+
+ ```typescript
+ import { signInWithGitHub } from "@/lib/auth-client";
+
+ await signInWithGitHub("/"); // Redirect to home after auth
+ ```
+
+
+
+ User is redirected to GitHub to authorize the application. GitHub shows:
+ - App name and permissions requested
+ - Option to grant or deny access
+
+
+
+ GitHub redirects back to Convex with an authorization code:
+ ```
+ https://your-convex-site.convex.site/api/auth/callback/github?code=xxx
+ ```
+
+ Better Auth exchanges the code for user info and creates a session.
+
+
+
+ The frontend receives a one-time token (OTT) and exchanges it for a session:
+
+ ```typescript
+ // Automatic in StableAuthProvider
+ await authClient.crossDomain.oneTimeToken.verify({ token: ott });
+ ```
+
+
+
+ The `UserSyncProvider` calls `users.ensure` to sync the user to Convex:
+
+ ```typescript
+ await convex.mutation(api.users.ensure, {
+ externalId: user.id,
+ email: user.email,
+ name: user.name,
+ avatarUrl: user.image
+ });
+ ```
+
+
+
+## Key Components
+
+### Auth Client (`auth-client.tsx`)
+
+The Better Auth client is configured with cross-domain support:
+
+```typescript
+import { createAuthClient } from "better-auth/react";
+import { convexClient, crossDomainClient } from "@convex-dev/better-auth/client/plugins";
+
+export const authClient = createAuthClient({
+ baseURL: env.CONVEX_SITE_URL,
+ plugins: [
+ convexClient(), // Convex JWT integration
+ crossDomainClient({ // Cross-origin session handling
+ storage: deduplicatingStorage,
+ }),
+ ],
+});
+```
+
+### useAuth Hook
+
+Access auth state anywhere in your app:
+
+```typescript
+import { useAuth } from "@/lib/auth-client";
+
+function MyComponent() {
+ const { user, isAuthenticated, loading } = useAuth();
+
+ if (loading) return ;
+ if (!isAuthenticated) return ;
+
+ return
+ );
+ }
+
+ return ;
+}
+```
+
+Protected routes in OpenChat:
+- `/` (home) - Shows landing page for guests, chat for authenticated
+- `/c/:chatId` - Individual chat pages
+- `/settings` - User settings
+
+## Convex Integration
+
+### User Schema
+
+```typescript
+// schema.ts
+users: defineTable({
+ externalId: v.string(), // Better Auth user ID
+ email: v.optional(v.string()),
+ name: v.optional(v.string()),
+ avatarUrl: v.optional(v.string()),
+ banned: v.optional(v.boolean()),
+ createdAt: v.number(),
+ updatedAt: v.number(),
+})
+ .index("by_external_id", ["externalId"])
+ .index("by_email", ["email"])
+```
+
+### User Sync Mutation
+
+```typescript
+// users.ts
+export const ensure = mutation({
+ args: {
+ externalId: v.string(),
+ email: v.optional(v.string()),
+ name: v.optional(v.string()),
+ avatarUrl: v.optional(v.string()),
+ },
+ handler: async (ctx, args) => {
+ // Find existing user
+ let user = await ctx.db
+ .query("users")
+ .withIndex("by_external_id", q => q.eq("externalId", args.externalId))
+ .first();
+
+ if (!user) {
+ // Create new user
+ const userId = await ctx.db.insert("users", {
+ externalId: args.externalId,
+ email: args.email,
+ name: args.name,
+ avatarUrl: args.avatarUrl,
+ createdAt: Date.now(),
+ updatedAt: Date.now(),
+ });
+ return { userId };
+ }
+
+ // Update existing user
+ await ctx.db.patch(user._id, {
+ email: args.email,
+ name: args.name,
+ avatarUrl: args.avatarUrl,
+ updatedAt: Date.now(),
+ });
+
+ return { userId: user._id };
+ },
+});
+```
+
+## Configuration
+
+### Environment Variables
+
+```bash
+# apps/web/.env.local
+
+# GitHub OAuth
+GITHUB_CLIENT_ID=your_client_id
+GITHUB_CLIENT_SECRET=your_client_secret
+
+# Auth secret (min 32 characters)
+BETTER_AUTH_SECRET=your_random_secret
+
+# Convex URLs
+VITE_CONVEX_URL=https://your-project.convex.cloud
+VITE_CONVEX_SITE_URL=https://your-project.convex.site
+```
+
+### GitHub OAuth Setup
+
+
+
+ Go to [GitHub Developer Settings](https://github.com/settings/developers) and click **New OAuth App**.
+
+
+
+ | Field | Value |
+ |-------|-------|
+ | Application name | OpenChat |
+ | Homepage URL | `https://your-domain.com` |
+ | Authorization callback URL | `https://your-convex-site.convex.site/api/auth/callback/github` |
+
+
+ The callback URL must point to your **Convex site URL**, not your web app domain.
+
+
+
+
+ Copy the **Client ID** and generate a **Client Secret**. Add them to your `.env.local`.
+
+
+
+### Better Auth Server Config
+
+```typescript
+// apps/server/convex/auth.ts
+import { convex } from "@convex-dev/better-auth";
+import { betterAuth } from "better-auth";
+
+export const auth = betterAuth({
+ trustedOrigins: [
+ "http://localhost:3000",
+ "https://your-domain.com",
+ ],
+ socialProviders: {
+ github: {
+ clientId: process.env.GITHUB_CLIENT_ID!,
+ clientSecret: process.env.GITHUB_CLIENT_SECRET!,
+ },
+ },
+ plugins: [convex()],
+});
+```
+
+## Sign Out
+
+To sign out a user:
+
+```typescript
+import { signOut } from "@/lib/auth-client";
+
+async function handleSignOut() {
+ await signOut();
+ // Redirects to /auth/sign-in automatically
+}
+```
+
+## Troubleshooting
+
+
+
+ Check that:
+ 1. Callback URL in GitHub matches exactly (no trailing slash)
+ 2. `VITE_CONVEX_SITE_URL` is correct in your env
+ 3. Convex is running and accessible
+
+
+
+ The session is stored in localStorage and synced to a cookie. Check:
+ 1. Browser allows cookies/localStorage
+ 2. No browser extensions blocking storage
+ 3. `BETTER_AUTH_SECRET` is consistent across restarts
+
+
+
+ If `users.ensure` fails:
+ 1. Check Convex logs for errors
+ 2. Verify rate limits aren't exceeded
+ 3. Ensure auth is complete before sync (check `isAuthenticated`)
+
+
+
+ This can happen if:
+ 1. Callback URL misconfigured
+ 2. Session validation failing
+ 3. Cross-domain cookie issues (check `SameSite` settings)
+
+
+
+## Security Considerations
+
+
+ Never expose `BETTER_AUTH_SECRET` or `GITHUB_CLIENT_SECRET` to the client. These should only be in server-side environment variables.
+
+
+- **Session tokens** are stored securely with `SameSite=Lax` cookies
+- **JWT tokens** for Convex expire and are refreshed automatically
+- **Rate limiting** prevents brute-force attacks on auth endpoints
+- **HTTPS required** in production for secure cookie transmission
+
+## Next Steps
+
+
+
+ Learn about model selection and BYOK
+
+
+ Deploy with your own auth configuration
+
+
diff --git a/docs-site/guides/contributing.mdx b/docs-site/guides/contributing.mdx
new file mode 100644
index 00000000..69e42c18
--- /dev/null
+++ b/docs-site/guides/contributing.mdx
@@ -0,0 +1,387 @@
+---
+title: Contributing
+description: How to contribute to OpenChat development
+---
+
+# Contributing to OpenChat
+
+Thanks for your interest in contributing to OpenChat! This guide will help you get started.
+
+## Before You Start
+
+
+
+ - **Bun 1.3+** - Package manager and runtime
+ - **Node.js 20+** - Required for some tooling
+ - **Git** - Version control
+ - **Docker** (optional) - For container-based development
+
+
+
+ Familiarize yourself with:
+ - [Architecture Overview](/guides/architecture) - How components work together
+ - [README.md](https://github.com/tryosschat/openchat) - Project overview
+ - `AGENTS.md` - Condensed guidelines for AI tools
+
+
+
+ Follow the [Quickstart](/quickstart) to get OpenChat running locally.
+
+
+
+## Development Workflow
+
+### 1. Create a Branch
+
+```bash
+# Sync with main
+git checkout main
+git pull --rebase origin main
+
+# Create feature branch
+git checkout -b feat/your-feature-name
+```
+
+Branch naming conventions:
+- `feat/` - New features
+- `fix/` - Bug fixes
+- `docs/` - Documentation changes
+- `refactor/` - Code refactoring
+- `chore/` - Maintenance tasks
+
+### 2. Make Changes
+
+Follow our coding standards:
+
+| Convention | Example |
+|------------|---------|
+| **Indentation** | Tabs |
+| **File names** | kebab-case (`sign-in.tsx`) |
+| **Components** | `apps/web/src/components/` |
+| **Type imports** | `import type { Foo } from "..."` |
+
+### 3. Verify Your Changes
+
+Run these before pushing:
+
+```bash
+# Lint
+bun check
+
+# Type check
+bun check-types
+
+# Run tests
+bun test
+
+# Build (for build-critical changes)
+bun build
+```
+
+### 4. Commit with Conventional Commits
+
+Use [Conventional Commits](https://www.conventionalcommits.org/) format:
+
+```bash
+# Feature
+git commit -m "feat(web): add workspace picker"
+
+# Bug fix
+git commit -m "fix(server): prevent duplicate chat titles"
+
+# Documentation
+git commit -m "docs: update deployment guide"
+
+# Chore
+git commit -m "chore: update dependencies"
+```
+
+Scopes: `web`, `server`, `docs`, `extension`, or omit for cross-cutting changes.
+
+### 5. Open a Pull Request
+
+Include in your PR:
+- **Summary** - What changed and why
+- **Scope** - Which apps affected (web/server/both)
+- **Testing notes** - How to verify the change
+- **Screenshots/GIFs** - For UI changes
+
+
+ Link related issues with `Fixes #123` or `Closes #123` to auto-close them on merge.
+
+
+## Coding Standards
+
+### TypeScript
+
+```typescript
+// ✅ Good: Type-only import
+import type { User } from "@/lib/types";
+
+// ✅ Good: Named exports
+export function useAuth() { ... }
+export const AUTH_COOKIE = "ba_session";
+
+// ❌ Avoid: Default exports (except for pages)
+export default function Component() { ... }
+```
+
+### React Components
+
+```tsx
+// ✅ Good: Clear, focused component
+function ChatMessage({ message }: { message: Message }) {
+ return (
+
+ {message.content}
+
+ );
+}
+
+// ✅ Good: Use cn() for conditional classes
+import { cn } from "@/lib/utils";
+
+
+```
+
+### Convex Functions
+
+```typescript
+// ✅ Good: Use new syntax
+export const myQuery = query({
+ args: { id: v.id("users") },
+ handler: async (ctx, args) => {
+ return ctx.db.get(args.id);
+ },
+});
+
+// ✅ Good: Rate limit mutations
+export const myMutation = mutation({
+ args: { ... },
+ handler: async (ctx, args) => {
+ const { ok, retryAfter } = await rateLimiter.limit(ctx, "myMutation", {
+ key: args.userId,
+ });
+ if (!ok) throwRateLimitError("action", retryAfter);
+
+ // ... rest of handler
+ },
+});
+```
+
+## Testing
+
+### Running Tests
+
+```bash
+# All tests
+bun test
+
+# Specific workspace
+bun test:web
+bun test:server
+
+# Watch mode
+bun test:watch
+
+# With coverage
+bun test -- --coverage
+```
+
+### Writing Tests
+
+Colocate tests with source files:
+
+```
+src/
+ lib/
+ utils.ts
+ utils.test.ts
+ components/
+ Button.tsx
+ Button.test.tsx
+```
+
+Example test:
+
+```typescript
+// utils.test.ts
+import { describe, it, expect } from "vitest";
+import { formatDate } from "./utils";
+
+describe("formatDate", () => {
+ it("formats date correctly", () => {
+ const date = new Date("2024-01-15");
+ expect(formatDate(date)).toBe("January 15, 2024");
+ });
+});
+```
+
+### Convex Tests
+
+Use `convex-test` for Convex function tests:
+
+```typescript
+// chats.test.ts
+import { describe, it, expect, beforeEach } from "vitest";
+import { convexTest } from "convex-test";
+import { api } from "./_generated/api";
+
+describe("chats", () => {
+ it("creates a chat", async () => {
+ const t = convexTest(schema);
+
+ // Insert test user
+ const userId = await t.run(async (ctx) => {
+ return ctx.db.insert("users", { ... });
+ });
+
+ // Test the mutation
+ const chatId = await t.mutation(api.chats.create, {
+ userId,
+ title: "Test Chat",
+ });
+
+ expect(chatId).toBeDefined();
+ });
+});
+```
+
+## Documentation
+
+Update docs when:
+- Adding user-facing features
+- Changing configuration options
+- Modifying API behavior
+- Updating deployment steps
+
+### Docs Structure
+
+```
+docs-site/ # Mintlify documentation
+├── index.mdx # Introduction
+├── quickstart.mdx # Getting started
+├── guides/ # In-depth guides
+│ ├── architecture.mdx
+│ ├── authentication.mdx
+│ └── ...
+└── self-hosting/ # Deployment docs
+ ├── docker.mdx
+ └── environment.mdx
+```
+
+### Local Docs Preview
+
+```bash
+cd docs-site
+npx mintlify dev
+# Open http://localhost:3000
+```
+
+## Pull Request Review
+
+### What We Look For
+
+- [ ] Code follows project conventions
+- [ ] Tests added/updated for changes
+- [ ] Documentation updated if needed
+- [ ] No breaking changes (or clearly documented)
+- [ ] Commits are clean and well-described
+- [ ] CI checks pass
+
+### Automated Checks
+
+PRs trigger:
+- **Lint** - oxlint
+- **Type check** - TypeScript
+- **Tests** - Vitest
+- **Build** - Production build verification
+- **CodeQL** - Security scanning
+
+### Getting Reviews
+
+1. **Self-review first** - Check your own PR before requesting review
+2. **Respond to feedback** - Address comments or explain why you disagree
+3. **Keep it small** - Smaller PRs get faster, better reviews
+
+## Common Tasks
+
+### Adding a New Page
+
+1. Create route file in `apps/web/src/routes/`
+2. Use auth guard pattern if needed
+3. Add navigation link to sidebar
+
+```tsx
+// apps/web/src/routes/new-page.tsx
+import { createFileRoute } from "@tanstack/react-router";
+import { useAuth } from "@/lib/auth-client";
+
+export const Route = createFileRoute("/new-page")({
+ component: NewPage,
+});
+
+function NewPage() {
+ const { isAuthenticated, loading } = useAuth();
+
+ if (loading) return ;
+ if (!isAuthenticated) return ;
+
+ return
Your content here
;
+}
+```
+
+### Adding a Convex Function
+
+1. Add to appropriate file in `apps/server/convex/`
+2. Update schema if adding new table
+3. Run `bun x convex codegen` to update types
+
+```typescript
+// apps/server/convex/example.ts
+import { query, mutation } from "./_generated/server";
+import { v } from "convex/values";
+
+export const myQuery = query({
+ args: { id: v.id("users") },
+ handler: async (ctx, args) => {
+ return ctx.db.get(args.id);
+ },
+});
+```
+
+### Adding UI Components
+
+1. Use shadcn/ui primitives when possible
+2. Add to `apps/web/src/components/`
+3. Follow existing patterns in the codebase
+
+```bash
+# Add shadcn component
+cd apps/web
+bunx shadcn@latest add button
+```
+
+## Getting Help
+
+
+
+ Ask questions and share ideas
+
+
+ Report bugs or check existing issues
+
+
+
+## Code of Conduct
+
+We follow the [Contributor Covenant](https://www.contributor-covenant.org/). Be respectful, inclusive, and constructive.
+
+For security issues, please email the maintainers directly rather than opening a public issue.
+
+---
+
+Thank you for contributing to OpenChat! Every contribution helps make the project better.
diff --git a/docs-site/index.mdx b/docs-site/index.mdx
index 15c23fb6..ac16b25a 100644
--- a/docs-site/index.mdx
+++ b/docs-site/index.mdx
@@ -1,97 +1,92 @@
---
-title: "Introduction"
-description: "Welcome to the new home for your documentation"
+title: Introduction
+description: OpenChat is an open-source AI chat workspace powered by OpenRouter
---
-## Setting up
+# Welcome to OpenChat
-Get your documentation site up and running in minutes.
+OpenChat is an **open-source AI chat workspace** that you can self-host or use via OpenChat Cloud. It provides a beautiful, real-time chat interface with access to hundreds of AI models through OpenRouter.
-
- Follow our three step quickstart guide.
-
-
-## Make it yours
-
-Design a docs site that looks great and empowers your users.
-
-
-
- Edit your docs locally and preview them in real time.
+
+
+ Get up and running with OpenChat in under 5 minutes
-
- Customize the design and colors of your site to match your brand.
+
+ Understand how OpenChat is built
-
- Organize your docs to help users find what they need and succeed with your product.
+
+ Deploy OpenChat on your own infrastructure
-
- Auto-generate API documentation from OpenAPI specifications.
+
+ Learn about model selection and BYOK
-
+
-## Create beautiful pages
+## Why OpenChat?
-Everything you need to create world-class documentation.
+
+
+ Access Claude, GPT-4, Gemini, Llama, Mistral, and many more models through a single interface. OpenRouter handles the complexity of multiple providers.
+
+
+ Built on Convex, OpenChat provides instant sync across devices. Your conversations are always up to date, with optimistic updates for a snappy feel.
+
+
+ Use OpenChat Cloud's free tier, or bring your own OpenRouter API key for unlimited access to any model. Your key, your credits.
+
+
+ Fully open source under AGPLv3. Inspect the code, contribute improvements, or self-host for complete control over your data.
+
+
-
-
- Use MDX to style your docs pages.
-
-
- Add sample code to demonstrate how to use your product.
-
-
- Display images and other media.
+## Key Features
+
+| Feature | Description |
+|---------|-------------|
+| **Streaming Responses** | Watch AI responses appear in real-time with smooth streaming |
+| **File Attachments** | Upload images and documents to include in your conversations |
+| **Web Search** | Enable AI to search the web for current information |
+| **Reasoning Mode** | See the AI's thinking process with extended reasoning models |
+| **Prompt Templates** | Create custom slash commands for frequently used prompts |
+| **Dark Mode** | Beautiful light and dark themes that respect your system preference |
+
+## How It Works
+
+```mermaid
+graph LR
+ A[User] --> B[OpenChat Web]
+ B --> C[Convex Backend]
+ B --> D[OpenRouter API]
+ D --> E[AI Models]
+ C --> F[Real-time Sync]
+```
+
+1. **Sign in** with GitHub OAuth
+2. **Start chatting** - messages are stored in Convex for real-time sync
+3. **AI responds** via OpenRouter, streaming directly to your browser
+4. **Access anywhere** - your chats sync across all your devices
+
+## Tech Stack
+
+OpenChat is built with modern, production-ready technologies:
+
+- **Frontend**: [TanStack Start](https://tanstack.com/start) (Vite + TanStack Router)
+- **Backend**: [Convex](https://convex.dev) for real-time database and functions
+- **Auth**: [Better Auth](https://better-auth.com) with GitHub OAuth
+- **AI**: [OpenRouter](https://openrouter.ai) for unified model access
+- **Styling**: [Tailwind CSS v4](https://tailwindcss.com) + [shadcn/ui](https://ui.shadcn.com)
+
+## Getting Help
+
+
+
+ Ask questions and share ideas
-
- Write once and reuse across your docs.
+
+ Report bugs or request features
-
+
-## Need inspiration?
+## License
-
- Browse our showcase of exceptional documentation sites.
-
+OpenChat is licensed under the [GNU Affero General Public License v3](https://github.com/tryosschat/openchat/blob/main/LICENSE). This means you can use, modify, and distribute OpenChat, but any modifications must also be open source.
diff --git a/docs-site/quickstart.mdx b/docs-site/quickstart.mdx
index c711458b..848c7d82 100644
--- a/docs-site/quickstart.mdx
+++ b/docs-site/quickstart.mdx
@@ -1,80 +1,246 @@
---
-title: "Quickstart"
-description: "Start building awesome documentation in minutes"
+title: Quickstart
+description: Get OpenChat running locally in under 5 minutes
---
-## Get started in three steps
+# Quickstart
-Get your documentation site running locally and make your first customization.
+This guide will help you get OpenChat running on your local machine for development or testing.
-### Step 1: Set up your local environment
+
+ Looking to use OpenChat without setting it up? Visit [osschat.io](https://osschat.io) to use the hosted version.
+
-
-
- During the onboarding process, you created a GitHub repository with your docs content if you didn't already have one. You can find a link to this repository in your [dashboard](https://dashboard.mintlify.com).
+## Prerequisites
+
+Before you begin, make sure you have:
+
+
+
+ OpenChat uses Bun as its package manager and runtime.
- To clone the repository locally so that you can make and preview changes to your docs, follow the [Cloning a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) guide in the GitHub docs.
-
-
- 1. Install the Mintlify CLI: `npm i -g mint`
- 2. Navigate to your docs directory and run: `mint dev`
- 3. Open `http://localhost:3000` to see your docs live!
+ ```bash
+ curl -fsSL https://bun.sh/install | bash
+ ```
- Your preview updates automatically as you edit files.
-
-
+ Verify installation:
+ ```bash
+ bun --version # Should be 1.3.0 or higher
+ ```
+
+
+
+ Some tooling requires Node.js.
+
+ ```bash
+ node --version # Should be 20.0.0 or higher
+ ```
+
+
+
+ For cloning the repository.
+
+ ```bash
+ git --version
+ ```
+
+
+
+## Installation
+
+
+
+ ```bash
+ git clone https://github.com/tryosschat/openchat.git
+ cd openchat
+ ```
+
+
+
+ ```bash
+ bun install
+ ```
+
+ This installs all dependencies for the monorepo, including the web app and Convex backend.
+
+
+
+ Copy the example environment files:
+
+ ```bash
+ cp env.web.example apps/web/.env.local
+ cp env.server.example apps/server/.env.local
+ ```
+
+ At minimum, you need to configure:
+
+
+
+ ```bash
+ # Convex
+ VITE_CONVEX_URL=http://localhost:3210
+ VITE_CONVEX_SITE_URL=http://localhost:3211
+
+ # GitHub OAuth (see Step 4)
+ GITHUB_CLIENT_ID=your_github_client_id
+ GITHUB_CLIENT_SECRET=your_github_client_secret
+
+ # Auth secret (generate with: openssl rand -base64 32)
+ BETTER_AUTH_SECRET=your_random_secret
+
+ # Optional: OpenRouter API key for OSSChat Cloud mode
+ OPENROUTER_API_KEY=your_openrouter_key
+ ```
+
+
+ ```bash
+ # Convex automatically configures itself in dev mode
+ # The convex dev command will set this up for you
+ ```
+
+
+
+
+ See [Environment Variables](/self-hosting/environment) for a complete list.
+
+
+
+
+ Create a GitHub OAuth App for authentication:
+
+ 1. Go to [GitHub Developer Settings](https://github.com/settings/developers)
+ 2. Click **New OAuth App**
+ 3. Fill in the details:
+ - **Application name**: OpenChat Local
+ - **Homepage URL**: `http://localhost:3000`
+ - **Authorization callback URL**: `http://localhost:3211/api/auth/callback/github`
+ 4. Copy the **Client ID** and generate a **Client Secret**
+ 5. Add them to `apps/web/.env.local`
+
+
+ The callback URL must point to the Convex site URL (port 3211), not the web app.
+
+
+
+
+ ```bash
+ bun dev
+ ```
+
+ This starts both the web app and Convex backend:
+ - **Web app**: http://localhost:3000
+ - **Convex**: http://localhost:3210
+
+
+ On first run, Convex will prompt you to log in and create a project. Follow the prompts in your terminal.
+
+
+
+
+## Verify Installation
+
+Once the servers are running:
+
+1. Open http://localhost:3000 in your browser
+2. Click **Sign In** and authenticate with GitHub
+3. Start a new chat and send a message
+
+
+ If you see the chat interface and can send messages, you're all set!
+
+
+## Project Structure
+
+```
+openchat/
+├── apps/
+│ ├── web/ # TanStack Start frontend
+│ │ ├── src/
+│ │ │ ├── routes/ # File-based routing
+│ │ │ ├── components/
+│ │ │ ├── stores/ # Zustand state management
+│ │ │ └── lib/ # Utilities, auth, Convex client
+│ │ └── .env.local # Web environment variables
+│ └── server/ # Convex backend
+│ └── convex/ # Convex functions and schema
+├── docs-site/ # This documentation (Mintlify)
+├── docs/ # Internal deployment guides
+└── package.json # Monorepo root
+```
+
+## Common Commands
+
+| Command | Description |
+|---------|-------------|
+| `bun dev` | Start all services (web + Convex) |
+| `bun dev:web` | Start only the web app |
+| `bun dev:server` | Start only Convex |
+| `bun check` | Run linting (oxlint) |
+| `bun check-types` | Type-check all packages |
+| `bun test` | Run tests |
+| `bun build` | Production build |
+
+## Next Steps
-### Step 2: Deploy your changes
+
+
+ Understand how OpenChat is structured
+
+
+ Deep dive into the auth system
+
+
+ Configure models and BYOK
+
+
+ Deploy to production
+
+
+
+## Troubleshooting
-
- Install the Mintlify GitHub app from your [dashboard](https://dashboard.mintlify.com/settings/organization/github-app).
+
+ Make sure you have the Convex CLI available:
+ ```bash
+ bun x convex --version
+ ```
- Our GitHub app automatically deploys your changes to your docs site, so you don't need to manage deployments yourself.
-
-
- For a first change, let's update the name and colors of your docs site.
-
- 1. Open `docs.json` in your editor.
- 2. Change the `"name"` field to your project name.
- 3. Update the `"colors"` to match your brand.
- 4. Save and see your changes instantly at `http://localhost:3000`.
-
- Try changing the primary color to see an immediate difference!
+ If it's your first time, Convex will prompt you to log in. Follow the instructions in the terminal.
+
+
+
+ Verify your callback URL matches exactly:
+ - **Correct**: `http://localhost:3211/api/auth/callback/github`
+ - **Wrong**: `http://localhost:3000/api/auth/callback/github` (wrong port)
+ - **Wrong**: Trailing slash after `github`
+
+ The callback must point to the Convex site URL, not the web app.
+
+
+
+ If port 3000 or 3210 is busy:
+ ```bash
+ # Find what's using the port
+ lsof -i :3000
+
+ # Kill the process
+ kill -9
+ ```
+
+
+
+ Regenerate Convex types:
+ ```bash
+ cd apps/server && bun x convex codegen
+ ```
+
+
+
+ Check that:
+ 1. You're signed in (check the sidebar for your profile)
+ 2. Convex is running (check terminal for errors)
+ 3. If using OSSChat Cloud mode, `OPENROUTER_API_KEY` is set
+ 4. If using BYOK, you've connected your OpenRouter account in Settings
-
-### Step 3: Go live
-
-
- 1. Commit and push your changes.
- 2. Your docs will update and be live in moments!
-
-
-## Next steps
-
-Now that you have your docs running, explore these key features:
-
-
-
-
- Learn MDX syntax and start writing your documentation.
-
-
-
- Make your docs match your brand perfectly.
-
-
-
- Include syntax-highlighted code blocks.
-
-
-
- Auto-generate API docs from OpenAPI specs.
-
-
-
-
-
- **Need help?** See our [full documentation](https://mintlify.com/docs) or join our [community](https://mintlify.com/community).
-
diff --git a/docs-site/self-hosting/docker.mdx b/docs-site/self-hosting/docker.mdx
new file mode 100644
index 00000000..0e20a7de
--- /dev/null
+++ b/docs-site/self-hosting/docker.mdx
@@ -0,0 +1,320 @@
+---
+title: Docker Deployment
+description: Deploy OpenChat with Docker Compose
+---
+
+# Docker Deployment
+
+Deploy OpenChat using Docker Compose for a self-contained, reproducible setup.
+
+## Prerequisites
+
+- Docker 24+
+- Docker Compose plugin
+- Valid credentials for GitHub OAuth and Convex
+
+## Quick Start
+
+
+
+ ```bash
+ git clone https://github.com/tryosschat/openchat.git
+ cd openchat
+ ```
+
+
+
+ Copy and fill in the environment files:
+
+ ```bash
+ cp env.web.example apps/web/.env.local
+ cp env.server.example apps/server/.env.local
+ ```
+
+ Minimum required variables:
+
+ ```bash
+ # apps/web/.env.local
+ VITE_CONVEX_URL=http://convex:3210
+ VITE_CONVEX_SITE_URL=http://localhost:3211
+ GITHUB_CLIENT_ID=your_client_id
+ GITHUB_CLIENT_SECRET=your_client_secret
+ BETTER_AUTH_SECRET=your_32_char_secret
+ OPENROUTER_API_KEY=sk-or-v1-... # Optional for OSSChat Cloud
+ ```
+
+
+
+ ```bash
+ docker compose up --build
+ ```
+
+ Services will be available at:
+ - **Web app**: http://localhost:3001
+ - **Convex**: http://localhost:3210
+ - **Convex dashboard**: http://localhost:6790
+
+
+
+## Container Architecture
+
+```mermaid
+graph TB
+ subgraph "Docker Network"
+ W[web:3001]
+ C[convex:3210]
+ D[dashboard:6790]
+ end
+
+ subgraph "External"
+ U[User Browser]
+ G[GitHub OAuth]
+ O[OpenRouter]
+ end
+
+ U --> W
+ W --> C
+ W --> O
+ C --> G
+```
+
+| Service | Image | Ports | Purpose |
+|---------|-------|-------|---------|
+| `web` | `docker/web.Dockerfile` | 3001 | TanStack Start frontend |
+| `convex` | `docker/convex.Dockerfile` | 3210, 6790 | Convex backend + dashboard |
+
+## Configuration
+
+### docker-compose.yml
+
+```yaml
+version: '3.8'
+
+services:
+ web:
+ build:
+ context: .
+ dockerfile: docker/web.Dockerfile
+ ports:
+ - "3001:3001"
+ environment:
+ - VITE_CONVEX_URL=http://convex:3210
+ - VITE_CONVEX_SITE_URL=http://localhost:3211
+ - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
+ - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
+ - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
+ - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
+ depends_on:
+ - convex
+ networks:
+ - openchat
+
+ convex:
+ build:
+ context: .
+ dockerfile: docker/convex.Dockerfile
+ ports:
+ - "3210:3210"
+ - "6790:6790"
+ volumes:
+ - ./apps/server:/app/apps/server
+ networks:
+ - openchat
+
+networks:
+ openchat:
+ driver: bridge
+```
+
+### Environment Variables
+
+
+
+ | Variable | Description |
+ |----------|-------------|
+ | `GITHUB_CLIENT_ID` | GitHub OAuth app client ID |
+ | `GITHUB_CLIENT_SECRET` | GitHub OAuth app secret |
+ | `BETTER_AUTH_SECRET` | Session encryption key (32+ chars) |
+ | `VITE_CONVEX_URL` | Internal Convex URL |
+ | `VITE_CONVEX_SITE_URL` | Public Convex URL for auth callbacks |
+
+
+ | Variable | Description | Default |
+ |----------|-------------|---------|
+ | `OPENROUTER_API_KEY` | Server OpenRouter key for free tier | - |
+ | `VALYU_API_KEY` | Web search API key | - |
+ | `VITE_POSTHOG_KEY` | PostHog analytics key | - |
+ | `VITE_POSTHOG_HOST` | PostHog host URL | - |
+
+
+
+## Production Deployment
+
+For production, you'll need to:
+
+### 1. Use HTTPS
+
+Place a reverse proxy (Nginx, Caddy, Traefik) in front:
+
+```nginx
+# nginx.conf
+upstream web {
+ server web:3001;
+}
+
+upstream convex {
+ server convex:3210;
+}
+
+server {
+ listen 443 ssl;
+ server_name chat.yourdomain.com;
+
+ ssl_certificate /etc/ssl/certs/fullchain.pem;
+ ssl_certificate_key /etc/ssl/private/privkey.pem;
+
+ location / {
+ proxy_pass http://web;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+}
+
+server {
+ listen 443 ssl;
+ server_name convex.yourdomain.com;
+
+ location / {
+ proxy_pass http://convex;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+}
+```
+
+### 2. Update OAuth Callback URLs
+
+Update your GitHub OAuth app callback URL to use your production Convex domain:
+
+```
+https://convex.yourdomain.com/api/auth/callback/github
+```
+
+### 3. Update Environment Variables
+
+```bash
+VITE_CONVEX_URL=https://convex.yourdomain.com
+VITE_CONVEX_SITE_URL=https://convex.yourdomain.com
+```
+
+### 4. Enable Persistent Storage
+
+Add volumes for data persistence:
+
+```yaml
+convex:
+ volumes:
+ - convex-data:/data
+
+volumes:
+ convex-data:
+```
+
+## Commands
+
+```bash
+# Build and start
+docker compose up --build
+
+# Start in background
+docker compose up -d
+
+# View logs
+docker compose logs -f
+
+# Rebuild specific service
+docker compose build web
+
+# Stop all services
+docker compose down
+
+# Stop and remove volumes
+docker compose down -v
+```
+
+## Health Checks
+
+OpenChat exposes a health endpoint:
+
+```bash
+curl http://localhost:3210/health
+# {"ok":true,"ts":1234567890}
+```
+
+Use this in your monitoring:
+
+```yaml
+convex:
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3210/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+```
+
+## Troubleshooting
+
+
+
+ Check logs for errors:
+ ```bash
+ docker compose logs web
+ docker compose logs convex
+ ```
+
+ Common issues:
+ - Missing environment variables
+ - Port conflicts (3001, 3210 already in use)
+ - Network connectivity issues
+
+
+
+ 1. Verify callback URL matches your `VITE_CONVEX_SITE_URL`
+ 2. Check GitHub OAuth app settings
+ 3. Ensure Convex is accessible from the browser (not just internally)
+
+
+
+ The web app needs two different URLs:
+ - `VITE_CONVEX_URL` - Internal Docker network URL (`http://convex:3210`)
+ - `VITE_CONVEX_SITE_URL` - Browser-accessible URL (`http://localhost:3211`)
+
+ Make sure both are correctly configured.
+
+
+
+ Hot reload for Convex functions works via volume mount:
+ ```yaml
+ volumes:
+ - ./apps/server:/app/apps/server
+ ```
+
+ For the web app, you need to rebuild:
+ ```bash
+ docker compose build web
+ ```
+
+
+
+## Next Steps
+
+
+
+ Complete environment reference
+
+
+ Alternative deployment platform
+
+
diff --git a/docs-site/self-hosting/environment.mdx b/docs-site/self-hosting/environment.mdx
new file mode 100644
index 00000000..cab25166
--- /dev/null
+++ b/docs-site/self-hosting/environment.mdx
@@ -0,0 +1,294 @@
+---
+title: Environment Variables
+description: Complete reference for all OpenChat configuration options
+---
+
+# Environment Variables
+
+This guide covers all environment variables used by OpenChat.
+
+## Quick Reference
+
+| Category | Required | Optional |
+|----------|----------|----------|
+| [Authentication](#authentication) | 4 | 0 |
+| [Convex](#convex) | 2 | 0 |
+| [AI Providers](#ai-providers) | 0 | 2 |
+| [Analytics](#analytics) | 0 | 2 |
+| [Development](#development) | 0 | 2 |
+
+## Web App (`apps/web/.env.local`)
+
+### Authentication
+
+
+ All authentication variables are **required** for the app to function.
+
+
+```bash
+# GitHub OAuth
+GITHUB_CLIENT_ID=your_github_client_id
+GITHUB_CLIENT_SECRET=your_github_client_secret
+
+# Auth encryption key (min 32 characters)
+# Generate with: openssl rand -base64 32
+BETTER_AUTH_SECRET=your_random_secret_min_32_chars
+```
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `GITHUB_CLIENT_ID` | Yes | GitHub OAuth app client ID |
+| `GITHUB_CLIENT_SECRET` | Yes | GitHub OAuth app client secret |
+| `BETTER_AUTH_SECRET` | Yes | Session encryption key (32+ chars) |
+
+
+ Generate a secure secret with:
+ ```bash
+ openssl rand -base64 32
+ ```
+
+
+### Convex
+
+```bash
+# Convex deployment URLs
+VITE_CONVEX_URL=https://your-project.convex.cloud
+VITE_CONVEX_SITE_URL=https://your-project.convex.site
+```
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `VITE_CONVEX_URL` | Yes | Convex deployment URL (for queries/mutations) |
+| `VITE_CONVEX_SITE_URL` | Yes | Convex site URL (for HTTP actions, auth) |
+
+
+ These must use the `VITE_` prefix to be available on the client side (TanStack Start uses Vite).
+
+
+### AI Providers
+
+```bash
+# OpenRouter API key (for OSSChat Cloud free tier)
+OPENROUTER_API_KEY=sk-or-v1-...
+
+# Valyu API key (for web search)
+VALYU_API_KEY=...
+```
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `OPENROUTER_API_KEY` | No | Server's OpenRouter key for free tier |
+| `VALYU_API_KEY` | No | Web search API key |
+
+
+ Without `OPENROUTER_API_KEY`, users must bring their own key (BYOK) to use AI features.
+
+
+### Analytics
+
+```bash
+# PostHog analytics
+VITE_POSTHOG_KEY=phc_...
+VITE_POSTHOG_HOST=https://us.i.posthog.com
+```
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `VITE_POSTHOG_KEY` | No | PostHog project API key |
+| `VITE_POSTHOG_HOST` | No | PostHog ingestion host |
+
+### Development
+
+```bash
+# Auth bypass for testing (NEVER use in production!)
+NEXT_PUBLIC_DEV_BYPASS_AUTH=0
+NEXT_PUBLIC_DEV_USER_ID=dev-user
+```
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `NEXT_PUBLIC_DEV_BYPASS_AUTH` | No | Set to `1` to skip auth (dev only) |
+| `NEXT_PUBLIC_DEV_USER_ID` | No | Mock user ID when bypassing auth |
+
+
+ **NEVER** set `NEXT_PUBLIC_DEV_BYPASS_AUTH=1` in production. This completely disables authentication.
+
+
+## Server (`apps/server/.env.local`)
+
+The Convex backend automatically configures most variables during `convex dev`. You typically only need to add secrets for production.
+
+```bash
+# Production deployment
+CONVEX_DEPLOY_KEY=prod:your-deploy-key
+
+# GitHub OAuth (same as web app)
+GITHUB_CLIENT_ID=your_github_client_id
+GITHUB_CLIENT_SECRET=your_github_client_secret
+```
+
+## Production Checklist
+
+
+
+ - [ ] `GITHUB_CLIENT_ID` - From GitHub OAuth app
+ - [ ] `GITHUB_CLIENT_SECRET` - From GitHub OAuth app
+ - [ ] `BETTER_AUTH_SECRET` - Random 32+ character string
+ - [ ] GitHub callback URL points to production Convex site
+
+
+
+ - [ ] `VITE_CONVEX_URL` - Production Convex URL
+ - [ ] `VITE_CONVEX_SITE_URL` - Production Convex site URL
+ - [ ] Convex deployed to production project
+
+
+
+ - [ ] `OPENROUTER_API_KEY` - If providing free tier
+ - [ ] `VALYU_API_KEY` - If enabling web search
+ - [ ] Rate limits configured for expected traffic
+
+
+
+ - [ ] All secrets in secure secret manager
+ - [ ] `NEXT_PUBLIC_DEV_BYPASS_AUTH` removed or set to `0`
+ - [ ] HTTPS enabled for all endpoints
+ - [ ] Sensitive env vars NOT in client bundle
+
+
+
+## Environment Validation
+
+OpenChat validates environment variables on startup. If required variables are missing, the app will fail to start with a clear error:
+
+```
+❌ Invalid environment variables:
+ - GITHUB_CLIENT_ID: Required
+ - VITE_CONVEX_URL: Invalid url
+```
+
+### Validation Schema
+
+```typescript
+// lib/env.ts
+const serverEnvSchema = z.object({
+ GITHUB_CLIENT_ID: z.string().min(1),
+ GITHUB_CLIENT_SECRET: z.string().min(1),
+ BETTER_AUTH_SECRET: z.string().min(32),
+ OPENROUTER_API_KEY: z.string().optional(),
+ VALYU_API_KEY: z.string().optional(),
+});
+
+const clientEnvSchema = z.object({
+ VITE_CONVEX_URL: z.string().url(),
+ VITE_CONVEX_SITE_URL: z.string().url(),
+ VITE_POSTHOG_KEY: z.string().optional(),
+ VITE_POSTHOG_HOST: z.string().url().optional(),
+});
+```
+
+## Docker / Container Deployment
+
+When deploying with Docker, pass environment variables via:
+
+### docker-compose.yml
+
+```yaml
+services:
+ web:
+ environment:
+ - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
+ - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
+ - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
+```
+
+### .env file
+
+```bash
+# .env (in project root, loaded by docker-compose)
+GITHUB_CLIENT_ID=...
+GITHUB_CLIENT_SECRET=...
+BETTER_AUTH_SECRET=...
+```
+
+### Docker run
+
+```bash
+docker run -e GITHUB_CLIENT_ID=... -e GITHUB_CLIENT_SECRET=... ...
+```
+
+## Secret Management
+
+
+ Never commit secrets to git. Use `.env.local` files (gitignored) or a proper secret manager.
+
+
+### Recommended Secret Managers
+
+| Platform | Solution |
+|----------|----------|
+| Vercel | Vercel Environment Variables |
+| AWS | AWS Secrets Manager, Parameter Store |
+| GCP | Google Secret Manager |
+| Self-hosted | Doppler, Vault, 1Password |
+
+### Example: Using Doppler
+
+```bash
+# Install Doppler
+brew install dopplerhq/cli/doppler
+
+# Login and configure
+doppler login
+doppler setup
+
+# Run with secrets injected
+doppler run -- bun dev
+```
+
+## Troubleshooting
+
+
+
+ Check:
+ 1. File is named `.env.local` (not `.env`)
+ 2. Variables use correct prefix (`VITE_` for client)
+ 3. No syntax errors in the file
+ 4. App was restarted after changes
+
+
+
+ Only variables prefixed with `VITE_` are exposed to the client. This is a Vite security feature.
+
+ ```bash
+ # Available on client
+ VITE_CONVEX_URL=...
+
+ # Server-only (NOT on client)
+ GITHUB_CLIENT_SECRET=...
+ ```
+
+
+
+ Common issue with Docker: internal vs external URLs.
+
+ ```bash
+ # Internal (container-to-container)
+ CONVEX_URL=http://convex:3210
+
+ # External (browser access)
+ VITE_CONVEX_SITE_URL=http://localhost:3211
+ ```
+
+
+
+## Next Steps
+
+
+
+ Deploy with Docker Compose
+
+
+ Configure GitHub OAuth
+
+
diff --git a/docs-site/snippets/snippet-intro.mdx b/docs-site/snippets/snippet-intro.mdx
deleted file mode 100644
index e20fbb6f..00000000
--- a/docs-site/snippets/snippet-intro.mdx
+++ /dev/null
@@ -1,4 +0,0 @@
-One of the core principles of software development is DRY (Don't Repeat
-Yourself). This is a principle that applies to documentation as
-well. If you find yourself repeating the same content in multiple places, you
-should consider creating a custom snippet to keep your content in sync.