# Distribution Guide

## Quick Start

```bash
docker pull ghcr.io/switchboard-core/switchboard-core:latest
docker run -p 8080:80 \
  -e SWITCHBOARD_ADMIN_USERNAME=admin \
  -e SWITCHBOARD_ADMIN_PASSWORD=changeme \
  ghcr.io/switchboard-core/switchboard-core:latest
```

On first run, bundled packages are automatically installed — workflows, surfaces, and extensions are ready to use immediately.

## Bundled Packages

The production Docker image ships with pre-built packages that are auto-installed on first boot:

| Package | Type | Description |
|---------|------|-------------|
| bug-report-triage | workflow | Public entry, severity routing, SLA timers |
| content-approval | workflow | Multi-party signoff example |
| employee-onboarding | workflow | Automated provisioning + manager signoff |
| webhook-notifier | workflow | HTTP outbound via connections + Starlark |
| workflow-demo | surface | Interactive walkthrough with diagrams |
| hello-dashboard | surface | Welcome/getting started surface |
| schedules | surface | Cron task management UI |
| tasks | full | Kanban/list task manager with webhooks |
| team-activity-log | surface | Team activity feed |
| gitea-client | library | Gitea API integration library |
| icd-test-runner | surface | E2E API test suite |
| sdk-test-runner | surface | SDK feature test suite |

### Behavior

- **First boot**: All bundled packages are installed and enabled automatically.
- **Subsequent boots**: No-op — already-installed packages are skipped.
- **Admin uninstalls a package**: It stays uninstalled. Bundled packages are never force-reinstalled.
- **To re-install**: Delete the package from the database, then restart the container.

### Selecting Packages (Allowlist)

Set `BUNDLED_PACKAGES` to a comma-separated list of package IDs to install only a subset:

```bash
# K8s / Helm — install only core surfaces, no demo/test packages
docker run -p 8080:80 \
  -e BUNDLED_PACKAGES="tasks,schedules,hello-dashboard" \
  ghcr.io/switchboard-core/switchboard-core:latest
```

Empty (default) means install all bundled packages. This is useful for Helm charts where different environments need different packages.

### Disabling Auto-Install

Set `SKIP_BUNDLED_PACKAGES=true` to prevent bundled packages from being installed:

```bash
docker run -p 8080:80 \
  -e SKIP_BUNDLED_PACKAGES=true \
  ghcr.io/switchboard-core/switchboard-core:latest
```

### Custom Bundle Directory

Override the default bundled packages location with `BUNDLED_PACKAGES_DIR`:

```bash
docker run -p 8080:80 \
  -e BUNDLED_PACKAGES_DIR=/custom/packages \
  -v /host/packages:/custom/packages \
  ghcr.io/switchboard-core/switchboard-core:latest
```

## Builder Image

The builder image pre-caches Go modules and Node dependencies for faster custom builds.

```bash
docker pull ghcr.io/switchboard-core/builder:latest
```

### What It Caches

- Go module cache (`go mod download` for all server dependencies)
- Node modules for the CM6 editor bundle
- Vendor library tarballs (marked, DOMPurify, mermaid, KaTeX)
- Build tools (zip, Node.js runtime)

### Using in Custom Builds

Reference the builder image as a base stage in your Dockerfile:

```dockerfile
FROM ghcr.io/switchboard-core/builder:latest AS builder
WORKDIR /app
COPY server/ .
RUN go build -ldflags="-s -w" -o /bin/switchboard .
```

### Building Locally

```bash
docker build -f Dockerfile.builder -t switchboard-builder .
```

## Custom Build Guide

### Adding Custom Packages

1. Create your package in `packages/your-package/` with a `manifest.json`
2. Build all packages: `cd packages && bash build.sh all`
3. Build the Docker image: `docker build -t my-switchboard .`

The Dockerfile automatically builds all packages in the `packages/` directory and bundles them into the production image.

### Removing Bundled Packages

To exclude specific packages from the bundle, either:
- Remove them from `packages/` before building
- Set `SKIP_BUNDLED_PACKAGES=true` and install packages manually via the admin UI

### Forking for Custom Builds

```bash
git clone https://github.com/switchboard-core/switchboard-core.git
cd switchboard-core

# Add/modify packages
cp -r my-extension packages/my-extension/

# Build with builder image for faster compilation
docker build -t my-switchboard .
```

## Production Deployment

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `8080` | Backend API port |
| `DB_DRIVER` | (auto) | `postgres` or `sqlite` |
| `DATABASE_URL` | | PostgreSQL connection string |
| `JWT_SECRET` | `dev-secret-change-me` | **Must change in production** |
| `ENCRYPTION_KEY` | | AES-256 key for credential encryption |
| `AUTH_MODE` | `builtin` | `builtin`, `mtls`, or `oidc` |
| `STORAGE_BACKEND` | (auto) | `pvc` or `s3` |
| `STORAGE_PATH` | `/data/storage` | PVC mount point |
| `BASE_PATH` | | URL prefix (e.g. `/switchboard`) |
| `SKIP_BUNDLED_PACKAGES` | `false` | Disable auto-install of bundled packages |
| `BUNDLED_PACKAGES` | (empty = all) | Comma-separated allowlist of package IDs to install |
| `BUNDLED_PACKAGES_DIR` | `/app/bundled-packages` | Override bundled packages location |
| `LOG_FORMAT` | `text` | `text` or `json` |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |

### Database

PostgreSQL is recommended for production. SQLite is suitable for single-instance evaluation.

```bash
# PostgreSQL (recommended)
docker run -p 8080:80 \
  -e DATABASE_URL="postgres://user:pass@host:5432/switchboard?sslmode=require" \
  -e JWT_SECRET="$(openssl rand -hex 32)" \
  -e ENCRYPTION_KEY="$(openssl rand -hex 32)" \
  ghcr.io/switchboard-core/switchboard-core:latest

# SQLite (evaluation only)
docker run -p 8080:80 \
  -e DB_DRIVER=sqlite \
  -v switchboard-data:/data \
  ghcr.io/switchboard-core/switchboard-core:latest
```

### Storage

Object storage is required for file uploads and package asset extraction.

```bash
# PVC (auto-detected if path is writable)
docker run -v switchboard-storage:/data/storage ...

# S3-compatible (MinIO, AWS S3, Ceph)
docker run \
  -e STORAGE_BACKEND=s3 \
  -e S3_BUCKET=switchboard \
  -e S3_ENDPOINT=https://minio.corp:9000 \
  -e S3_ACCESS_KEY=... \
  -e S3_SECRET_KEY=... \
  -e S3_FORCE_PATH_STYLE=true \
  ...
```

### Kubernetes

See the `k8s/` directory for example manifests. Key considerations:

- Use `POSTGRES_HOST`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` env vars (assembled into DSN automatically)
- Set liveness probe to `/healthz/live`, readiness probe to `/healthz/ready`
- Mount a PVC at `/data/storage` for file storage, or configure S3
- Set `JWT_SECRET` and `ENCRYPTION_KEY` via Kubernetes Secrets
