Skip to main content
Clawboo ships one user-facing command and four MCP stdio binaries. Run bare, clawboo is a launcher: it finds or starts the bundled dashboard server, checks what version it found, and opens a browser. Three subcommands sit alongside that default action, and each exists because the server runs detached: taking a consistent copy of a live database, clearing an instance you can no longer Ctrl-C, and rolling a running instance onto a newer build are all things a detached process makes awkward. Everything else, onboarding, Gateway detection, runtime connection, and team deployment, still happens in the web UI. The MCP bins are not launched by humans; an external agent runtime spawns them to attach Clawboo’s board, memory, tools, and team-chat surfaces over stdio.
These docs describe Clawboo v0.3.1, the current release.

At a glance

All five are declared in the package’s bin map and shipped in the published dist/ tarball. The clawboo binary carries one default action and three subcommands:

clawboo

The launcher is built on Commander: one default action, the backup / stop / restart subcommands, and the two flags Commander provides automatically (--version and --help). The runtime is Node 22+ (the package engines field requires node >=22.0.0).

Flags

--no-version-check and -y apply to the default action only. clawboo backup adds -f, --force; clawboo restart adds --no-open; clawboo stop takes no flags. Beyond that the launch flow is not configurable on the command line; it is steered by environment variables.

What clawboo does

Running clawboo with no flags executes the launch sequence below.
  1. Print the banner. An ASCII logo, the tagline, and an intro line showing Clawboo v<version>.
  2. Informational Gateway probe. A TCP probe of localhost:18789 (the OpenClaw Gateway’s default port). The result is purely informational; it prints “OpenClaw Gateway detected” or “No Gateway detected; the dashboard will guide you through setup.” It does not gate anything; the dashboard handles Gateway setup.
  3. Find a running dashboard. findRunningDashboard() looks for an existing Clawboo server (see Port discovery).
  4. Compare versions. When a server is already running, the launcher reads GET /api/system/self-version from it and compares that response’s current field to its own compiled-in version. Three outcomes: same or newer, attach silently; older, print a short notice and offer to restart into this build; unreachable, unparseable, or either side reading 0.0.0* (a dev checkout), attach silently. The check is deliberately fail-open: a version it cannot read must never stand between you and an open browser. --no-version-check skips it entirely.
  5. Start the server if none is running, choosing one of two strategies:
    • Bundled mode (primary): if server.js exists next to the CLI entry, it is forked detached with NODE_ENV=production, cwd set to the CLI’s own directory, CLAWBOO_VERSION set to the launcher’s version, and CLAWBOO_MCP_BIN_DIR pointed at the sibling bin/ directory (so the server’s /api/mcp/config can emit node <bin> stdio attach snippets). The child is unref’d so the CLI can exit while the server keeps running.
    • Dev mode (fallback): if there is no bundled server but a monorepo root (a package.json with "name": "clawboo") is found, the launcher spawns npx tsx apps/web/server/index.ts detached with NODE_ENV=production.
    • Neither found: it prints a warning to npm install -g clawboo and exits 1.
  6. Poll for the port. After spawning, it polls findRunningDashboard() every 500 ms for up to 90 attempts (≈45 s). The long window accommodates a cold first boot of the bundled server on Windows (Defender scanning the freshly-extracted package plus Node’s first-load module compile). On timeout it fails the spinner and exits 1.
  7. Open the browser. Resolves http://localhost:<port> and opens it with the platform launcher (open on macOS, start on Windows, xdg-open elsewhere).
  8. Print next steps. A success outro with pointers to the dashboard and docs.
The version check exists because the server is spawned detached and unref’d. A dashboard started weeks ago on an older build stays bound to the port, and every later clawboo run attached to it, so upgrading the package changed nothing until you rebooted. Reading the running server’s own version closes that loop.Note what this is not: it compares the running server against the launcher you just invoked, not against npm. The “a newer release exists upstream” check is a separate, server-side surface; the same endpoint also returns latest and updateAvailable, which the dashboard’s update chip renders. The launcher asks for ?local=1, which tells the server to skip its registry probe, so the comparison stays local and adds no network round-trip. See GET /api/system/self-version for the full payload.
The CLI never connects to a Gateway, never reads a Gateway token, and never mutates Clawboo state. backup opens the database read-only and writes its copy where you point it; stop and restart act on a process, not on data. Everything that owns state, the board, the vault, settings, stays behind the dashboard server.

