Batch-fetch action widgets so the dashboard can show free/total memory and disk usage without N+1 requests.
ClusterCanvas
A web UI and Go API for managing remote hosts over SSH. You register hosts manually (grouped as containers, VMs, or Docker for navigation), run shell or script actions on them—including scheduled action groups—and administer users, groups, and sessions with a first-run setup wizard, optional TOTP, and audit logs.
Features
- SSH hosts — Register nodes with generated or imported keys, test connectivity, and organize them under Containers / VMs / Docker categories
- Action library — Global shell/script actions (seeded builtins: Uptime, Disk usage, System info) plus custom actions with env vars and optional sudo
- Action groups — Per-node ordered items (library refs or local copies), schedules (interval or clock times),
set_variable/run_if, and run logs - Auth & access — Setup wizard, session cookies, group permissions, optional TOTP, and reauth for sensitive operations
- Activity — Node and auth audit trails, plus per-node action run history
Layout
| Path | Role |
|---|---|
service/ |
Go HTTP API |
webui/ |
Vite + React + TypeScript SPA |
Prerequisites
- Go 1.25+ (toolchain may download a newer Go as needed)
- Node.js 20+ and npm
Configuration
Service config lives under /etc/ClusterCanvas/ by default:
| File | Purpose |
|---|---|
settings.json |
Setup, network, security, and groups (plain JSON) |
passwords.enc |
AES-GCM encrypted user credentials (argon2id hashes, TOTP secrets) |
sessions.enc |
AES-GCM encrypted session store |
secrets.enc |
AES-GCM encrypted secrets placeholder (integrations not wired yet) |
nodes.json |
Registered SSH hosts |
node-keys.enc |
AES-GCM encrypted SSH private keys |
actions.json |
Global action library |
node-action-groups.json |
Per-node action groups and schedules |
node-audit.json |
Node mutation / SSH-test audit trail |
auth-audit.json |
Auth / user / group audit trail |
action-logs/ |
Per-node action run history |
Config directory precedence (highest first):
-configdirflag — e.g../bin/clustercanvas-server -configdir="~/.myconfigs"CLUSTERCANVAS_CONFIG_DIRenvironment variable/etc/ClusterCanvas
A leading ~/ in the path is expanded to the user home directory.
Secrets encryption key: set CLUSTERCANVAS_CONFIG_KEY to a base64-encoded 16-, 24-, or 32-byte key (required for first-run setup and login):
openssl rand -base64 32
export CLUSTERCANVAS_CONFIG_KEY='…paste output…'
For a one-off shell session you can also prefix the command:
CLUSTERCANVAS_CONFIG_KEY="$(openssl rand -base64 32)" make service
# or with make dev:
export CLUSTERCANVAS_CONFIG_KEY="$(openssl rand -base64 32)"
make dev
Keep the same key across restarts if you already ran setup — changing it makes existing passwords.enc / sessions.enc / node-keys.enc unreadable.
First-run setup
On a clean install (setup_completed is false / missing), the web UI opens a setup wizard instead of the dashboard. It creates the admin user, Administrators group, network settings (listen address, optional public hostname, IP allow/deny lists), and security policy, then requires sign-in.
Session cookies use the __Host-ClusterCanvas-Session name with Secure, HttpOnly, SameSite=Strict, and Path=/ (no Domain). Production should set a public hostname and terminate TLS at a reverse proxy so browsers reach the UI over HTTPS. Listen address changes apply on the next process restart.
Run
Start API and web UI together (API :8080, UI :5173; Ctrl+C stops both):
make dev
Or start them separately:
make service # API on :8080 (override with PORT)
make webui # UI on :5173 (proxies /health and /api)
Custom config directory for the API:
cd service && go run ./cmd/server -configdir="$HOME/.myconfigs"
Optional: set VITE_API_BASE_URL (defaults to http://localhost:8080) if you call the API without the Vite proxy. An empty value means same-origin requests (used by make remotedev).
Remote dev (VM without SDKs)
Build on your machine, copy runnables over SSH, and run both processes on a remote host (no Go/Node required there—only OpenSSH).
Prepare host/user once (recommended):
cp remotedev.mk.example remotedev.mk
# edit REMOTEDEV_USER and REMOTEDEV_HOST in remotedev.mk
make remotedev
Or pass them on the CLI:
REMOTEDEV_USER=youruser REMOTEDEV_HOST=192.168.1.50 make remotedev
# or combined: REMOTEDEV_HOST=youruser@192.168.1.50 make remotedev
| Env | Default | Meaning |
|---|---|---|
REMOTEDEV_HOST |
(required) | Host/IP, user@host, or an SSH config Host alias |
REMOTEDEV_USER |
(optional) | Username; combined as user@host when set |
REMOTEDEV_DIR |
clustercanvas-remotedev |
Remote directory (~/… is fine; relative paths are under the remote home) |
REMOTEDEV_CONFIG_KEY |
(baked-in remotedev key) | Dev-only CLUSTERCANVAS_CONFIG_KEY exported on the VM (override in remotedev.mk) |
PORT |
8080 |
API listen port on the VM (all interfaces) |
WEBUI_PORT |
5173 |
Web UI listen port on the VM (all interfaces) |
What lands on the VM: clustercanvas-server, clustercanvas-webui, and web/ (built SPA). Config is stored under $(REMOTEDEV_DIR)/config with the remotedev encryption key so the setup wizard works without extra env setup. Transfer uses scp (OpenSSH only on the remote—no rsync required). Re-running make remotedev stops any previous instance on the VM before uploading so binaries are not busy. The webui binary serves the SPA and proxies /api and /health to the API. Open http://<vm-ip>:5173. Ctrl+C stops both remote processes. A reverse proxy in front of these ports is optional and separate.
Do not use REMOTEDEV_CONFIG_KEY (or its default) in production — generate a unique key with openssl rand -base64 32 instead.
Test
make test
This runs go test ./... in service/ and Vitest in webui/.
Build
make build
Produces bin/clustercanvas-server, bin/clustercanvas-webui, and webui/dist/.