跳转到内容

OpenClaw Channel 插件 — AI Agent 加密通信通道

快速开始

import { ArthasChannelAdapter } from '@arthas-chat/openclaw-channel';
const adapter = new ArthasChannelAdapter();
adapter.onMessage(msg => console.log(`${msg.userName}: ${msg.text}`));
await adapter.connect({ serverUrl: 'wss://arthas100-arthas-server.hf.space/ws', shareCode: 'roomId:base64Key' });
await adapter.send({ text: 'Hello from AI!', id: '1', channelId: 'arthas' });

注:示例使用 ESM top-level await(需要 package.json 中 "type": "module" 且 Node.js ≥ 18)。


简介

@arthas-chat/openclaw-channel(v1.0.0)是 Arthas 项目的 OpenClaw 通道插件,将 Arthas 端到端加密聊天室暴露为 AI Agent 通信通道。

核心价值: Arthas 是目前唯一提供端到端加密(E2EE)的 AI Agent 通信通道。服务器仅作为密文中转站(blind relay),无法观察用户的提示词(prompts)或 AI 的回复内容(responses)。你的对话数据从发送端加密,到接收端解密,中间任何节点都无法窥探明文。

适用场景:

  • 需要隐私保护的 AI 对话(医疗咨询、法律建议、财务分析)
  • 企业内部 AI 助手(防止云服务商读取对话内容)
  • 合规要求数据不落地的场景

安装

Terminal window
npm install @arthas-chat/openclaw-channel

从源码安装(可选)

Terminal window
# 1. 克隆 Arthas 仓库
git clone https://github.com/michaelwang123/arthas.git
cd arthas
# 2. 安装依赖并构建
cd packages/openclaw-channel
npm install
npm run build
# 3. 在你的项目中链接
npm link
cd /path/to/your-project
npm link @arthas-chat/openclaw-channel