Port discovery

findRunningDashboard() mirrors the server’s port resolver and probes for a Clawboo dashboard in this priority order:
  1. CLAWBOO_API_PORT: if set to a valid port (1–65535), probe only that port.
  2. Runtime port file: read <CLAWBOO_HOME>/api-port.txt (default ~/.clawboo/api-port.txt), which the server writes on successful bind, and probe it.
  3. Range scan: probe 18790, then scan upward through 20 consecutive ports (1879018809).
The default port is 18790 (one above the OpenClaw Gateway’s 18789). Every command that needs a server shares this one resolver: the default action uses it to decide whether to spawn, and stop / restart use it to decide what to terminate, so they act on exactly the instance clawboo would have attached to. backup does not use it at all; it goes straight to the database file, which is why it works with no server running. Each probe is not a bare TCP check. probeClawbooDashboard() does a cheap TCP probe first, then an HTTP GET /api/settings and validates a Clawboo-shaped JSON body (the response must include both a gatewayUrl string and a hasToken boolean). This signature check is load-bearing: the fallback range 1879018809 overlaps the OpenClaw Gateway’s auxiliary ports (1879118792) and Chrome’s --remote-debugging-port (commonly 18800), and a naive TCP probe would route the browser at one of those (a 401 page, a DevTools target list). The signature check rejects any non-Clawboo listener regardless of what else is bound in the range. stop re-runs it immediately before resolving a process to terminate, for the same reason.
The launcher’s resolution comment lists CLAWBOO_API_URL as an override, but no code reads it; only CLAWBOO_API_PORT is honored. Use CLAWBOO_API_PORT.

Token-gated installs

