-
-
Notifications
You must be signed in to change notification settings - Fork 54.4k
Description
Summary
openclaw node install generates a LaunchAgent plist that does not include OPENCLAW_GATEWAY_TOKEN in the EnvironmentVariables section, even when the env var is set during installation. This causes the node service to fail with gateway token mismatch on every restart/reboot, since the runner code falls back to reading gateway.auth.token from openclaw.json — which may not match the remote gateway's token in a multi-machine setup.
Context
This affects the documented architecture where:
- Machine A (e.g., Docker container on a server) runs the gateway
- Machine B (e.g., a Mac) runs as a headless node connected to the remote gateway via Tailscale/TLS
The node host authenticates to the gateway using OPENCLAW_GATEWAY_TOKEN. When running openclaw node run manually with the env var, everything works. But openclaw node install doesn't persist this env var into the service definition.
Steps to reproduce
-
On Machine B (node), set the gateway token and install:
OPENCLAW_GATEWAY_TOKEN="<remote-gateway-token>" openclaw node install \ --host gateway.example.ts.net --port 443 --tls --display-name "Mac"
-
Check the generated plist:
grep "GATEWAY_TOKEN" ~/Library/LaunchAgents/ai.openclaw.node.plist
Result: No output —
OPENCLAW_GATEWAY_TOKENis not present. -
Start the service:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.node.plist
-
Check logs:
cat ~/.openclaw/logs/node.err.log | tail -5
Result:
node host gateway connect failed: unauthorized: gateway token mismatch (provide gateway auth token) gateway connect failed: Error: unauthorized: gateway token mismatch (provide gateway auth token) node host gateway closed (1008): unauthorized: gateway token mismatch (provide gateway auth token)
Expected behavior
openclaw node install should include OPENCLAW_GATEWAY_TOKEN in the plist's EnvironmentVariables when:
- The env var is set during
node install, OR - A
--tokenflag is provided tonode install, OR - The token is read from
node.json(via a newgatewayTokenfield)
Actual behavior
The generated plist contains many OPENCLAW_* env vars (service markers, labels, etc.) but not OPENCLAW_GATEWAY_TOKEN. The node service starts, cannot authenticate to the remote gateway, and enters a reconnect loop.
Root cause
In src/node-host/runner.ts, the token is correctly resolved at runtime:
const token =
process.env.OPENCLAW_GATEWAY_TOKEN?.trim() ||
(isRemoteMode ? cfg.gateway?.remote?.token : cfg.gateway?.auth?.token);But the service installer that generates the plist/systemd unit does not capture OPENCLAW_GATEWAY_TOKEN from the current environment into the service definition.
The fallback path (gateway.auth.token from openclaw.json) doesn't help because:
- On a node-only machine,
openclaw.jsonmay have a different token (from a previous local gateway setup) - Or
openclaw.jsonmay not have agateway.auth.tokenat all - The
node.jsonschema (NodeHostConfig) has no field for the gateway token
Workaround
Manually patch the plist after installation:
# After openclaw node install:
sed -i '' 's|<key>OPENCLAW_SERVICE_KIND</key>|<key>OPENCLAW_GATEWAY_TOKEN</key>\
<string>YOUR_TOKEN_HERE</string>\
<key>OPENCLAW_SERVICE_KIND</key>|' ~/Library/LaunchAgents/ai.openclaw.node.plistThen reload:
launchctl bootout gui/$(id -u)/ai.openclaw.node 2>/dev/null
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.node.plistThis workaround is fragile — any subsequent openclaw node install --force will overwrite the plist and remove the token again.
Suggested fix
Any of these would work:
-
Capture
OPENCLAW_GATEWAY_TOKENfrom env during install — If the env var is set whennode installruns, include it in the generated plist/systemd unitEnvironmentVariables. -
Add
--tokenflag tonode install— Similar to howgateway installhandles--token. Example:openclaw node install --host gw.example.net --port 443 --tls --token "<token>" -
Add
gatewayTokentonode.json— ExtendNodeHostConfigschema to include agatewayTokenfield, and have the runner read it as a fallback.
Option 2 is the cleanest UX and consistent with gateway install.
Related issues
- LaunchAgent plist gets redacted env vars on service install #13340 — LaunchAgent plist gets redacted env vars on
gateway install(similar category: service install loses auth credentials) - [Bug]:
openclaw node installtries to enableopenclaw-gateway.serviceinstead of node service #13642 —openclaw node installtries to enableopenclaw-gateway.serviceinstead of node service (node install code path undertested)
Environment
- OpenClaw version: 2026.2.26 (node) / 2026.2.27 (gateway)
- OS: macOS 26.x (arm64) — node; Linux WSL2 Docker — gateway
- Install method: npm global (node), Docker (gateway)
- Connection: Tailscale Serve (wss:// via
.ts.netdomain) - Node type: headless (
openclaw node run/openclaw node install)