Skip to content

Gateway: Implemented WebSocket interface referencing OpenClaw#751

Open
seanly wants to merge 2 commits intosipeed:mainfrom
seanly:webclaw
Open

Gateway: Implemented WebSocket interface referencing OpenClaw#751
seanly wants to merge 2 commits intosipeed:mainfrom
seanly:webclaw

Conversation

@seanly
Copy link

@seanly seanly commented Feb 25, 2026

  • Add --help/-h: print gatewayHelp() and exit before loading config
  • Default enableWebSocket=true (WebSocket gateway + /health, /ready)
  • Add --no-websocket for health-only mode
  • Remove -e; keep --enable-websocket as no-op for compatibility
  • Add gatewayHelp() with usage, options, examples
  • works perfectly with webclaw: https://github.com/seanly/webclaw/tree/picoclaw"

📝 Description

🗣️ Type of Change

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 📖 Documentation update
  • ⚡ Code refactoring (no functional changes, no api changes)

🤖 AI Code Generation

  • 🤖 Fully AI-generated (100% AI, 0% Human)
  • 🛠️ Mostly AI-generated (AI draft, Human verified/modified)
  • 👨‍💻 Mostly Human-written (Human lead, AI assisted or none)

🔗 Related Issue

📚 Technical Context (Skip for Docs)

  • Reference URL:
  • Reasoning:

🧪 Test Environment

  • Hardware:
  • OS:
  • Model/Provider:
  • Channels:

📸 Evidence (Optional)

Click to view Logs/Screenshots

☑️ Checklist

  • My code/docs follow the style of this project.
  • I have performed a self-review of my own changes.
  • I have updated the documentation accordingly.

YS Liu and others added 2 commits February 25, 2026 10:36
- Add --help/-h: print gatewayHelp() and exit before loading config
- Default enableWebSocket=true (WebSocket gateway + /health, /ready)
- Add --no-websocket for health-only mode
- Remove -e; keep --enable-websocket as no-op for compatibility
- Add gatewayHelp() with usage, options, examples

Co-authored-by: Cursor <cursoragent@cursor.com>
@xiaket xiaket added enhancement New feature or request domain: agent labels Feb 25, 2026
@alexhoshina
Copy link
Collaborator

Hey, @seanly I think this feature should be added as a channel function~. We have already opened the refactor branch, which includes an implementation of a WebSocket server. Perhaps you could make improvements based on this. Looking forward to your modifications and suggestions!

Copy link

@nikolasdehor nikolasdehor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a significant feature (WebSocket gateway). The architecture is reasonable — JSON-framed req/res/event protocol over WebSocket with session management. However, there are several issues that should be addressed before merging:

Security Issues:

  1. CheckOrigin allows all origins: CheckOrigin: func(r *http.Request) bool { return true } is a textbook WebSocket CSRF vulnerability. Any website the user visits can connect to the local gateway and interact with the agent. This should at minimum check Origin against allowed origins, or require token auth on the upgrade request itself (not just in a post-upgrade connect frame).

  2. Auth happens after connection is established: The connect method validates token/password, but by then the WebSocket is already open. Before connect succeeds, the client can still send frames — the only check is frame.Type != "req" which skips non-request frames, but request frames for other methods (like chat.send) are processed without checking if connect was called first. There is no per-connection auth state tracking.

  3. Empty token AND password means "deny all": If both cfg.Token and cfg.Password are empty strings (the default!), handleConnect returns "UNAUTHORIZED", "Missing gateway auth". This means the WebSocket gateway is unusable by default until the user configures auth. This is arguably safe but the user experience will be confusing — the gateway starts, says "WebSocket Gateway available", but nobody can connect. Consider either requiring auth config to enable WS, or allowing unauthenticated local access when bound to 127.0.0.1.

Correctness Issues:

  1. SubscribeOutbound is a blocking poll: consumeOutbound calls s.bus.SubscribeOutbound(ctx) in a loop. If this is a channel-based subscription, only one consumer gets each message. If other channels (Telegram, Discord) also subscribe, the web gateway would steal messages from them. Need to verify this is a fan-out subscription, not a single-consumer queue.

  2. Session key displayed without agent: prefix can collide: The display key strips agent:main:, but if two agents have sessions with the same suffix, the display keys collide. The resolveSessionKey round-trip may resolve to the wrong agent.

  3. No connection cleanup on close: When serveWebSocket exits, subscriptions are cleaned up, but in-flight chat.send messages (already published to the bus) will generate outbound events that go to dead connections. The goroutine in consumeOutbound will call writeJSONLocked on a closed connection — this should be handled (check conn is still alive).

Minor:

  1. Missing newline at end of server.go: The file does not end with a newline.

  2. SHA1 for message IDs: Using SHA1 in msgID is fine for non-security purposes but worth noting it is not a security hash — it is just a stable ID generator.

Good test coverage (395 lines), which is appreciated for a feature of this scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain: agent enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants