Summary
Cron announce delivery fails on bind: "loopback" setups because the internal delivery path constructs http://127.0.0.1:<port> but isSecureWebSocketUrl() only accepts ws:// and wss:// protocols. The http:// URL falls through all protocol checks and throws SECURITY ERROR: Gateway URL uses plaintext ws:// to a non-loopback address.
Environment
- OpenClaw version: 2026.3.2
- Setup: bare-metal VM (Ubuntu 24.04), systemd user service
- Config:
gateway.bind: "loopback", gateway.auth.mode: "token", OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1
Steps to reproduce
- Configure gateway with
bind: "loopback" and auth.mode: "token"
- Create a cron job with
delivery.mode: "announce" and delivery.channel: "telegram"
- Wait for cron to fire (or force-run with
openclaw cron run <id> --url ws://127.0.0.1:18789 --token <token>)
- Agent turn completes (
lastStatus: ok) but delivery fails (lastDelivered: false)
Expected behavior
Cron announce delivery should succeed — the gateway and subagent are co-located on loopback.
Actual behavior
Log shows:
Subagent completion direct announce failed: SECURITY ERROR: Gateway URL "http://127.0.0.1:18789"
uses plaintext ws:// to a non-loopback address.
The announce delivery path internally constructs http://127.0.0.1:18789 (HTTP protocol), but isSecureWebSocketUrl() in net-Bf8Z-b6p.js (and 6 other chunk files where it's duplicated) only checks for ws: and wss: protocols:
// net-Bf8Z-b6p.js line 249
function isSecureWebSocketUrl(url, opts) {
// ...
if (parsed.protocol === "wss:") return true;
if (parsed.protocol !== "ws:") return false; // ← http: hits this, returns false
if (isLoopbackHost(parsed.hostname)) return true;
// ...
}
When parsed.protocol is "http:", the second check (!== "ws:") returns false, so the loopback check on line 258 is never reached.
Root cause
The cron announce delivery uses the HTTP chatCompletions endpoint (http://127.0.0.1:18789) rather than the WebSocket endpoint (ws://127.0.0.1:18789). The security check doesn't account for http:// URLs even though they carry the same loopback safety properties.
Workaround
Patch isSecureWebSocketUrl() in all 7 dist files to accept http: and https: protocols:
if (parsed.protocol === "wss:" || parsed.protocol === "https:") return true;
if (parsed.protocol !== "ws:" && parsed.protocol !== "http:") return false;
Files to patch: net-Bf8Z-b6p.js, chrome-BJ1olan3.js, chrome-CdadObm0.js, daemon-cli.js, pi-embedded-CtM2Mrrj.js, pi-embedded-DgYXShcG.js, ws-j0__CWxN.js
Clear compile cache after: rm -rf /var/tmp/openclaw-compile-cache/*
Patch is overwritten on next npm update.
Related issues
Summary
Cron announce delivery fails on
bind: "loopback"setups because the internal delivery path constructshttp://127.0.0.1:<port>butisSecureWebSocketUrl()only acceptsws://andwss://protocols. Thehttp://URL falls through all protocol checks and throwsSECURITY ERROR: Gateway URL uses plaintext ws:// to a non-loopback address.Environment
gateway.bind: "loopback",gateway.auth.mode: "token",OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1Steps to reproduce
bind: "loopback"andauth.mode: "token"delivery.mode: "announce"anddelivery.channel: "telegram"openclaw cron run <id> --url ws://127.0.0.1:18789 --token <token>)lastStatus: ok) but delivery fails (lastDelivered: false)Expected behavior
Cron announce delivery should succeed — the gateway and subagent are co-located on loopback.
Actual behavior
Log shows:
The announce delivery path internally constructs
http://127.0.0.1:18789(HTTP protocol), butisSecureWebSocketUrl()innet-Bf8Z-b6p.js(and 6 other chunk files where it's duplicated) only checks forws:andwss:protocols:When
parsed.protocolis"http:", the second check (!== "ws:") returnsfalse, so the loopback check on line 258 is never reached.Root cause
The cron announce delivery uses the HTTP chatCompletions endpoint (
http://127.0.0.1:18789) rather than the WebSocket endpoint (ws://127.0.0.1:18789). The security check doesn't account forhttp://URLs even though they carry the same loopback safety properties.Workaround
Patch
isSecureWebSocketUrl()in all 7 dist files to accepthttp:andhttps:protocols:Files to patch:
net-Bf8Z-b6p.js,chrome-BJ1olan3.js,chrome-CdadObm0.js,daemon-cli.js,pi-embedded-CtM2Mrrj.js,pi-embedded-DgYXShcG.js,ws-j0__CWxN.jsClear compile cache after:
rm -rf /var/tmp/openclaw-compile-cache/*Patch is overwritten on next
npm update.Related issues
bind: "lan", notbind: "loopback")gateway.internalUrl(open)