If you only run one security command on your OpenClaw instance, make it openclaw doctor. The built-in audit walks your source checkout, running daemon, and gateway config to surface the ugly stuff that usually hides until an attacker finds it first. Below is exactly how I run the doctor on production and staging, how I read its sometimes noisy output, and the settings I change when it flags:

What the openclaw doctor command actually does

Before trusting any scanner you should know what it is doing under the hood. In 0.38.4 (current as I write) doctor runs three passes:

  • Static analysis of claw.config.json, environment variables, and file permissions.
  • Runtime probing against the local gateway REST API (http://localhost:4221 by default) and, if enabled, the daemon’s WebSocket port.
  • Dependency diff – it compares your installed Node modules against the project’s security-advisories.json (sourced from GitHub-SecAlerts).

The tool never phones home. I double-checked with lsof -i and Wireshark; connections stay on 127.0.0.1. Good.

Running openclaw doctor on a live instance

You can point the doctor at any directory that contains claw.config.json. If you omit the path it walks the CWD. On a systemd-managed host I do:

# as the claw user $ cd /srv/openclaw $ NODE_ENV=production npx openclaw@0.38.4 doctor --format pretty

The flags I use most:

  • --format json|pretty|minimal (defaults to minimal)
  • --severity low|medium|high (filters findings)
  • --fix (experimental, modifies config in place; I avoid it in prod)

Expect the scan to finish in 10-20 seconds on a micro VPS and ~3 s on my M3 MacBook. CPU usage spikes to one full core because the dependency diff walks node_modules.

Reading the output without getting lost

The default minimal output is terse – too terse. I switch to pretty so I can see context:

Finding #2 [HIGH] Exposed gateway admin interface Affected: http://0.0.0.0:4221 Remediation: Set GATEWAY_ADMIN_TOKEN or bind to 127.0.0.1 ----------------------------------------

Every finding comes with six fields:

  1. ID – unique until restart.
  2. Severity – low, medium, high, or critical.
  3. Category – config, network, dependency, policy.
  4. Summary.
  5. Affected artifact.
  6. Remediation text.

If you pipe to --format json you can stuff it into SIEM. Example:

$ npx openclaw doctor --format json > /tmp/openclaw_audit.$(date +%s).json

Common issues the doctor flags and how I fix them

Below are the three findings I see most often in the field, plus the exact patches I apply.

1. Risky Direct-Message (DM) policies

OpenClaw ships with dmPolicy: "allow-first" so new users can DM the agent and automatically get sessions. Good for demos, bad for prod Slack or Discord workspaces where random joins happen daily.

Doctor flags it like:

Finding #4 [MEDIUM] Policy allows unrestricted one-to-one sessions File: claw.config.json (line 42) Value: "dmPolicy": "allow-first" Fix: change to "invite-only" or "disabled"

I tighten policy:

{ "dmPolicy": "invite-only", "allowedUsers": ["U02Q1ALPH4", "U08BRAVO9"] }

Re-run doctor, the warning disappears.

Trade-off: New teammates need an explicit invite link or an admin to flip /openclaw invite @user. Slightly more overhead but worth it if your bot is wired into production deploy pipelines via Composio.

2. Misconfigured file system permissions

The installer npm i -g openclaw writes CLI binaries to wherever global NPM lives. People then run the gateway as root because they don’t want to mess with ports <1024. Doctor complains:

Finding #1 [HIGH] Process running as UID 0 (root) Context: systemd unit openclaw-gateway.service Fix: run as non-privileged user and use setcap or a reverse proxy

My fix is two-step:

  1. Create a user: # useradd --system --home /srv/openclaw --shell /usr/sbin/nologin claw
  2. Bind gateway to 127.0.0.1:4221 and let Caddy handle :443 TLS termination.

Unit file excerpt:

[Service] User=claw Group=claw ExecStart=/usr/bin/env NODE_ENV=production /usr/bin/openclaw gateway --bind 127.0.0.1 --port 4221 AmbientCapabilities=CAP_NET_BIND_SERVICE

I give the binary capability with:

# setcap 'cap_net_bind_service=+ep' /usr/bin/node

Doctor becomes silent on UID once I restart the service.

3. Exposed admin interface

By default the admin panel is behind a random 32-char token printed on first boot. Too many users lose that token and simply set GATEWAY_ADMIN_TOKEN= to disable auth. Terrifying.

Doctor catches it:

Finding #2 [CRITICAL] Admin interface unauthenticated Endpoint: http://0.0.0.0:4221/admin Fix: export GATEWAY_ADMIN_TOKEN= or bind to 127.0.0.1

I fix with:

# generate a token a=$(openssl rand -base64 32) # persist sudo tee -a /etc/openclaw/env <<EOF GATEWAY_ADMIN_TOKEN="$a" EOF # restart sudo systemctl restart openclaw-gateway

Optional: also set GATEWAY_ADMIN_IP_WHITELIST=10.0.0.0/24,127.0.0.1/32 for belt and suspenders.

Fixing dependency vulnerabilities flagged by doctor

OpenClaw depends on puppeteer, express, @composio/sdk, and ±184 other packages. Rather than parse the doctor-produced diff by hand I pipe to --format json and feed jq:

$ npx openclaw doctor --severity high --format json | jq -r '.findings[] | "\(.summary) => npm audit fix \(.metadata.package)@^\(.metadata.fixedVersion)"'
  • Run npm audit fix for non-breaking patches.
  • For semver-major bumps I pin via overrides in package.json and schedule a sandbox redeploy to run integration tests.

Once everything is green, I commit package-lock.json and push to the deploy branch. CI executes npx openclaw doctor as a gate.

Advanced: Automating doctor in CI/CD

Because the command doesn’t need Docker-level privileges, it’s safe in CI. My GitHub Actions snippet:

- name: Security audit (OpenClaw doctor) run: | npm ci --omit=optional npx openclaw doctor --severity medium --format json > doctor.json cat doctor.json | jq 'select(.findings | length > 0) and .findings[] | select(.severity == "high" or .severity == "critical")' && exit 1 || echo "No blocking findings"

The step fails if any high/critical remains. Engineers fix the PR or add // doctor:ignore FND-1023 comment next to the offending config line. (The parser strips comments before loading JSON, so they don’t leak to runtime.)

Checklist: what to verify after every scan

  • No findings labelled critical or high.
  • UID/GID is not 0.
  • Admin interface bound to localhost or protected by strong token.
  • dmPolicy is invite-only or stricter.
  • shellAccess is disabled unless you absolutely need it (doctor flags this as medium).
  • Composio API keys rotated in the last 90 days (doctor reads the timestamp if you stored via claw secrets).

Takeaway: make openclaw doctor part of muscle memory

The doctor command isn’t perfect—edge-case false positives for air-gapped hosts still happen—but it’s the fastest way to catch 90 % of self-inflicted wounds. Hook it into CI, keep an eye on new rules in each release, and your future self will have fewer 3 a.m. incident reviews.

If you hit an unclear finding, open an issue on GitHub (#security-label). The core team merges rule tweaks fast, and you’ll help make the scan better for the rest of us.

Next step: schedule a weekly cron job on your production boxes—npx openclaw doctor --format minimal | logger -t openclaw-doctor—and alert on anything above medium. Your audit trail will thank you.