root 1421f9d645 Add capabilities negotiation flow
Changes:
- Server sends 'request_capabilities' after handshake
- Client responds with 'capabilities_response' containing supported features
- Server computes intersection of account + client capabilities
- Effective capabilities used for message validation
- Client metadata stored per-connection
- Updated tests for capabilities negotiation
- Updated CLIENT_SPEC.md documentation

Protocol messages added:
- request_capabilities (server → client)
- capabilities_response (client → server)

ConnectedClient fields added:
- clientCapabilities?: ChannelCapabilities
- clientMetadata?: Record<string, unknown>
- capabilitiesReady: boolean

New methods:
- getEffectiveCapabilities(client) - returns intersection
- clientSupportsCapability(client, capability) - check support
2026-03-31 21:41:26 +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
  • 🛡️ 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' }
});

Message Format

Client → Server:

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

Server → Client (Handshake):

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

Server → Client (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());
  if (msg.type === 'message') {
    console.log('Bot:', msg.content.text);
  }
});

// Send message
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'] == '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%