root 0d226d608b Update README with capabilities negotiation flow
- Add capabilities negotiation to Features list
- Document connection flow (5 steps)
- Add capabilities_response message format
- Add request_capabilities message format
- Update JavaScript example with capabilities handling
- Update Python example with capabilities handling
2026-03-31 21:43:06 +02:00
2026-03-31 21:41:26 +02:00
2026-03-31 21:41:26 +02:00

WebSocket Channel Plugin for OpenClaw

Custom WebSocket-based messaging channel plugin for OpenClaw. Enables custom messaging platforms to connect via WebSocket with configurable capabilities and API key authentication.

Features

  • 🔐 API Key Authentication - Secure connections via X-API-Key header or query parameter
  • 🏢 Multi-tenant - Support for multiple accounts with unique API keys
  • ⚙️ Configurable Capabilities - Per-account configuration for:
    • Text messages
    • Images
    • Files
    • Voice messages
    • Reactions
    • Threads
    • Typing indicators
  • 🔄 Capabilities Negotiation - Clients report their capabilities on connect
  • 🛡️ DM Security - Three policies: allowlist, open, closed
  • 🔗 Threading - Full support for threaded conversations
  • 🤝 Pairing Flow - Text-based pairing with code exchange
  • 📊 CLI Commands - Management via openclaw websocket status and openclaw websocket accounts

Installation

Local Development

# Plugin is already in extensions/websocket-channel
# OpenClaw will auto-load it

From Git

git clone https://git.azimkin.dev/Azimkin/openclaw-websocket-channel.git
cd openclaw-websocket-channel
openclaw plugins install ./openclaw-websocket-channel

Configuration

Add to your openclaw.json:

{
  "channels": {
    "websocket": {
      "port": 8765,
      "path": "/ws",
      "accounts": [
        {
          "accountId": "default",
          "apiKey": "sk_test_your_api_key_here",
          "allowFrom": ["user-123", "user-456"],
          "dmPolicy": "allowlist",
          "capabilities": {
            "text": true,
            "images": true,
            "files": false,
            "voice": false,
            "reactions": false,
            "threads": true,
            "typing": true
          },
          "metadata": {
            "serverName": "My WebSocket Server",
            "region": "eu-west"
          }
        }
      ]
    }
  }
}

WebSocket Protocol

Connection

ws://localhost:8765/ws?key=YOUR_API_KEY

Or with header:

const ws = new WebSocket('ws://localhost:8765/ws', {
  headers: { 'X-API-Key': 'YOUR_API_KEY' }
});

Connection Flow

  1. Client connects → Server authenticates via API key
  2. Server sends handshake → Server capabilities (maximum allowed)
  3. Server requests capabilities → Asks client what it supports
  4. Client responds → Client capabilities + metadata
  5. Effective capabilities → Server uses intersection of both
  6. Messaging begins → Only supported message types allowed

Message Format

Client → Server

Capabilities Response (required after handshake):

{
  "type": "capabilities_response",
  "capabilities": {
    "text": true,
    "images": true,
    "files": false,
    "voice": false,
    "reactions": false,
    "threads": true,
    "typing": true
  },
  "metadata": {
    "clientName": "My App",
    "version": "1.0.0",
    "platform": "custom"
  }
}

Text Message:

{
  "type": "message",
  "from": "user-123",
  "content": {
    "type": "text",
    "text": "Hello, bot!"
  }
}

Server → Client

Handshake:

{
  "type": "handshake",
  "accountId": "default",
  "capabilities": {
    "text": true,
    "images": true,
    "files": true,
    "voice": true,
    "reactions": false,
    "threads": true,
    "typing": true
  },
  "metadata": {
    "serverName": "My WebSocket Server"
  },
  "timestamp": 1703275200000
}

Capabilities Request:

{
  "type": "request_capabilities"
}

Message:

{
  "type": "message",
  "to": "user-123",
  "content": {
    "type": "text",
    "text": "Hello! How can I help?"
  },
  "messageId": "msg_abc123",
  "timestamp": 1703275200000
}

Client Examples

JavaScript

import WebSocket from 'ws';

const ws = new WebSocket('ws://localhost:8765/ws?key=YOUR_API_KEY');

ws.on('message', (data) => {
  const msg = JSON.parse(data.toString());
  
  switch (msg.type) {
    case 'handshake':
      console.log('Server capabilities:', msg.capabilities);
      break;
      
    case 'request_capabilities':
      // Send client capabilities
      ws.send(JSON.stringify({
        type: 'capabilities_response',
        capabilities: {
          text: true,
          images: true,
          files: false,
          voice: false,
          reactions: false,
          threads: true,
          typing: true
        },
        metadata: {
          clientName: 'My App',
          version: '1.0.0'
        }
      }));
      break;
      
    case 'message':
      console.log('Bot:', msg.content.text);
      break;
  }
});

// Send message (after capabilities_response)
ws.send(JSON.stringify({
  type: 'message',
  from: 'user-123',
  content: { type: 'text', text: 'Hello!' }
}));

Python

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    
    if data['type'] == 'handshake':
        print(f"Server capabilities: {data['capabilities']}")
        
    elif data['type'] == 'request_capabilities':
        # Send client capabilities
        ws.send(json.dumps({
            "type": "capabilities_response",
            "capabilities": {
                "text": True,
                "images": True,
                "files": False,
                "voice": False,
                "reactions": False,
                "threads": True,
                "typing": True
            },
            "metadata": {
                "clientName": "My Python App",
                "version": "1.0.0"
            }
        }))
        
    elif data['type'] == 'message':
        print(f"Bot: {data['content']['text']}")

ws = websocket.WebSocketApp(
    "ws://localhost:8765/ws?key=YOUR_API_KEY",
    on_message=on_message
)
ws.run_forever()

CLI Commands

# Show WebSocket status
openclaw websocket status

# List configured accounts
openclaw websocket accounts

Testing

# Install wscat
npm install -g wscat

# Connect
wscat -c "ws://localhost:8765/ws?key=YOUR_API_KEY"

# Send message
> {"type":"message","from":"user-123","content":{"type":"text","text":"Hello!"}}

Documentation

Development

Project Structure

websocket-channel/
├── package.json              # Package metadata
├── openclaw.plugin.json      # Plugin manifest
├── index.ts                  # Main entry point
├── setup-entry.ts            # Setup entry point
├── api.ts                    # Public API exports
└── src/
    ├── config.ts             # Types and defaults
    ├── auth.ts               # Authentication utilities
    ├── websocket-server.ts   # WebSocket server
    ├── client.ts             # Connection handler
    ├── channel.ts            # Channel plugin config
    └── channel.test.ts       # Test suite

Running Tests

cd extensions/websocket-channel
pnpm test

License

MIT

Support

Description
WebSocket channel plugin for OpenClaw - enables custom messaging platforms to connect via WebSocket
Readme 67 KiB
Languages
TypeScript 100%