252 lines
10 KiB
Markdown
252 lines
10 KiB
Markdown
# Chat Switchboard
|
|
|
|
A self-hosted AI chat application for enterprise and government environments. Unified interface for multiple AI providers with admin controls, team management, and security features.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Clone and start
|
|
git clone <repo-url> && cd chat-switchboard
|
|
docker compose up -d
|
|
|
|
# Access at http://localhost:3000
|
|
# Default admin: admin / admin
|
|
```
|
|
|
|
## Features
|
|
|
|
- **Multi-Provider**: Anthropic, OpenAI, OpenRouter, Venice AI — with BYOK support
|
|
- **Team Management**: Roles (admin/user) for vertical permissions, Teams for horizontal visibility
|
|
- **Personas**: Preset configurations (model + system prompt + parameters) at global, team, or personal scope
|
|
- **Model Catalog**: Three-state visibility (enabled/disabled/team-only) with admin controls
|
|
- **Message Trees**: Edit and regenerate with full conversation forking — navigate sibling branches
|
|
- **Notes**: Markdown notes with full-text search, folders, and tags
|
|
- **Extensions**: Browser extension system with custom renderers, tool bridge, and self-contained styling
|
|
- Built-in: Mermaid diagrams, KaTeX math, CSV tables, diff viewer, JS sandbox, regex tester
|
|
- **Server Tools**: Calculator and datetime tools (auto-registered, zero config)
|
|
- **Audit Log**: All admin operations logged with actor, action, and resource details
|
|
- **Security**: JWT + refresh tokens, AES-256-GCM API key encryption, optional mTLS, optional OIDC/Keycloak, environment classification banners
|
|
- **Airgapped**: Local vendor files (marked.js, DOMPurify, KaTeX, mermaid.js), no CDN required
|
|
- **Mobile**: Responsive design, PWA manifest
|
|
- **Real-time**: WebSocket event bus for tool bridge, live updates
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────┐ ┌──────────────────────────────┐
|
|
│ Browser │────▶│ nginx (port 80) │
|
|
│ (vanilla JS)│◀────│ ├─ /api/* → Go backend:8080 │
|
|
└─────────────┘ │ ├─ /ws → WebSocket proxy │
|
|
│ └─ /* → static files │
|
|
└──────────────────────────────┘
|
|
│
|
|
┌─────────▼─────────┐
|
|
│ Go Backend │
|
|
│ ├─ handlers/ │
|
|
│ ├─ store/postgres/ │
|
|
│ ├─ providers/ │
|
|
│ └─ capabilities/ │
|
|
└─────────┬─────────┘
|
|
│
|
|
┌─────────▼─────────┐
|
|
│ PostgreSQL 16 │
|
|
└───────────────────┘
|
|
```
|
|
|
|
**Go backend** (vanilla, no framework beyond Gin) with a store layer abstracting all database access. Providers package handles LLM API calls. Capabilities package resolves model features from catalog data, known model tables, and heuristic inference. Server tools (calculator, datetime) auto-register via `init()`. EventBus + WebSocket hub routes tool calls between backend and browser extensions.
|
|
|
|
**Frontend** is vanilla JavaScript — no build step, no bundler. 15 files organized by domain: `api.js` (HTTP client), `app.js` (state + init), `chat.js` (send, regen, edit, branch), `events.js` (event bus + WebSocket), `extensions.js` (loader, registry, renderer pipeline, tool bridge), `ui-core.js` (DOM rendering + streaming), `ui-format.js` (markdown, code blocks), `ui-primitives.js` (shared components), `ui-settings.js` / `ui-admin.js` (settings and admin panels), `notes.js`, `tokens.js`, `debug.js`, `settings-handlers.js`, `admin-handlers.js`.
|
|
|
|
## Configuration
|
|
|
|
All configuration via environment variables. See `server/.env.example` for the full list.
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `PORT` | `8080` | Backend listen port |
|
|
| `BASE_PATH` | ` ` | URL prefix (e.g. `/chat`) |
|
|
| `DB_HOST` | `localhost` | PostgreSQL host |
|
|
| `DB_NAME` | `chat_switchboard` | Database name |
|
|
| `JWT_SECRET` | (required) | Token signing key |
|
|
| `SWITCHBOARD_ADMIN_USERNAME` | ` ` | Bootstrap admin username |
|
|
| `SWITCHBOARD_ADMIN_PASSWORD` | ` ` | Bootstrap admin password |
|
|
| `ENCRYPTION_KEY` | ` ` | AES key for API key encryption (required if encrypted keys exist) |
|
|
| `SEED_USERS` | ` ` | Dev/test only: `user:pass:role,user2:pass2:role2` |
|
|
|
|
## Deployment
|
|
|
|
Three Docker images support different scenarios:
|
|
|
|
| Image | Dockerfile | Use Case |
|
|
|-------|-----------|----------|
|
|
| **Unified** | `Dockerfile` | Dev, docker-compose, single-node |
|
|
| **Backend** | `server/Dockerfile` | K8s — scale API pods independently |
|
|
| **Frontend** | `Dockerfile.frontend` | K8s — scale FE pods independently |
|
|
|
|
### Docker Compose (development — unified image)
|
|
|
|
```bash
|
|
docker compose up -d # start all services
|
|
docker compose up -d --build # rebuild after code changes
|
|
docker compose --profile dev up # include Adminer DB UI
|
|
```
|
|
|
|
### Kubernetes (split images)
|
|
|
|
Build and push both images:
|
|
|
|
```bash
|
|
docker build -f server/Dockerfile -t your-registry/switchboard-api:0.11.0 server/
|
|
docker build -f Dockerfile.frontend -t your-registry/switchboard-fe:0.11.0 .
|
|
```
|
|
|
|
**Backend deployment:**
|
|
|
|
```yaml
|
|
containers:
|
|
- name: api
|
|
image: your-registry/switchboard-api:0.11.0
|
|
ports:
|
|
- containerPort: 8080
|
|
env:
|
|
- name: DB_HOST
|
|
value: "postgres-service"
|
|
- name: BASE_PATH
|
|
value: "/chat" # must match ingress path
|
|
- name: JWT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: switchboard-secrets
|
|
key: jwt-secret
|
|
```
|
|
|
|
**Frontend deployment:**
|
|
|
|
```yaml
|
|
containers:
|
|
- name: frontend
|
|
image: your-registry/switchboard-fe:0.11.0
|
|
ports:
|
|
- containerPort: 80
|
|
env:
|
|
- name: BASE_PATH
|
|
value: "/chat" # injected into index.html at startup
|
|
volumeMounts:
|
|
- name: branding
|
|
mountPath: /branding
|
|
readOnly: true # optional: custom logo, colors
|
|
```
|
|
|
|
**Ingress** routes `/api/*` and `/ws` to the backend Service, everything else to the frontend Service. The frontend entrypoint generates the nginx config dynamically based on `BASE_PATH`.
|
|
|
|
### Path-Based Routing
|
|
|
|
Set `BASE_PATH=/chat` to serve the application under a subpath. The frontend reads `window.__BASE__` injected at container startup, and all API calls are prefixed automatically.
|
|
|
|
### Airgapped / Disconnected
|
|
|
|
The Docker build bakes in `marked.js`, `DOMPurify`, and `KaTeX` (JS + CSS + fonts) from npm during the vendor stage. Mermaid.js loads dynamically from local vendor with CDN fallback. No CDN calls at runtime in airgapped deployments. The `src/vendor/` directory contains local copies as fallback for development without Docker.
|
|
|
|
## Database
|
|
|
|
PostgreSQL 16+ required. The `pgcrypto` extension is used for `gen_random_uuid()`.
|
|
|
|
### Schema Management
|
|
|
|
The Go backend auto-migrates on startup using files in `server/database/migrations/`. For manual operations:
|
|
|
|
```bash
|
|
# Bootstrap database (superuser, creates role + DB)
|
|
scripts/db-bootstrap.sh
|
|
|
|
# Manual migration (usually not needed)
|
|
scripts/db-migrate.sh
|
|
|
|
# Validate schema
|
|
scripts/db-validate.sh
|
|
```
|
|
|
|
### Key Tables (v0.11)
|
|
|
|
| Table | Purpose |
|
|
|-------|---------|
|
|
| `users` | Accounts with role, avatar, settings, encrypted vault (UEK) |
|
|
| `provider_configs` | API provider configurations (scope: global/team/personal) |
|
|
| `model_catalog` | Synced model list with capabilities, visibility, and type |
|
|
| `personas` | Model presets (scope: global/team/personal) |
|
|
| `channels` | Conversations with type (direct/group/channel) |
|
|
| `messages` | Message tree with parent_id for forking, tool_calls JSONB |
|
|
| `teams` / `team_members` | Organizational units |
|
|
| `notes` | Markdown notes with full-text search |
|
|
| `extensions` / `extension_user_settings` | Browser extension registry and per-user config |
|
|
| `usage_log` / `model_pricing` | Token usage tracking and cost calculation |
|
|
| `audit_log` | Admin action audit trail |
|
|
| `user_model_settings` | Per-user model visibility and sort preferences |
|
|
|
|
## API
|
|
|
|
All endpoints under `/api/v1/`. Authentication via `Authorization: Bearer <token>` header.
|
|
|
|
### Auth
|
|
- `POST /auth/register` — Register (if `allow_registration` policy is true)
|
|
- `POST /auth/login` — Login, returns access + refresh tokens
|
|
- `POST /auth/refresh` — Refresh access token
|
|
- `POST /auth/logout` — Revoke refresh token
|
|
|
|
### Channels & Messages
|
|
- `GET/POST /channels` — List/create conversations
|
|
- `GET/PUT/DELETE /channels/:id` — Channel CRUD
|
|
- `GET/POST /channels/:id/messages` — Message list/create
|
|
- `POST /chat/completions` — Stream AI completions (SSE)
|
|
- `POST /channels/:id/messages/:msgId/edit` — Edit and fork
|
|
- `POST /channels/:id/messages/:msgId/regenerate` — Regenerate response
|
|
|
|
### Models
|
|
- `GET /models/enabled` — Models available to the user (global + team + personal)
|
|
- `GET/PUT /models/preferences` — User model visibility settings
|
|
|
|
### Personal Providers (BYOK)
|
|
- `GET/POST /api-configs` — User's personal provider configs
|
|
- `GET/PUT/DELETE /api-configs/:id` — Provider CRUD
|
|
- `POST /api-configs/:id/models/fetch` — Refresh models from provider API
|
|
- `GET /api-configs/:id/models` — List models for a personal provider
|
|
|
|
### Teams
|
|
- `GET /teams` — Teams the user belongs to
|
|
- `GET/POST /teams/:id/providers` — Team provider management (team admins)
|
|
- `GET/POST /teams/:id/presets` — Team preset management (team admins)
|
|
|
|
### Admin
|
|
- `GET/POST /admin/users` — User management
|
|
- `GET/PUT /admin/settings/:key` — Global settings and policies
|
|
- `GET/POST /admin/configs` — Global provider configs
|
|
- `GET/POST /admin/models/fetch` — Model catalog sync
|
|
- `GET/PUT /admin/roles/:role` — Model role configuration
|
|
- `GET /admin/usage` — Usage dashboard
|
|
- `GET /admin/audit` — Audit log
|
|
- `GET/POST/PUT/DELETE /admin/extensions` — Extension management
|
|
|
|
### Extensions
|
|
- `GET /extensions?tier=browser` — List enabled browser extensions
|
|
- `GET /extensions/:id/assets/*path` — Serve extension assets (public, no auth)
|
|
|
|
### WebSocket
|
|
- `GET /ws?token=<jwt>` — EventBus WebSocket for real-time events and tool bridge
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# Backend (requires Go 1.22+)
|
|
cd server
|
|
cp .env.example .env # edit with your DB credentials
|
|
go run .
|
|
|
|
# Frontend (just serve static files)
|
|
# Use any HTTP server pointed at src/
|
|
python3 -m http.server 3000 --directory src
|
|
```
|
|
|
|
## License
|
|
|
|
Proprietary. All rights reserved.
|