🔀 Chat Switchboard
Multi-Model AI Chat Platform — Self-Hosted, Extensible, Fast
A self-hosted AI chat interface that routes conversations across multiple LLM providers. Built with a Go backend for performance and a clean vanilla JS frontend inspired by Open WebUI.
What It Does
- Multi-provider routing — Connect OpenAI, Anthropic, Venice.ai, Ollama, or any OpenAI-compatible API. Switch models per-conversation.
- Streaming responses — SSE-based streaming with stop button, thinking block display, and full markdown rendering.
- Per-user API keys — Each user manages their own provider keys. Admins can set global keys shared across the instance.
- Admin panel — User management, global provider config, model visibility, registration toggle, usage stats.
- Self-hosted — Single Docker image, PostgreSQL backend. No external dependencies at runtime.
Quick Start
Docker (Recommended)
git clone https://git.gobha.me/xcaliber/chat-switchboard.git
cd chat-switchboard
cp server/.env.example server/.env
# Edit .env: set DATABASE_URL, JWT_SECRET, etc.
docker-compose up -d
Access at http://localhost:3000. First registered user becomes admin.
Manual
# Backend
cd server
cp .env.example .env
go build -o switchboard .
./switchboard
# Frontend — serve src/ with any HTTP server
cd ../src
python3 -m http.server 8080
Point the frontend at the backend by setting the API base URL (defaults to same-origin).
Architecture
┌──────────────────────────┐
│ Frontend (Vanilla JS) │
│ 4 files + vendor libs │
│ No build step required │
└───────────┬──────────────┘
│ REST + SSE
▼
┌──────────────────────────┐
│ Go Backend │
│ ├── Auth (JWT + refresh)│
│ ├── Chat CRUD │
│ ├── Completion proxy │
│ ├── Provider management │
│ ├── Admin endpoints │
│ └── Static file server │
└───────────┬──────────────┘
│
PostgreSQL
Frontend (src/)
| File | Size | Purpose |
|---|---|---|
api.js |
240 lines | HTTP client, token management, auto-refresh on 401 |
app.js |
490 lines | State, init flow, auth, chat CRUD, event wiring |
ui.js |
510 lines | DOM rendering, streaming, modals, formatting |
debug.js |
550 lines | Console/network intercept, state inspector (Ctrl+Shift+L) |
vendor/ |
62KB | marked.js + DOMPurify (local, CDN fallback) |
No framework. No build step. No node_modules. Works in disconnected environments with vendor libs baked in.
Backend (server/)
Go 1.22, ~26 source files. Key packages:
handlers/— Auth, chats, messages, completions, API configs, adminmiddleware/— JWT auth, admin role check, rate limiting, error handling, loggingproviders/— OpenAI and Anthropic adapters with streaming supportmodels/— Database modelsdatabase/— PostgreSQL connection + migration runnerconfig/— Environment-based configuration
API Surface
| Route | Method | Auth | Description |
|---|---|---|---|
/health |
GET | — | Health check + version |
/api/v1/auth/register |
POST | — | Create account |
/api/v1/auth/login |
POST | — | Get JWT tokens |
/api/v1/auth/refresh |
POST | — | Rotate access token |
/api/v1/chats |
GET/POST | ✓ | List / create chats |
/api/v1/chats/:id |
GET/PUT/DELETE | ✓ | Chat CRUD |
/api/v1/chats/:id/messages |
GET | ✓ | Message history |
/api/v1/chat/completions |
POST | ✓ | Streaming SSE or sync JSON |
/api/v1/models/enabled |
GET | ✓ | Available models |
/api/v1/api-configs |
GET/POST/DELETE | ✓ | User provider management |
/api/v1/profile |
GET/PUT | ✓ | User profile |
/api/v1/settings |
GET/PUT | ✓ | User settings (persisted) |
/api/v1/admin/* |
various | admin | User/provider/model/settings management |
Configuration
All via environment variables (see server/.env.example):
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Backend listen port |
DATABASE_URL |
— | PostgreSQL connection string |
JWT_SECRET |
— | Token signing key |
JWT_EXPIRY |
15m |
Access token TTL |
REFRESH_EXPIRY |
7d |
Refresh token TTL |
CORS_ORIGINS |
* |
Allowed origins |
REGISTRATION_ENABLED |
true |
Allow new signups |
Deployment
Docker Compose (unified)
The provided Dockerfile is a 3-stage build:
golang:1.22-bookworm— compiles Go backendnode:20-alpine— downloads vendor JS libs vianpm packnginx:1-alpine— serves frontend, proxies/api/to Go backend
Vendor libs are baked into the image at build time — no CDN access needed at runtime.
Reverse Proxy
If running behind nginx/Caddy, proxy /api/ and /health to the Go backend. Serve src/ as static files.
Development
# Backend (hot reload with air)
cd server
go install github.com/air-verse/air@latest
air
# Frontend — just edit files, hard-refresh browser
# Debug: Ctrl+Shift+L opens debug modal
Database Migrations
Migrations in migrations/ run automatically on startup. Current schema:
001_full_schema.sql— users, chats, messages, api_configs002_refresh_tokens.sql— token rotation003_global_settings.sql— admin settings table004_model_configs.sql— per-model enable/disable
Roadmap
See ROADMAP.md for the full plan. Next up:
- WebSocket hub — real-time message delivery, typing indicators
- Channels — multi-user + AI chat rooms with @mentions
- Plugin system — Go-native extensions, installable via admin UI
- Notes & Knowledge Base — markdown notes, document upload, RAG via pgvector
License
MIT — build anything, including commercial products.
Repository: https://git.gobha.me/xcaliber/chat-switchboard