Skip to main content
Use this page when you want to run the Clawboo dashboard server: locally with 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 clawboo npm package ships the server and UI inside its dist/.
  • For dev mode, the monorepo with pnpm install completed.

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.
Or run npx clawboo to try it without installing. What the launcher does, in order:
  1. Informational Gateway probe: TCP-probes localhost:18789 (the OpenClaw Gateway). This is a hint only; it never blocks startup.
  2. Find a running dashboard: findRunningDashboard() checks, in priority order: the CLAWBOO_API_PORT env var, then the runtime port file (~/.clawboo/api-port.txt), then a scan of ports 1879018809. Each candidate is validated with an HTTP GET /api/settings that must return a Clawboo-shaped JSON body (both gatewayUrl: string and hasToken: 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 reads GET /api/system/self-version from it and compares that server’s version against its own before attaching.
  3. Start a server if none is found: the CLI forks the bundled dist/server.js (located next to the CLI entry) with NODE_ENV=production, detached and unref’d. If the bundled server isn’t present, it falls back to a dev-mode npx tsx apps/web/server/index.ts after locating the monorepo root.
  4. 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-sqlite3 native binding can take 20–30 s).
  5. Open the browser at http://localhost:<port>.
If a Clawboo dashboard is already running, clawboo skips the spawn and just opens the browser to the existing instance. Run it again any time to re-open the tab. The exception is a server running an older build than the launcher you invoked: it says so and offers to restart into the newer one, since the server is detached and would otherwise stay bound to the port indefinitely. --no-version-check skips that comparison.

What the bundle contains

The published clawboo 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 tsup noExternal).
  • 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 sets CLAWBOO_MCP_BIN_DIR to dist/bin on the forked server so MCP attach snippets point at them.
Runtime native dependencies kept external from the bundle, 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:
It prints a banner so you always know where things are:
If 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 via resolveApiPort(), 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.
Pin the port with CLAWBOO_API_PORT=18790 when you want a stable URL (a reverse proxy upstream, CI, or running multiple instances on chosen ports). Two unpinned instances coexist fine; the second gets 18791.

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’s own state dir (~/.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:
In production mode the server serves the SPA from 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:
The catch-all serves index.html as a path relative to a root, never as one absolute path. Given an absolute path with no root, Express’s send splits the entire path into segments and 404s if any segment starts with a dot (its default dotfiles: 'ignore'). Because npx installs under ~/.npm/_npx/…, that .npm segment made every deep route and browser refresh 404 for real users while / kept working, since express.static already passes a root. If you ever rewrite this handler, keep the { root } form; serveSpa.test.ts covers both a plain and a dot-containing install path.

Behind a reverse proxy / on a remote box

By default the server binds 127.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.
A non-loopback bind without an access token means the server refuses to start: it logs a SECURITY: error and exits with code 1. This is deliberate; the origin guard is not authentication against a non-browser client (a LAN peer forges Host/Origin freely), so on a wide bind the access token is the only real auth.Fix it by one of: set STUDIO_ACCESS_TOKEN=<random> to require a token; unset HOST to bind loopback only; or set CLAWBOO_ALLOW_INSECURE=1 to run unauthenticated on purpose. Only that last escape hatch reaches the warn-and-keep-serving path.
When 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/settings cannot bypass /api/ to reach a route unauthenticated.
  • Loopback /api/mcp/* requests are exempt (a spawned runtime attaches its MCP client from 127.0.0.1 with no cookie). Non-loopback /api/mcp/* still requires the token.
  • The gate sets Secure on 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.
For the front-end proxy itself, forward both HTTP and the WebSocket upgrade for /api/gateway/ws:
Bind Clawboo to loopback and let the proxy be the only network-facing surface, or bind it wide and protect it with 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: true means 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

CLAWBOO_API_PORT=N is already in use; an explicitly pinned port gets no fallback. Free that port, pick another, or unset CLAWBOO_API_PORT to auto-scan.
clawboo opens a 401 or an unrelated page. Older launchers used a bare TCP probe and could route to the Gateway’s aux ports or Chrome’s debug port. The current launcher validates GET /api/settings, so make sure you are on a build that ships the HTTP-signature probe (v0.1.3+). If a stale api-port.txt points at a dead port, the launcher re-scans; delete the file if it persists.
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.
Last modified on August 10, 2026