Clawboo is single-tenant and local-first. There are no user accounts, roles, or scopes; the access gate is one shared secret that grants full operator access. See The honest caveats before you expose it to anyone but yourself.
Prerequisites
- A working Clawboo server you can start with
node dist/server.jsorclawboo. See Deployment. - For the recommended path: SSH access to the box, or a reverse proxy (nginx, Caddy) you can terminate TLS on.
- For the wide-bind path: the ability to set environment variables on the process (
HOST,STUDIO_ACCESS_TOKEN).
Decision path
Step 1: Understand the default (do nothing if it fits)
On a fresh install the dashboard binds127.0.0.1, not 0.0.0.0. The host resolver returns loopback unless you explicitly set HOST; an explicit value wins (trimmed), anything else falls back to loopback. So the dashboard and every /api/* route are reachable only from the local machine until you opt out of that. (HOSTNAME is deliberately not a bind signal — Docker/systemd/CI auto-inject it, so honoring it would silently widen the bind inside a container. Use an explicit HOST=.)
localhost, ::1, and the whole 127.0.0.0/8 range count as loopback. 0.0.0.0, ::, a LAN IP, or a hostname are all network-exposed; and binding one of those without a token is what makes the server refuse to start (see below).
If you only need the dashboard from the same machine, and you do not set HOST, you are done. No token, no proxy, nothing else to configure.
Step 2: Reach it from another machine without widening the bind (recommended)
The safest way to reach Clawboo remotely is to keep the bind loopback and put a real transport-security layer in front. Two options: SSH tunnel. Forward a local port on your client to the loopback port on the server. Nothing about Clawboo’s bind changes:127.0.0.1:<port>. Pin the port so the upstream is stable (Port resolution), and widen the same-origin guard to the public hostname you will reach it at:
/api/gateway/ws:
Step 3: Or widen the bind, and set an access token
If you do bind a non-loopback interface, you take on the access gate yourself. SetHOST and STUDIO_ACCESS_TOKEN together (a token-less wide bind refuses to start):
0.0.0.0 is a wildcard, not a name anyone types, so the guard’s automatic bind-host entry covers nothing and a LAN browser 403s until you enumerate the address it actually uses. (Binding that address directly, HOST=192.168.1.10, gets the same two entries for free.) The boot log warns on any non-loopback bind with no CLAWBOO_ALLOWED_ORIGINS.
The token must come from the safe charset [A-Za-z0-9._~-] (which openssl rand -hex satisfies). A token containing any other character is rejected; the gate logs a warning naming the offending character and disables itself rather than ship a permanent lockout, because a cookie-delimiter character would otherwise corrupt the auth cookie and silently 401 every /api/* route. So a “wrong charset” token doesn’t half-work; it turns the gate off. Generate from the safe set and you will never hit this.
Step 4: Know how the gate behaves once it’s on
WhenSTUDIO_ACCESS_TOKEN is set, the access gate is the only authentication on the dashboard. Four behaviors matter for a self-host setup; the security page has the full treatment.
Presenting the token, and the cookie. Open /?access_token=<token> once. The gate validates it, sets an HttpOnly, SameSite=Lax, Path=/ cookie named clawboo_access, and 302-redirects to strip the token from the URL. After that the cookie is the steady-state credential, validated on every /api/* request and on the /api/gateway/ws WebSocket upgrade. A request without a valid cookie gets 401 with a JSON { error } body whose message tells you to open /?access_token=<token> once.
Constant-time, length-hiding compare. The token is never compared byte-by-byte. Both the cookie value and the configured token are SHA-256-hashed to a fixed 32-byte digest first, then compared in constant time; so the check neither short-circuits on the first differing byte (a timing oracle) nor leaks the token’s length.
Case-folded path test. The gate lowercases the pathname before testing the /api/ prefix, so an uppercased /API/settings can’t slip past a case-sensitive prefix check and reach the real handler unauthenticated. The Express app also sets case-sensitive routing so its matcher and the gate agree.
Loopback /api/mcp/* exemption. A spawned runtime attaches its MCP client to http://127.0.0.1:<port>/api/mcp/* with no cookie, by design, because the runtime’s environment is scrubbed of the access token. The gate therefore lets a request through only when it is both loopback (127.0.0.1, ::1, or ::ffff:127.0.0.1 at the TCP socket) and targets /api/mcp/*. A remote client cannot forge a loopback source address on a real TCP handshake. Every other /api/* route, and any non-loopback /api/mcp/* request, still requires the cookie.
The exemption is loopback-scoped at the socket layer. Putting a reverse proxy in front does not widen it: a proxied
/api/mcp/* request arrives from the proxy’s source address (the proxy and the server are both on the box, so it’s still loopback, fine), but a request that genuinely originates off-host can never present a loopback peer address. The runtime control plane stays local-only.Step 5: Terminate TLS so the cookie is Secure
The gate marks the access cookie Secure only when the request arrived over TLS, which it detects from the X-Forwarded-Proto: https header. This is deliberate: a Secure cookie is never sent back over plain HTTP, so adding it unconditionally would break the gate on a loopback/dev HTTP origin.
The practical consequence for a self-host: terminate TLS at your reverse proxy and forward X-Forwarded-Proto. The nginx sketch in Step 2 already sets proxy_set_header X-Forwarded-Proto $scheme;; that single header is what upgrades the cookie to Secure end to end.
Options / variations
See Environment variables for every variable, its reader, and its default.
Verify it worked
- Confirm the bind. Boot the server and read the startup log. A non-loopback bind with no token refuses to start with a
SECURITY:error (unlessCLAWBOO_ALLOW_INSECURE=1, which logs a loud warning instead); a clean boot means you’re either loopback or gated. - Check the access gate. With the gate on,
curl https://your-host/api/settingswithout a cookie should return401. The same request with?access_token=<token>should302-redirect and set theclawboo_accesscookie. A403 Cross-origin request blocked.instead means the always-on same-origin guard rejected the publicHostbefore the gate ever ran; add that hostname toCLAWBOO_ALLOWED_HOSTS. - Confirm Clawboo-shaped health.
GET /api/settingsreturns{ gatewayUrl, hasToken };GET /api/healthreturns the boot probe ({ ok, degraded, fatal, checks, … }).ok: truemeans no fatal checks failed. See Deployment → Verify it worked.
Troubleshooting
Don’t reach for
CLAWBOO_ALLOW_INSECURE=1 “just for a minute.” Every /api/* route, including the ones that resolve provider keys into spawned runtimes, is reachable unauthenticated the moment the bind is non-loopback and no token is set. That’s exactly why a token-less wide bind refuses to start; the opt-out flag disables that safety, so only use it behind your own firewall/proxy.The honest caveats
Clawboo is single-tenant and local-first by design. The access gate makes a deliberately exposed single-user dashboard safe; it is not a multi-user authorization system.- The token is all-or-nothing. Everyone who has it has full operator access. There are no per-user accounts, roles, or scopes, and no per-user revocation; rotate by changing the env var and restarting.
- The vault is defense in depth, not targeted-attacker-proof. It defeats commodity infostealers and the accidental backup or sharing of the vault file, but it does not protect against a process running as you. See Security → the vault.
- Single-tenant only. The
tenant_idcolumns across the schema are a dormant future seam; no per-tenant isolation or scoping is active in v0.3.1. - OpenClaw shared memory is registered globally. Because OpenClaw agents are cross-team, Clawboo registers the shared Memory MCP server for the OpenClaw runtime at global scope rather than per-run/per-team scope (the other four runtimes get per-run team scope). In a multi-tenant world that would need narrowing; it is a documented multi-tenant deferral, not a leak in the single-tenant model Clawboo ships today.
These docs describe Clawboo v0.3.1, the current release.
See also
- Security model and safe exposure, the full security model end to end
- Deploying Clawboo, ports, state dir, host binding, reverse-proxy setup
- Environment variables,
HOST,STUDIO_ACCESS_TOKEN,CLAWBOO_HOME,CLAWBOO_SECRETS_MASTER_KEY, and the rest - Connecting runtimes, the encrypted vault and the credential-resolution chain
- MCP servers, the loopback
/api/mcp/*control plane the gate exempts - Glossary, access gate, vault, loopback, runtime