系统要求

  • Node.js ≥ 18.0.0
  • ESM 模块支持(package.json 中 "type": "module"

配置参考

插件通过环境变量或 ChannelConfig 对象配置。优先级:环境变量 > ChannelConfig > 默认值。

环境变量必填默认值说明
ARTHAS_SERVER_URL✅ 是Arthas WebSocket 服务器地址(必须以 wss://ws:// 开头)
ARTHAS_SHARE_CODE✅ 是房间分享码(格式:roomId:base64Key[:ephemeral:expiresAt],最少 2 段,每段非空)
ARTHAS_DISPLAY_NAME❌ 否AI AssistantAgent 在房间中的显示名称
ARTHAS_SIGNING_ENABLED❌ 否false是否启用 Ed25519 消息签名(true1 启用)
ARTHAS_ROOM_PASSWORD❌ 否密码保护房间的密码(传输时使用 SHA-256 哈希)

配置示例

# .env 文件示例
ARTHAS_SERVER_URL=wss://arthas100-arthas-server.hf.space/ws
ARTHAS_SHARE_CODE=X2-KtJ6oRzdxbguxl5DAR:AMVGFZBTFLeed7tVncI1oKoFUdNIv6goGz64x0cuU1M
ARTHAS_DISPLAY_NAME=Code Assistant
ARTHAS_SIGNING_ENABLED=true
ARTHAS_ROOM_PASSWORD=my-secret-password

📡 公共演示服务器: wss://arthas100-arthas-server.hf.space/ws 此服务器仅供体验和测试,生产环境请自托管部署

分享码格式

roomId:base64Key[:ephemeral:expiresAt]
│ │ │ │
│ │ │ └─ 段4: 过期时间戳(Unix 秒,0=永不过期)
│ │ └─ 段3: 临时房间标志(0=持久, 1=临时)
│ └─ 段2: base64url 编码的 AES-256 加密密钥
└─ 段1: 房间唯一标识符

最少需要 2 段(roomId + key),段 3 和段 4 可选。


使用示例

基本 Adapter 设置

import { ArthasChannelAdapter } from '@arthas-chat/openclaw-channel';
// 📚 学习要点: 延迟初始化模式
// 构造函数不执行任何 I/O 操作(不连接 WebSocket、不读取配置)。
// 所有初始化逻辑集中在 connect() 方法中,支持 Gateway 的延迟加载策略。
const adapter = new ArthasChannelAdapter();
// 📚 学习要点: 为什么 connect() 是异步的?
// connect() 内部执行:配置验证 → 密钥派生 → WebSocket 连接 → 加入房间。
// 任何步骤失败都会抛出描述性错误(Fail-Fast 原则)。
await adapter.connect({
serverUrl: 'wss://arthas-chat.onrender.com/ws',
shareCode: 'X2-KtJ6oRzdxbguxl5DAR:AMVGFZBTFLeed7tVncI1oKoFUdNIv6goGz64x0cuU1M',
displayName: 'Code Assistant',
signingEnabled: true,
});

消息处理回调

// 📚 学习要点: 为什么使用 onMessage 回调而非 EventEmitter?
// OpenClaw Gateway 的 ChannelAdapter 接口规定使用回调模式,
// 确保消息处理的顺序性(避免并发回调导致的竞态条件)。
adapter.onMessage((message) => {
console.log(`[${message.timestamp.toISOString()}] ${message.userName}: ${message.text}`);
// message 结构:
// - id: string — 消息唯一 ID(UUID v4)
// - channelId: 'arthas' — 通道标识
// - userId: string — 发送者 ID
// - userName: string — 发送者显示名称
// - text: string — 解密后的明文内容
// - timestamp: Date — 消息时间戳
// - attachments?: [] — 文件附件(可选)
});

文件传输接收

adapter.onMessage((message) => {
// 📚 学习要点: 文件传输通过 attachments 字段传递
// FileReceiver 在内部收集所有分片、解密并重组文件,
// 最终作为 IncomingMessage 的 attachments 传递给回调。
if (message.attachments && message.attachments.length > 0) {
for (const file of message.attachments) {
console.log(`收到文件: ${file.fileName} (${file.size} bytes, ${file.mimeType})`);
// file.data: Buffer | Uint8Array — 解密后的文件内容
}
}
});

连接状态监控

// 📚 学习要点: 有限状态机(FSM)
// 连接状态遵循转换规则:
// disconnected → connecting → connected → reconnecting → connected/error
adapter.onStatusChange((status) => {
switch (status) {
case 'connected':
console.log('✅ 已连接到 Arthas 房间');
break;
case 'reconnecting':
console.log('🔄 连接断开,正在自动重连...');
break;
case 'error':
console.log('❌ 连接错误(不可恢复)');
break;
case 'disconnected':
console.log('⚪ 已断开连接');
break;
}
});

安全模型

AES-256-GCM 加密

所有消息在发送前使用 AES-256-GCM 对称加密:

  • 密钥派生:从分享码的第 2 段(base64url 编码的 256-bit 密钥)派生
  • IV 生成:每条消息使用 crypto.randomBytes(12) 生成唯一的 12 字节 IV(初始化向量)
  • GCM 认证标签:AES-GCM 自动生成 16 字节认证标签(authentication tag),确保密文完整性——任何篡改都会导致解密失败
  • 编码格式:IV 和密文均使用 base64url 编码后通过 WebSocket 传输

可选 Ed25519 签名

启用 ARTHAS_SIGNING_ENABLED=true 后:

  • 密钥生成connect() 时生成 Ed25519 密钥对
  • 公钥广播:连接成功后立即将公钥作为加密消息广播到房间
  • 消息签名:Agent 的每条消息附带 Ed25519 数字签名
  • 验证:其他客户端可验证消息确实来自此 Agent(防伪造)

密钥生命周期

connect()
├─ 从分享码派生 AES-256 密钥 → 存储在内存(this.key)
├─ 生成 Ed25519 密钥对(如果启用)→ 存储在内存(this.signingKeyPair)
│ ... 运行期间:密钥仅存在于进程内存 ...
disconnect()
├─ this.key.fill(0) → 清零 AES 密钥内存
├─ zeroKeyPair(signingKeyPair) → 清零签名密钥对内存
└─ 密钥不持久化到磁盘、不传输到外部

安全属性:

  • 密钥仅存在于进程内存中(memory-only)
  • 断开连接时立即清零(zeroed on disconnect)
  • 服务器永远无法获取密钥(zero-knowledge relay)

故障排除

缺少必填配置

[Arthas 配置错误] 缺少必填配置: serverUrl(Arthas 服务器地址)
设置方式:
环境变量: ARTHAS_SERVER_URL=wss://your-server.com/ws
或 ChannelConfig: { serverUrl: 'wss://your-server.com/ws' }

原因:未设置 ARTHAS_SERVER_URL 环境变量,也未在 connect() 的 config 参数中提供 serverUrl

修复:在 .env 文件或系统环境变量中设置 ARTHAS_SERVER_URL


[Arthas 配置错误] 缺少必填配置: shareCode(房间分享码)
设置方式:
环境变量: ARTHAS_SHARE_CODE=roomId:encryptionKey
或 ChannelConfig: { shareCode: 'roomId:encryptionKey' }
获取方式: 在 Arthas 客户端中创建房间后,点击"分享"获取分享码

原因:未设置 ARTHAS_SHARE_CODE 环境变量。

修复:在 Arthas Web 客户端或 CLI 中创建房间,获取分享码后设置环境变量。

serverUrl 格式无效

[Arthas 配置错误] serverUrl 格式无效: "http://example.com"
期望格式: wss://your-server.com/ws 或 ws://localhost:9000/ws
设置方式: 环境变量 ARTHAS_SERVER_URL=wss://your-server.com/ws

原因:URL 未使用 wss://ws:// 协议前缀。

修复:确保 URL 以 wss://(生产环境)或 ws://(本地开发)开头。

shareCode 格式无效

[Arthas 配置错误] shareCode 格式无效: 需要至少 2 个冒号分隔的段(roomId:key),当前只有 1 段
期望格式: roomId:base64Key 或 roomId:base64Key:ephemeral:expiresAt
设置方式: 环境变量 ARTHAS_SHARE_CODE=your-room-id:your-encryption-key

原因:分享码格式不正确,缺少冒号分隔符。

修复:确保完整复制了分享码(至少包含 roomId:key 两段)。

非加密连接警告

[Arthas 配置警告] serverUrl 使用了非加密的 ws:// 协议: "ws://example.com/ws"
建议: 生产环境请使用 wss:// 确保传输层安全

原因:使用了 ws:// 而非 wss://,传输层未加密。

修复:生产环境务必使用 wss://ws:// 仅用于 localhost 开发。


API 参考

导出类

ArthasChannelAdapter

实现 ChannelAdapter 接口的核心类。

方法签名说明
connect(config: ChannelConfig) => Promise<void>加载配置、派生密钥、连接 WebSocket、加入房间
disconnect() => Promise<void>断开连接、清零密钥、释放资源
send(message: OutgoingMessage) => Promise<void>加密并发送消息(长消息自动分割为 ≤4000 字符的片段)
onMessage(callback: (msg: IncomingMessage) => void) => void注册消息接收回调
onStatusChange(callback: (status: ConnectionStatus) => void) => void注册连接状态变化回调

导出函数

definePlugin

function definePlugin(definition: PluginDefinition): Plugin

OpenClaw 插件定义函数,用于向 Gateway 注册插件。

导出类型

类型说明
PluginDefinition插件定义选项(name, version, channels)
ChannelRegistration通道注册信息(id, name, adapter)
ChannelAdapter通道适配器接口(connect, disconnect, send, onMessage, onStatusChange)
ChannelConfig通用配置容器(Record<string, unknown>
ArthasChannelConfigArthas 通道强类型配置(serverUrl, shareCode, displayName, signingEnabled, roomPassword)
IncomingMessage入站消息(id, channelId, userId, userName, text, timestamp, attachments?, metadata?)
OutgoingMessage出站消息(id, channelId, text, attachments?, replyTo?, type?)
MessageAttachment消息附件(fileName, mimeType, size, data)
ConnectionStatus连接状态联合类型:`‘disconnected'
MessageType消息类型:`‘text'
Plugin插件实例(definition, onInit?, onDestroy?)

数据流架构

User (Web/CLI)
│ 加密消息
Arthas Server (blind relay, 只转发密文)
│ 转发密文
@arthas-chat/openclaw-channel (解密 → 明文)
│ IncomingMessage
OpenClaw Gateway (路由 + 上下文管理)
│ prompt
AI Agent (LLM 推理)
│ response
OpenClaw Gateway
│ OutgoingMessage
@arthas-chat/openclaw-channel (加密 → 密文)
│ 加密消息
Arthas Server (blind relay)
│ 转发密文
User (解密 → 明文)

服务器在整个流程中仅充当密文中转站,无法解密任何消息内容。


下一步