Curate default bundled packages to core set of 8
Previously all 23 bundled .pkg archives auto-installed on first boot. Now only the curated default set installs: notes, chat-core, workflow-chat, dashboard, and 4 demo workflows. Other packages still ship in the image and can be enabled via BUNDLED_PACKAGES env var (or "*" for all). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,42 +14,66 @@ On first run, bundled packages are automatically installed — workflows, surfac
|
|||||||
|
|
||||||
## Bundled Packages
|
## Bundled Packages
|
||||||
|
|
||||||
The production Docker image ships with pre-built packages that are auto-installed on first boot:
|
The production Docker image ships with pre-built packages. A **curated default set** is auto-installed on first boot; additional packages ship in the image and can be opted-in via `BUNDLED_PACKAGES`.
|
||||||
|
|
||||||
|
#### Default Set (auto-installed)
|
||||||
|
|
||||||
| Package | Type | Description |
|
| Package | Type | Description |
|
||||||
|---------|------|-------------|
|
|---------|------|-------------|
|
||||||
|
| notes | surface | Markdown notes with graph view |
|
||||||
|
| chat-core | library | Conversations, messages, read cursors |
|
||||||
|
| workflow-chat | extension | Chat integration for workflow stages |
|
||||||
|
| dashboard | surface | Dashboard surface |
|
||||||
|
| workflow-demo | surface | Interactive walkthrough with diagrams |
|
||||||
| bug-report-triage | workflow | Public entry, severity routing, SLA timers |
|
| bug-report-triage | workflow | Public entry, severity routing, SLA timers |
|
||||||
| content-approval | workflow | Multi-party signoff example |
|
| content-approval | workflow | Multi-party signoff example |
|
||||||
| employee-onboarding | workflow | Automated provisioning + manager signoff |
|
| employee-onboarding | workflow | Automated provisioning + manager signoff |
|
||||||
| webhook-notifier | workflow | HTTP outbound via connections + Starlark |
|
|
||||||
| workflow-demo | surface | Interactive walkthrough with diagrams |
|
#### Opt-in (ship in image, require `BUNDLED_PACKAGES` to enable)
|
||||||
| hello-dashboard | surface | Welcome/getting started surface |
|
|
||||||
| schedules | surface | Cron task management UI |
|
| Package | Type | Description |
|
||||||
|
|---------|------|-------------|
|
||||||
| tasks | full | Kanban/list task manager with webhooks |
|
| tasks | full | Kanban/list task manager with webhooks |
|
||||||
|
| schedules | surface | Cron task management UI |
|
||||||
|
| editor | surface | Rich markdown editor |
|
||||||
|
| hello-dashboard | surface | Welcome/getting started surface |
|
||||||
| team-activity-log | surface | Team activity feed |
|
| team-activity-log | surface | Team activity feed |
|
||||||
|
| webhook-notifier | workflow | HTTP outbound via connections + Starlark |
|
||||||
|
| csv-table | extension | CSV table renderer |
|
||||||
|
| diff-viewer | extension | Diff visualization |
|
||||||
|
| git-board | surface | Git board interface |
|
||||||
| gitea-client | library | Gitea API integration library |
|
| gitea-client | library | Gitea API integration library |
|
||||||
|
| js-sandbox | extension | JavaScript sandbox |
|
||||||
|
| katex-renderer | extension | LaTeX rendering |
|
||||||
|
| mermaid-renderer | extension | Diagram rendering |
|
||||||
|
| regex-tester | extension | Regex testing tool |
|
||||||
| icd-test-runner | surface | E2E API test suite |
|
| icd-test-runner | surface | E2E API test suite |
|
||||||
| sdk-test-runner | surface | SDK feature test suite |
|
| sdk-test-runner | surface | SDK feature test suite |
|
||||||
|
|
||||||
### Behavior
|
### Behavior
|
||||||
|
|
||||||
- **First boot**: All bundled packages are installed and enabled automatically.
|
- **First boot**: Curated default packages are installed and enabled automatically.
|
||||||
- **Subsequent boots**: No-op — already-installed packages are skipped.
|
- **Subsequent boots**: No-op — already-installed packages are skipped.
|
||||||
- **Admin uninstalls a package**: It stays uninstalled. Bundled packages are never force-reinstalled.
|
- **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.
|
- **To re-install**: Delete the package from the database, then restart the container.
|
||||||
|
|
||||||
### Selecting Packages (Allowlist)
|
### Selecting Packages (Allowlist)
|
||||||
|
|
||||||
Set `BUNDLED_PACKAGES` to a comma-separated list of package IDs to install only a subset:
|
Set `BUNDLED_PACKAGES` to control which packages are installed:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# K8s / Helm — install only core surfaces, no demo/test packages
|
# Install ALL packages (everything in the image)
|
||||||
docker run -p 8080:80 \
|
docker run -p 8080:80 \
|
||||||
-e BUNDLED_PACKAGES="tasks,schedules,hello-dashboard" \
|
-e BUNDLED_PACKAGES="*" \
|
||||||
|
ghcr.io/switchboard-core/switchboard-core:latest
|
||||||
|
|
||||||
|
# Install specific packages only
|
||||||
|
docker run -p 8080:80 \
|
||||||
|
-e BUNDLED_PACKAGES="notes,tasks,schedules" \
|
||||||
ghcr.io/switchboard-core/switchboard-core:latest
|
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.
|
Empty (default) installs the curated default set. Use `*` to install all packages. This is useful for Helm charts where different environments need different packages.
|
||||||
|
|
||||||
### Disabling Auto-Install
|
### Disabling Auto-Install
|
||||||
|
|
||||||
@@ -149,7 +173,7 @@ docker build -t my-switchboard .
|
|||||||
| `STORAGE_PATH` | `/data/storage` | PVC mount point |
|
| `STORAGE_PATH` | `/data/storage` | PVC mount point |
|
||||||
| `BASE_PATH` | | URL prefix (e.g. `/switchboard`) |
|
| `BASE_PATH` | | URL prefix (e.g. `/switchboard`) |
|
||||||
| `SKIP_BUNDLED_PACKAGES` | `false` | Disable auto-install of bundled packages |
|
| `SKIP_BUNDLED_PACKAGES` | `false` | Disable auto-install of bundled packages |
|
||||||
| `BUNDLED_PACKAGES` | (empty = all) | Comma-separated allowlist of package IDs to install |
|
| `BUNDLED_PACKAGES` | (empty = defaults) | `""` curated defaults, `"*"` all, or comma-separated IDs |
|
||||||
| `BUNDLED_PACKAGES_DIR` | `/app/bundled-packages` | Override bundled packages location |
|
| `BUNDLED_PACKAGES_DIR` | `/app/bundled-packages` | Override bundled packages location |
|
||||||
| `LOG_FORMAT` | `text` | `text` or `json` |
|
| `LOG_FORMAT` | `text` | `text` or `json` |
|
||||||
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
|
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
|
||||||
|
|||||||
@@ -63,11 +63,13 @@ type Config struct {
|
|||||||
MTLSAutoActivate bool // auto-activate new users (default true)
|
MTLSAutoActivate bool // auto-activate new users (default true)
|
||||||
MTLSDefaultTeam string // team ID for auto-provisioned users (optional)
|
MTLSDefaultTeam string // team ID for auto-provisioned users (optional)
|
||||||
|
|
||||||
// Bundled packages (v0.3.8)
|
// Bundled packages (v0.3.8, curated v0.5.4)
|
||||||
// SKIP_BUNDLED_PACKAGES: set true to disable auto-install of bundled packages on first run.
|
// SKIP_BUNDLED_PACKAGES: set true to disable auto-install of bundled packages on first run.
|
||||||
// BUNDLED_PACKAGES_DIR: directory containing pre-built .pkg archives (default /app/bundled-packages).
|
// BUNDLED_PACKAGES_DIR: directory containing pre-built .pkg archives (default /app/bundled-packages).
|
||||||
// BUNDLED_PACKAGES: comma-separated allowlist of package IDs to install (empty = all).
|
// BUNDLED_PACKAGES: controls which bundled packages are auto-installed:
|
||||||
// e.g. "tasks,schedules,hello-dashboard" installs only those three.
|
// empty → curated default set (notes, chat-core, workflow-chat, dashboard, demo workflows)
|
||||||
|
// "*" → install all .pkg archives found in the directory
|
||||||
|
// "a,b" → comma-separated allowlist of specific package IDs
|
||||||
SkipBundledPackages bool
|
SkipBundledPackages bool
|
||||||
BundledPackagesDir string
|
BundledPackagesDir string
|
||||||
BundledPackages string
|
BundledPackages string
|
||||||
|
|||||||
@@ -22,13 +22,28 @@ import (
|
|||||||
"switchboard-core/triggers"
|
"switchboard-core/triggers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// defaultBundledPackages is the curated set of packages installed by default.
|
||||||
|
// Other packages still ship in the Docker image but require BUNDLED_PACKAGES
|
||||||
|
// to be set explicitly (or "*" for all).
|
||||||
|
var defaultBundledPackages = map[string]bool{
|
||||||
|
"notes": true,
|
||||||
|
"chat-core": true,
|
||||||
|
"workflow-chat": true,
|
||||||
|
"dashboard": true,
|
||||||
|
"workflow-demo": true,
|
||||||
|
"bug-report-triage": true,
|
||||||
|
"content-approval": true,
|
||||||
|
"employee-onboarding": true,
|
||||||
|
}
|
||||||
|
|
||||||
// InstallBundledPackages scans bundledDir for .pkg archives and installs
|
// InstallBundledPackages scans bundledDir for .pkg archives and installs
|
||||||
// any that don't already exist in the database. Called once at startup
|
// any that don't already exist in the database. Called once at startup
|
||||||
// (unless SKIP_BUNDLED_PACKAGES=true).
|
// (unless SKIP_BUNDLED_PACKAGES=true).
|
||||||
//
|
//
|
||||||
// allowlist is an optional comma-separated list of package IDs to install.
|
// allowlist controls which packages are installed:
|
||||||
// Empty string means install all. This allows Helm/K8s deployments to
|
// - Empty string → install the curated default set (see defaultBundledPackages)
|
||||||
// select a subset: e.g. "tasks,schedules,hello-dashboard".
|
// - "*" → install all .pkg archives found in the directory
|
||||||
|
// - Comma-separated IDs → install only those (e.g. "tasks,schedules")
|
||||||
//
|
//
|
||||||
// Design: install-once, skip-if-present. If an admin uninstalls a bundled
|
// Design: install-once, skip-if-present. If an admin uninstalls a bundled
|
||||||
// package, it won't be re-installed on the next restart.
|
// package, it won't be re-installed on the next restart.
|
||||||
@@ -43,11 +58,15 @@ func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse allowlist into a set (empty = allow all)
|
// Parse allowlist: "" → curated defaults, "*" → all, "a,b" → explicit list
|
||||||
allowed := parseBundleAllowlist(allowlist)
|
allowed := parseBundleAllowlist(allowlist)
|
||||||
if len(allowed) > 0 {
|
if allowed != nil {
|
||||||
|
if allowlist == "" || allowlist == " " {
|
||||||
|
log.Printf("[bundled] Using curated default set (%d packages)", len(defaultBundledPackages))
|
||||||
|
} else {
|
||||||
log.Printf("[bundled] Allowlist: %v", allowlist)
|
log.Printf("[bundled] Allowlist: %v", allowlist)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var installed, skipped, filtered int
|
var installed, skipped, filtered int
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
@@ -87,10 +106,16 @@ func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseBundleAllowlist parses a comma-separated string into a set of
|
// parseBundleAllowlist parses a comma-separated string into a set of
|
||||||
// package IDs. Returns nil for empty input (meaning "allow all").
|
// package IDs. Returns:
|
||||||
|
// - nil for "*" input (meaning "install all")
|
||||||
|
// - defaultBundledPackages for empty input
|
||||||
|
// - explicit set for comma-separated list
|
||||||
func parseBundleAllowlist(s string) map[string]bool {
|
func parseBundleAllowlist(s string) map[string]bool {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
if s == "" {
|
if s == "" {
|
||||||
|
return defaultBundledPackages
|
||||||
|
}
|
||||||
|
if s == "*" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
parts := strings.Split(s, ",")
|
parts := strings.Split(s, ",")
|
||||||
|
|||||||
Reference in New Issue
Block a user