When the gateway client reconnects using a stored device token, it was
defaulting to ["operator.admin"] scopes instead of preserving the
previously authorized scopes from the stored token. This caused the
operator device token to be regenerated without operator.read scope,
breaking status/probe/health commands.
This fix:
1. Loads the stored scopes along with the stored token in selectConnectAuth
2. Uses the stored scopes when reconnecting with a valid device token
3. Falls back to explicitly requested scopes or default admin-only scope
when no stored scopes exist
Fixes#46000
* fix: import CHANNEL_IDS from leaf module to avoid TDZ on init (#48832)
schema.ts and validation.ts imported CHANNEL_IDS from channels/registry.js,
which re-exports from channels/ids.js but also imports plugins/runtime.js.
When the bundler resolves this dependency graph, the re-exported CHANNEL_IDS
can be undefined at the point config/validation.ts evaluates (temporal dead
zone), causing 'CHANNEL_IDS is not iterable' on startup.
Fix: import CHANNEL_IDS directly from channels/ids.js (the leaf module with
zero heavy dependencies) and normalizeChatChannelId from channels/chat-meta.js.
Fixes#48832
* fix: improve WS handshake reliability on slow-startup environments (#48736)
On Windows with large dist bundles (46MB/639 files), heavy synchronous
module loading blocks the event loop during CLI startup, preventing
timely processing of the connect.challenge frame and causing ~80%
handshake timeout failures.
Changes:
- Yield event loop (setImmediate) before starting WS connection in
callGateway to let pending I/O drain after heavy module loading
- Add OPENCLAW_CONNECT_CHALLENGE_TIMEOUT_MS env var override for
client-side connect challenge timeout (server already has
OPENCLAW_HANDSHAKE_TIMEOUT_MS)
- Include diagnostic timing in challenge timeout error messages
(elapsed vs limit) for easier debugging
- Add tests for env var override and resolution logic
---------
Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
* fix(gateway): pass actual version to Control UI client instead of "dev"
The GatewayClient, CLI WS client, and browser Control UI all sent
"dev" as their clientVersion during handshake, making it impossible
to distinguish builds in gateway logs and health snapshots.
- GatewayClient and CLI WS client now use the resolved VERSION constant
- Control UI reads serverVersion from the bootstrap endpoint and
forwards it when connecting
- Bootstrap contract extended with serverVersion field
Closes#35209
* Gateway: fix control-ui version version-reporting consistency
* Control UI: guard deferred bootstrap connect after disconnect
* fix(ui): accept same-origin http and relative gateway URLs for client version
---------
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
* fix(security): block plaintext WebSocket connections to non-loopback addresses
Addresses CWE-319 (Cleartext Transmission of Sensitive Information).
Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).
Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS
Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs
Fixes#12519
* fix(test): update gateway-chat mock to preserve net.js exports
Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
When the gateway connection fails due to device token mismatch (e.g., after
re-pairing the device), clear the stored device-auth token so that
subsequent connection attempts can obtain a fresh token.
This fixes the cron tool failing with 'device token mismatch' error
after running 'openclaw configure' to re-pair the device.
Fixes#18175