This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/README.md
2026-02-25 21:38:49 +00:00

261 lines
11 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` |
| `STORAGE_BACKEND` | (auto) | `pvc` or `s3`. Auto-detects PVC if not set. |
| `STORAGE_PATH` | `/data/storage` | PVC mount point (also scratch dir for S3 extraction) |
| `S3_ENDPOINT` | ` ` | S3 endpoint (e.g. `http://minio:9000`, required for S3) |
| `S3_BUCKET` | ` ` | S3 bucket name (must exist, required for S3) |
| `S3_ACCESS_KEY` | ` ` | S3 access key ID |
| `S3_SECRET_KEY` | ` ` | S3 secret access key |
| `S3_REGION` | `us-east-1` | S3 region |
| `S3_PREFIX` | ` ` | Optional key prefix within bucket |
| `S3_FORCE_PATH_STYLE` | `true` | Path-style URLs (required for MinIO, Ceph) |
## 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.