4.2 KiB
| read_when | ||
|---|---|---|
|
Deployment
ClickClack ships as one Go binary that embeds the Svelte SPA and the SQL migrations. The deployment story is "drop a binary on a box, point it at a data directory, run it behind a reverse proxy."
Public surfaces:
clickclack.chat— product website.app.clickclack.chat— chat app. The same app is also available at/appfor local development and simple single-host deployments.docs.clickclack.chat— documentation site built bypnpm docs:site.
Single binary
pnpm install
pnpm build # builds the SPA into apps/api/internal/webassets/dist
go build -o clickclack ./apps/api/cmd/clickclack
./clickclack serve --addr :8080 --data /var/lib/clickclack
The Go build step requires the SPA dist/ to be present because webassets
uses go:embed. The pnpm build script copies apps/web/dist into
apps/api/internal/webassets/dist; CI must run it before go build.
Releases
GoReleaser is configured in .goreleaser.yml. It builds clickclack for
Linux, macOS, Windows, and FreeBSD on amd64 and arm64, with Windows
archives emitted as .zip and the others as .tar.gz. Linux .deb and
.rpm packages are generated through nfpm.
pnpm install
goreleaser release --snapshot --clean
The GoReleaser config runs pnpm build before compiling so the embedded SPA
is refreshed. Publishing is handled by .github/workflows/release.yml on
v* tags or manual dispatch with an existing tag.
Docker
The provided Dockerfile is multi-stage:
docker build -t clickclack .
docker run --rm -p 8080:8080 -v clickclack-data:/app/data clickclack
Stages:
node:25-alpine— installs pnpm dependencies and runspnpm build.golang:1.26-alpine— builds the Go binary, importing the SPA dist.alpine:3.23— runtime image, runs as theclickclackuser, exposes8080, mounts/app/dataas a volume.
Override the entrypoint command to run admin tasks:
docker run --rm -v clickclack-data:/app/data clickclack \
admin bootstrap --name "Peter" --email steipete@gmail.com
Data layout
<data>/
clickclack.db # SQLite database (WAL files alongside)
uploads/ # local files for /api/uploads
logs/ # reserved; nothing writes here today
Back this directory up. SQLite WAL means a snapshot of the directory is consistent enough, but prefer the online backup:
clickclack backup --data /var/lib/clickclack --out /var/backups/clickclack-$(date +%F).db
Reverse proxy
Required for TLS, Origin enforcement, and request size limits. The
WebSocket endpoint accepts upgrades with InsecureSkipVerify=true today, so
the proxy is the right place to enforce origin policy.
A minimal nginx block:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
GitHub OAuth
If you want GitHub login, set:
CLICKCLACK_PUBLIC_URL=https://chat.example.com
CLICKCLACK_GITHUB_CLIENT_ID=...
CLICKCLACK_GITHUB_CLIENT_SECRET=...
CLICKCLACK_GITHUB_ALLOWED_ORG=openclaw
CLICKCLACK_DEV_BOOTSTRAP=false
Configure the GitHub OAuth app callback to
<public-url>/api/auth/github/callback. When CLICKCLACK_GITHUB_ALLOWED_ORG
is set, ClickClack asks GitHub for read:org and only accepts active members
of that org. See features/auth.md.
Migrations
clickclack serve applies migrations on boot. For zero-downtime deploys, run
clickclack migrate ahead of the new binary so the old binary doesn't see
unexpected tables. Migrations live in
apps/api/internal/store/sqlite/migrations/ and are append-only.
Backups and restore
# hot backup
clickclack backup --out /var/backups/clickclack-$(date +%F).db
# JSON dump (good for sanity, not for restore)
clickclack export --out /var/backups/clickclack-$(date +%F).json
Restore is a file swap: stop clickclack, replace <data>/clickclack.db,
delete any stale *.db-wal/*.db-shm, start it back up.