This guide walks through installing rocCLAW and the OpenClaw gateway on a fresh Ubuntu 24.04 machine. rocCLAW is a web-based operator dashboard that proxies all communication between your browser and the OpenClaw gateway.
| Dependency | Minimum Version | Purpose |
|---|---|---|
| Node.js | 20.9.0 | Runtime for Next.js server and build tooling |
| npm | (bundled) | Package manager (ships with Node.js) |
| build-essential | any | C/C++ compiler for native modules (better-sqlite3, ws) |
| python3 | 3.x | Required by node-gyp during native compilation |
| git | any | Clone the repository (source installs only) |
nvm lets you install and switch between Node.js versions without sudo.
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Load nvm into the current shell
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# Install the version pinned in .nvmrc
nvm install 20.9.0
nvm use 20.9.0
# Verify
node --version # should print v20.9.0 or later
npm --versioncurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node --version # should print v20.xbetter-sqlite3 is a native C++ addon compiled during npm install. It requires a C++ toolchain and Python 3.
sudo apt update
sudo apt install -y build-essential python3If you also plan to clone the repo, install git:
sudo apt install -y gitRun rocCLAW directly without cloning the repository:
npx openclaw-rocclaw@latestThis downloads the latest published package, compiles native dependencies, and starts the server on http://localhost:3000.
git clone https://github.com/kiritigowda/rocCLAW.git
cd rocCLAW
npm install
npm run devnpm run dev automatically repairs native module ABI mismatches on startup (via verify-native-runtime.mjs --repair). The dev server runs at http://localhost:3000 with hot reload.
git clone https://github.com/kiritigowda/rocCLAW.git
cd rocCLAW
npm install
npm run build
node server/index.jsOr use the combined command:
npm run start # runs: next build && node server/index.jsThe production server binds to 127.0.0.1 and ::1 on port 3000 by default. Native modules are verified in check mode on startup -- if they fail, rebuild with npm run verify:native-runtime:repair.
rocCLAW connects to the OpenClaw gateway over WebSocket. Install the gateway separately if it is not already running.
# Install the OpenClaw CLI (if not already installed)
npm install -g openclaw
# Start the gateway on the default port
openclaw gateway --port 18789The gateway listens on ws://localhost:18789 by default. For a same-machine setup where both rocCLAW and the gateway run on the same host, no further network configuration is needed.
To verify the gateway is running:
openclaw status --json
openclaw sessions --jsonTo read the gateway auth token (rocCLAW needs this to connect):
openclaw config get gateway.auth.tokenIf the OpenClaw CLI is installed and the gateway is running on the same machine, rocCLAW automatically detects the gateway URL and auth token from ~/.openclaw/openclaw.json on startup. No manual configuration is needed.
- Open
http://localhost:3000in your browser. - Click the Connection tab.
- Enter the gateway URL (e.g.,
ws://localhost:18789). - Enter the gateway auth token.
- Click Connect.
Settings are saved to ~/.openclaw/openclaw-rocclaw/settings.json and persist across restarts.
The interactive setup script prompts for the gateway URL and token, then writes settings.json:
npm run rocclaw:setupIt auto-detects the token from the OpenClaw CLI if available. To overwrite existing settings, add --force:
npm run rocclaw:setup -- --forcerocCLAW binds to loopback (127.0.0.1 / ::1) by default, so it is not reachable from other machines. Two methods for remote access are documented below: SSH tunnels and Tailscale.
SSH tunnels are the simplest way to access a remote rocCLAW instance. They require no extra software on the remote host — just an SSH server. All traffic is encrypted through the SSH connection.
Your laptop Remote server
┌────────────┐ SSH tunnel ┌─────────────────────┐
│ Browser │──────────────────────▶│ rocCLAW (:3000) │
│ :3000 │ │ Gateway (:18789) │
└────────────┘ └─────────────────────┘
SSH -L (local forwarding) binds a port on your local machine and forwards all traffic through the SSH connection to a port on the remote host.
Forward both ports in a single SSH command:
ssh -L 3000:127.0.0.1:3000 -L 18789:127.0.0.1:18789 user@remote-hostThen open http://localhost:3000 in your local browser. rocCLAW connects to the gateway via ws://localhost:18789, which the tunnel forwards to the remote host.
Forward each port to its respective host:
# Terminal 1: Forward rocCLAW
ssh -L 3000:127.0.0.1:3000 user@rocclaw-host
# Terminal 2: Forward the gateway
ssh -L 18789:127.0.0.1:18789 user@gateway-hostOr, if you can reach the gateway host from the rocCLAW host:
# Single command — forward rocCLAW directly, gateway via jump
ssh -L 3000:127.0.0.1:3000 -L 18789:<gateway-host-ip>:18789 user@rocclaw-hostIf you run rocCLAW on your local machine and only the gateway is remote:
ssh -L 18789:127.0.0.1:18789 user@gateway-hostThen set the gateway URL in rocCLAW to ws://localhost:18789.
| Flag | Meaning |
|---|---|
-L local:remote_host:remote |
Forward local port to remote_host:remote through the SSH connection |
-N |
Don't open a shell — just forward ports |
-f |
Run in background after authentication |
-o ServerAliveInterval=30 |
Send a keepalive every 30 seconds to prevent idle disconnects |
-o ExitOnForwardFailure=yes |
Exit if the port forward fails (e.g., port already in use) |
Recommended one-liner (background, keepalive, fail-fast):
ssh -f -N \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes \
-L 3000:127.0.0.1:3000 \
-L 18789:127.0.0.1:18789 \
user@remote-hostautossh monitors the SSH connection and restarts it automatically if it drops. This is essential for long-running setups.
Install:
# Ubuntu / Debian
sudo apt install -y autossh
# macOS
brew install autosshRun:
autossh -M 0 -f -N \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-L 3000:127.0.0.1:3000 \
-L 18789:127.0.0.1:18789 \
user@remote-host-M 0disables autossh's built-in monitoring port and relies on SSH'sServerAliveIntervalinstead (recommended for modern OpenSSH).-fruns in the background after connecting.ServerAliveCountMax=3means the tunnel restarts after 3 missed keepalives (90 seconds).
For a tunnel that starts on boot and restarts on failure, create a systemd user service:
mkdir -p ~/.config/systemd/userCreate ~/.config/systemd/user/rocclaw-tunnel.service:
[Unit]
Description=SSH tunnel to rocCLAW and OpenClaw gateway
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/ssh -N \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-L 3000:127.0.0.1:3000 \
-L 18789:127.0.0.1:18789 \
user@remote-host
Restart=always
RestartSec=5
[Install]
WantedBy=default.targetEnable and start:
systemctl --user daemon-reload
systemctl --user enable rocclaw-tunnel
systemctl --user start rocclaw-tunnel
# Check status
systemctl --user status rocclaw-tunnel
# View logs
journalctl --user -u rocclaw-tunnel -fNote: For systemd services, SSH key-based authentication is required (no interactive password prompts). See the next section.
Password prompts don't work with background tunnels. Set up key-based auth:
# Generate a key (if you don't have one)
ssh-keygen -t ed25519 -C "rocclaw-tunnel"
# Copy the public key to the remote host
ssh-copy-id user@remote-host
# Test — should connect without a password prompt
ssh -o BatchMode=yes user@remote-host echo "Key auth works"# Find the tunnel process
ps aux | grep 'ssh.*-L.*3000'
# Kill by PID
kill <pid>
# Or kill all SSH tunnels to a specific host
pkill -f 'ssh.*remote-host'
# If using systemd
systemctl --user stop rocclaw-tunnelTailscale is a mesh VPN built on WireGuard that gives every device a stable DNS name and encrypted connectivity. It requires no port forwarding, no firewall rules, and no SSH tunnels. Tailscale is optional but recommended for teams or multi-device setups.
Your laptop Tailscale network Remote server
┌────────────┐ ┌─────────────────────┐
│ Browser │◀──── encrypted WireGuard tunnel ────────────────▶│ rocCLAW (:3000) │
│ │ my-server.tailnet-name.ts.net │ Gateway (:18789) │
└────────────┘ └─────────────────────┘
Every machine on the tailnet gets a DNS name like my-server.tailnet-name.ts.net and a stable 100.x.x.x IP address.
Install on every machine that needs to communicate (your laptop, the rocCLAW host, the gateway host):
# Ubuntu / Debian
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# macOS
brew install --cask tailscale
# Then open Tailscale from Applications and sign in
# Other Linux
# See https://tailscale.com/download/linuxAfter tailscale up, it opens a browser to authenticate with your Tailscale account (Google, Microsoft, GitHub, etc.).
# Check your tailnet status
tailscale status
# Example output:
# 100.64.0.1 my-laptop yourname@ linux -
# 100.64.0.2 gpu-server yourname@ linux -
# Ping another device by tailnet name
tailscale ping gpu-serverTailscale Serve lets you expose a local service over HTTPS on your tailnet with automatic TLS certificates.
On the rocCLAW host:
# Expose rocCLAW (port 3000) over HTTPS on port 443
tailscale serve --bg --https 443 http://127.0.0.1:3000
# Verify it's serving
tailscale serve statusThen open https://<rocclaw-host>.tailnet-name.ts.net from any device on your tailnet.
If the OpenClaw gateway runs on a different machine:
On the gateway host:
# Expose the gateway WebSocket port
tailscale serve --bg --tcp 18789 tcp://127.0.0.1:18789Then set the gateway URL in rocCLAW to:
wss://<gateway-host>.tailnet-name.ts.net
Note: When using Tailscale Serve with HTTPS, use
wss://(secure WebSocket) instead ofws://.
# Full DNS name
tailscale status --json | jq -r '.Self.DNSName'
# Or just the hostname part
tailscale status --json | jq -r '.Self.HostName'The DNS name format is: <hostname>.<tailnet-name>.ts.net
By default, all devices on your tailnet can reach each other. For tighter security, use Tailscale ACLs to restrict which devices can access rocCLAW:
- Go to Tailscale Admin Console
- Add rules like:
{
"acls": [
{
"action": "accept",
"src": ["tag:admin"],
"dst": ["tag:rocclaw:3000", "tag:rocclaw:18789"]
}
],
"tagOwners": {
"tag:admin": ["yourname@example.com"],
"tag:rocclaw": ["yourname@example.com"]
}
}- Tag your devices:
# On the rocCLAW host
sudo tailscale up --advertise-tags=tag:rocclawIf both rocCLAW and the gateway are on the same machine and you only need Tailscale for remote access to the rocCLAW UI:
# Only expose rocCLAW — the gateway stays on localhost
tailscale serve --bg --https 443 http://127.0.0.1:3000Set the gateway URL in rocCLAW to ws://localhost:18789 (no Tailscale needed for the gateway connection since they're on the same host).
# Remove a specific serve rule
tailscale serve --https 443 off
# Remove all serve rules
tailscale serve reset
# Check what's being served
tailscale serve status| Problem | Fix |
|---|---|
tailscale up hangs |
Check if a firewall blocks UDP 41641. Tailscale uses WireGuard which needs UDP connectivity. |
| DNS name not resolving | Run tailscale status to verify the device is online. Check that MagicDNS is enabled in the admin console. |
wss:// connection fails |
Ensure you're using wss:// (not ws://) when connecting through Tailscale Serve HTTPS. |
| Certificate errors | Tailscale auto-provisions Let's Encrypt certs. Wait a minute after tailscale serve and retry. |
| Can't reach device | Check ACLs in the admin console. Default policy allows all traffic, but custom ACLs may block it. |
| Factor | SSH tunnels | Tailscale |
|---|---|---|
| Setup effort | None (SSH is already available) | Install on each device + sign in |
| Extra software | None (autossh optional) | Tailscale client on every device |
| DNS names | No — use IP addresses | Yes — stable *.ts.net names |
| TLS/HTTPS | No — HTTP over tunnel | Yes — automatic certificates |
| Multi-device | One tunnel per device | All devices on the tailnet connect directly |
| Firewall/NAT | Requires SSH access (port 22) | Works through most NATs without port forwarding |
| Persistence | Manual (autossh/systemd) | Automatic (daemon runs on boot) |
| Best for | Single user, existing SSH access | Teams, multiple devices, long-term setups |
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP port for the rocCLAW server |
HOST |
127.0.0.1 |
Bind address. Set to 0.0.0.0 for LAN access (requires ROCCLAW_ACCESS_TOKEN) |
ROCCLAW_ACCESS_TOKEN |
(none) | Cookie-based access token. Required when binding to a public/non-loopback address |
NEXT_PUBLIC_GATEWAY_URL |
ws://127.0.0.1:18789 |
Default gateway URL when no saved settings exist |
OPENCLAW_STATE_DIR |
~/.openclaw |
Override the base directory for all OpenClaw/rocCLAW state |
OPENCLAW_SKIP_NATIVE_RUNTIME_VERIFY |
(unset) | Set to 1 to skip the native module ABI check on startup |
To set these, either export them in your shell or create a .env file in the project root (see .env.example).
rocCLAW creates these paths automatically on first run. All paths are relative to the state directory (~/.openclaw by default).
| Path | Purpose |
|---|---|
~/.openclaw/openclaw-rocclaw/settings.json |
Gateway URL, token, and UI preferences |
~/.openclaw/openclaw-rocclaw/device.json |
Ed25519 keypair for gateway authentication (file mode 0600) |
~/.openclaw/openclaw-rocclaw/state.db |
SQLite database (WAL mode) for event projection and outbox |
~/.openclaw/openclaw.json |
Shared OpenClaw config (gateway port, auth token). Written by the OpenClaw CLI, read by rocCLAW for auto-detection |
Symptom: Startup crashes or npm install fails with syntax errors.
Fix: rocCLAW requires Node.js 20.9.0 or later. Check with node --version. If using nvm:
nvm install 20.9.0
nvm use 20.9.0Symptom: npm install fails with errors like gyp ERR! build error or make: not found.
Fix:
sudo apt install -y build-essential python3
rm -rf node_modules
npm installSymptom: Error on startup: The module was compiled against a different Node.js version or NODE_MODULE_VERSION mismatch.
This happens when you switch Node.js versions after npm install.
Fix:
npm run verify:native-runtime:repairOr manually rebuild:
rm -rf node_modules
npm installSymptom: rocCLAW loads but shows "Disconnected" or "Connection failed" in the UI.
Fix: Start the gateway:
openclaw gateway --port 18789Verify it is running:
openclaw status --jsonSymptom: rocCLAW connects but the gateway rejects the handshake.
Fix: The gateway requires an auth token. Retrieve it and enter it in the Connection tab:
openclaw config get gateway.auth.tokenOr re-run the setup script:
npm run rocclaw:setup -- --forceSymptom: Errors mentioning Ed25519, device identity, or challenge-response signing.
Fix: Delete the device keypair file and restart. rocCLAW generates a new one automatically:
rm ~/.openclaw/openclaw-rocclaw/device.jsonEnsure the directory has correct permissions:
ls -la ~/.openclaw/openclaw-rocclaw/device.json
# Should show -rw------- (0600)Symptom: Error: listen EADDRINUSE: address already in use :::3000.
Fix: Either stop the other process using port 3000, or start rocCLAW on a different port:
PORT=3001 npm run devSymptom: rocCLAW is on the same machine as the gateway, but the gateway URL is set to a LAN IP (e.g., ws://192.168.1.50:18789) and connections fail or are slow.
Fix: When both services are on the same machine, always use ws://localhost:18789 or ws://127.0.0.1:18789. The gateway typically binds to loopback only.
Symptom: Server refuses to start with the error: Refusing to bind ROCclaw to public host "0.0.0.0" without ROCCLAW_ACCESS_TOKEN.
Fix: When binding to a non-loopback address (e.g., HOST=0.0.0.0), you must set an access token:
ROCCLAW_ACCESS_TOKEN=your-secret-token HOST=0.0.0.0 npm run devThen open the dashboard once with the token in the URL to set the auth cookie:
http://<your-ip>:3000/?access_token=your-secret-token