Setting STUDIO_ACCESS_TOKEN turns on the access gate, and every /api/* route then requires a cookie the launcher does not have — including the GET /api/settings that discovery uses. A gated server therefore cannot be attached to from the command line. The gate answers with a Clawboo-specific JSON error rather than a bare 401, so the launcher can still tell that a Clawboo dashboard is there. It says so and stops, instead of concluding nothing is running and starting a second server onto the same database. clawboo stop and clawboo restart report the same thing and print the manual command; they deliberately do not terminate a process on a port they could not positively identify. To use the dashboard, open http://localhost:<port>/?access_token=<token> once to set the cookie. To restart a gated server without unsetting the token, stop it by hand and start a fresh one:
Or unset STUDIO_ACCESS_TOKEN and use clawboo restart normally.

clawboo backup

Writes a single, checkpoint-consistent copy of ~/.clawboo/clawboo.db. Safe while the dashboard is live: it opens the source read-only and uses better-sqlite3’s online .backup(), which reads pages through the shared WAL and produces one self-contained file with no -wal/-shm sidecars. A plain cp of clawboo.db on a running server can miss everything still sitting in the WAL; that gap is the reason this subcommand exists. The unlink under --force is load-bearing rather than tidy: .backup() opens its destination as a SQLite database, so it fails SQLITE_NOTADB against a non-SQLite file and would leave stale trailing pages behind on a smaller pre-existing one. Deleting first guarantees a clean single-file image. The source is resolveClawbooDir()/clawboo.db, the same file the server uses, honoring CLAWBOO_HOME. If it does not exist the command errors out rather than writing an empty database; start the server once to create it, or point CLAWBOO_HOME at the right directory. See Data and state for the full backup and restore model, including what a whole-directory copy captures that a database-only backup does not.

clawboo stop

Terminates the running dashboard server.
stop resolves the server through the same port discovery chain the launcher uses, so it stops exactly the instance clawboo would have attached to (CLAWBOO_API_PORT=<n> clawboo stop targets one explicitly). With no Clawboo dashboard running it says so, clears a stale api-port.txt if one is left over, and exits 0: stopping something already stopped is not an error, so this is safe in a teardown script. It exits 1 only when a server is running and could not be terminated.
api-port.txt records a port, not a PID, and the detached server writes no PID file of its own. stop therefore resolves port to owning process the way the server resolves the Gateway’s: lsof -nP -iTCP:<port> -sTCP:LISTEN -t on Unix, netstat -ano filtered to listening rows on Windows. On a machine with neither tool on PATH the lookup returns nothing and stop reports that it could not identify the process, and prints the manual command, rather than guessing at one.It then sends SIGTERM and watches the port, not the PID, for up to 4 seconds before escalating to SIGKILL. A PID can be recycled; a port that stopped answering cannot be misread. Before escalating it re-resolves the listener and only force-kills a process that is still the one holding the port.
Stopping the server stops everything that lives inside it: in-flight agent runs, the server-side delegation engine, scheduled Routines, and the background reconcilers. Nothing is lost, the board is durable and boot-resume re-arms every active routine on the next start, but a run in progress is interrupted.On Windows there is no graceful signal for a detached console process: Node’s process.kill maps to TerminateProcess, so the server’s shutdown handler does not run. stop cleans up api-port.txt itself in that case, and the SQLite WAL is crash-safe and recovered on the next open.

clawboo restart

Stops the running server and starts a fresh one on the same port.
This is stop followed by a launch, with one difference that matters: the successor is pinned to the same port the old server held (CLAWBOO_API_PORT) and told to wait for that port to free before binding (CLAWBOO_AWAIT_PORT). Without the wait, the successor would race the exiting process and either fail loudly on a pinned port or drift to the next free one, orphaning any browser tab already open on the old URL. With it, an open tab reconnects on its own. If the port turns out not to be free after the stop (something else claimed it in the gap), the successor is started without a pin and falls back to ordinary port resolution — an auto-scan, or your own CLAWBOO_API_PORT if you have one exported, in which case the boot still fails loudly on the taken port. Reach for it after a global upgrade (npm install -g clawboo@latest replaces the bytes on disk, but not the process already running the old ones), after changing an environment variable the server reads at boot, or any time you want a clean process without hunting one down. It is also what the launcher offers to run for you when it finds an older server during discovery. With nothing running, restart simply starts a server, the same convention systemctl restart follows. If the stop fails it exits 1 and deliberately does not start a replacement: a pinned start would fail on the still-taken port, and an unpinned one would leave you with two dashboards.
In a monorepo checkout the dev server is usually supervised by pnpm dev, which will respawn it. Restart through your dev script there rather than through clawboo restart.

Environment variables honored

These are the only environment variables the clawboo launcher itself reads or sets. The bundled server it starts reads many more; see Environment variables.

Example

Or run npx clawboo to try it without installing.

MCP stdio bins

Each clawboo-mcp-* bin is a standalone Node script that runs one MCP server over stdio. They exist so an external agent runtime (for example, a Codex or Claude Code process you configure yourself) can attach Clawboo’s coordination surfaces as MCP tools without going through the dashboard. The dashboard server hosts the same servers in-process and over Streamable HTTP; the stdio bins are the standalone path. All four open the shared Clawboo SQLite database via createDb(defaultDbPath()). defaultDbPath() returns ~/.openclaw/clawboo/clawboo.db unless CLAWBOO_DB_PATH overrides it. Opening the same file the in-process server serves is safe because the database is created in WAL mode with the multi-process contention recipe; a bin spawned by an external runtime and the Express server read and write the one file concurrently. For the tool list and zod input shapes each server registers, see the MCP tools reference.

Packaging

The bins are bundled self-contained (the MCP SDK, @clawboo/db, and drizzle are inlined) so they run from a clean Clawboo install. Only native and process-level deps stay external (better-sqlite3, ws, pino, pino-pretty) and are resolved from the CLI’s installed dependencies; OpenTelemetry stays external and is lazily loaded, so the bins never require it at boot. The #!/usr/bin/env node shebang is preserved on each.

Attaching from a runtime

You normally do not invoke these by hand. The server’s GET /api/mcp/config?runtime=&server=&transport=stdio emits a ready-to-paste attach snippet. For the stdio transport that requires the server to know where the built bins live; the clawboo launcher sets CLAWBOO_MCP_BIN_DIR to the sibling bin/ directory on the bundled server, and the config endpoint joins that dir with <server>.js to produce a { command: "node", args: [<binPath>] } invocation. See the Tools & MCP API for the config endpoint and the transport options.

Honored environment variables

The memory bin additionally consults embedding-provider env vars at boot; those are documented under Environment variables, not here.

Example

See also

Last modified on August 6, 2026