clawboo, from the monorepo in dev mode, or on a remote/headless box behind a reverse proxy. Clawboo is a single Express server that serves both the SPA and every /api/* route, plus a WebSocket proxy at /api/gateway/ws.
These docs describe Clawboo v0.3.1, the current release.
Prerequisites
- Node.js 22+ (the CLI and server both declare
engines.node >= 22). - For the bundled path, nothing else; the
clawboonpm package ships the server and UI inside itsdist/. - For dev mode, the monorepo with
pnpm installcompleted.
At a glance
The default bind is loopback only (
127.0.0.1). A fresh install is never reachable from another host until you explicitly set HOST (and HOST alone; HOSTNAME is ignored, see below).
Bundled mode: clawboo
Run bare, clawboo is a thin launcher. It probes for an already-running dashboard, starts one if needed, then opens your browser at the resolved URL. clawboo stop and clawboo restart reach that server afterwards; see the CLI reference.
npx clawboo to try it without installing.
What the launcher does, in order:
- Informational Gateway probe: TCP-probes
localhost:18789(the OpenClaw Gateway). This is a hint only; it never blocks startup. - Find a running dashboard:
findRunningDashboard()checks, in priority order: theCLAWBOO_API_PORTenv var, then the runtime port file (~/.clawboo/api-port.txt), then a scan of ports18790–18809. Each candidate is validated with an HTTPGET /api/settingsthat must return a Clawboo-shaped JSON body (bothgatewayUrl: stringandhasToken: boolean), so an unrelated listener in that range (Gateway aux ports, Chrome’s--remote-debugging-port) is never mistaken for Clawboo. When one is found, the launcher then readsGET /api/system/self-versionfrom it and compares that server’s version against its own before attaching. - Start a server if none is found: the CLI forks the bundled
dist/server.js(located next to the CLI entry) withNODE_ENV=production, detached andunref’d. If the bundled server isn’t present, it falls back to a dev-modenpx tsx apps/web/server/index.tsafter locating the monorepo root. - Poll for readiness: it re-runs discovery every 500 ms for up to 45 seconds (a cold Windows first-boot of the bundled CJS plus the
better-sqlite3native binding can take 20–30 s). - Open the browser at
http://localhost:<port>.
What the bundle contains
The publishedclawboo package ships everything the server needs in dist/:
dist/index.js: the CLI launcher.dist/server.js: the single-file bundled server (Express, the API router, the SPA static host, all@clawboo/*libraries inlined via tsupnoExternal).dist/ui/: the Vite build output served as the SPA.dist/bin/{tasks,memory,tools,teamchat}.js: the four MCP stdio bins (see MCP servers). The CLI setsCLAWBOO_MCP_BIN_DIRtodist/binon the forked server so MCP attach snippets point at them.
better-sqlite3, ws, pino, pino-pretty, are declared as dependencies of the clawboo package and installed by npm.
Dev mode: pnpm dev
pnpm dev runs the API server and the Vite SPA together. A small orchestrator (apps/web/scripts/dev-orchestrator.cjs) picks a free API port up front and exports it as CLAWBOO_API_PORT, so the API and Vite agree on the port without a race:
18790 was busy it appends (18790 was busy — picked next free port). The orchestrator then runs pnpm dev:api (tsx watch server/index.ts --dev) and pnpm dev:ui (vite) concurrently. Vite serves the SPA on :5173 and proxies /api (and the /api/gateway/ws upgrade) to the Express API on the resolved port. Vite resolves that port from CLAWBOO_API_PORT, then the runtime port file, then the default 18790.
In dev mode the server enables CORS (
cors({ origin: true })) because the SPA origin (:5173) differs from the API origin. In production the SPA and API share one origin, so CORS is off.Port resolution
The Express server picks its port at boot viaresolveApiPort(), in this priority order:
The default
18790 sits one above the OpenClaw Gateway’s 18789, in the uncommonly-used 18000–18999 range, so it sidesteps the “port 3000 is already taken” failure mode.
After a successful bind the server writes the chosen port to a runtime port file at ~/.clawboo/api-port.txt. External tools (the CLI, the Vite dev proxy, e2e helpers) read this file to discover the port without scanning. The file is removed on graceful shutdown (SIGINT/SIGTERM/exit); a stale file is harmless because every consumer probes the port before trusting it.
The state directory
Clawboo owns one state directory, default~/.clawboo, overridable with CLAWBOO_HOME (~-expansion applies). Everything Clawboo writes lives under it:
There is no migration ladder. The schema is the
CREATE TABLE IF NOT EXISTS DDL in ensureSchema (packages/db/src/schemaBootstrap.ts), and a version bump reconciles an existing database up to it on first open, adding any columns it is missing, so an image bump needs no data migration step. That covers additive changes only: a release that rewrote or removed an existing column would still need the reset below, and would say so in its notes. To wipe state, run clawboo stop, delete ~/.clawboo (or just ~/.clawboo/clawboo.db), then re-run onboarding. See Upgrading an existing database.~/.openclaw, set by OPENCLAW_STATE_DIR) is read-only interop; Clawboo reads the Gateway config and a provider-key fallback from it but never writes there.
Running standalone (no CLI)
To run just the server, for a systemd unit, a container, or a PaaS dyno:CLAWBOO_UI_DIR (default: the ui/ directory next to server.js) via express.static, with a GET catch-all that returns index.html so client-side routing works. Two overrides matter for non-standard layouts:
Behind a reverse proxy / on a remote box
By default the server binds127.0.0.1, so it is unreachable off-host. To expose it (a remote box, a container, behind nginx/Caddy), set HOST:
resolveHost() returns the trimmed HOST when set, otherwise loopback. A bind to 0.0.0.0/:: reports a browser URL of http://localhost:<port> in the log, but the listener accepts connections on all interfaces.
HOSTNAME is deliberately ignored. Only HOST widens the bind. Docker, systemd, and many CI runners auto-inject HOSTNAME into every process env, so honouring it would silently expose a container that never asked to be exposed. Setting HOSTNAME=0.0.0.0 leaves the server on loopback.STUDIO_ACCESS_TOKEN is set, the access gate protects every /api/* route. A few load-bearing details for proxy setups:
- The token charset is restricted to
[A-Za-z0-9._~-]. A token with any other character disables the gate (and logs a warning) rather than silently locking you out, so generate tokens from that set (e.g.openssl rand -hex 24). - The pathname prefix check is case-folded;
/API/settingscannot bypass/api/to reach a route unauthenticated. - Loopback
/api/mcp/*requests are exempt (a spawned runtime attaches its MCP client from127.0.0.1with no cookie). Non-loopback/api/mcp/*still requires the token. - The gate sets
Secureon its cookie only when the request arrived over TLS (X-Forwarded-Proto: https). If you terminate TLS at the proxy, forward that header so the secure-cookie path engages.
/api/gateway/ws:
STUDIO_ACCESS_TOKEN, full details in Security.
Verify it worked
- Hit
GET /api/settings; a 200 with{ gatewayUrl, hasToken }confirms the server is up and Clawboo-shaped. - Hit
GET /api/health, the boot probe report ({ ok, degraded, fatal, checks, ... }).ok: truemeans no fatal checks failed; the report also surfaces the resolved state dir, vault, db, and port. - Confirm the port file exists:
cat ~/.clawboo/api-port.txt.
Troubleshooting
Deleting
~/.clawboo deletes all Clawboo state: the database (board, memory, registry), the encrypted vault, and worktrees. There is no undo; a delete is a clean reset that re-triggers onboarding.Related
- Security, access gate, device auth, the vault, redaction, safe exposure
- MCP servers, attaching the bundled MCP bins
- Environment variables,
CLAWBOO_API_PORT,CLAWBOO_HOME,CLAWBOO_UI_DIR,HOST,STUDIO_ACCESS_TOKEN, and the rest clawbooCLI reference, theclawboocommand, its subcommands, and the MCP bins- Configuration,
settings.jsonand file locations