Skip to content

Commit 80457b2

Browse files
committed
feat: add breaking changes documentation for messaging API v0.6.5
1 parent 8ff1710 commit 80457b2

File tree

2 files changed

+342
-4
lines changed

2 files changed

+342
-4
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
# 消息 API 破坏性变更通知(v0.6.5)
2+
3+
> **回复方**: ClawNet 团队
4+
> **接收方**: TelAgent 项目组
5+
> **日期**: 2026-03-10
6+
> **涉及包**: `@claw-network/core@0.6.5`, `@claw-network/protocol@0.6.5`, `@claw-network/node@0.6.5`, `@claw-network/sdk@0.6.5`
7+
> **npm 状态**: ✅ 已发布
8+
> **关联文档**: `issues/clawnet-p2p-binary-transport-reply.md`
9+
10+
---
11+
12+
## 概述
13+
14+
`v0.6.5` 对消息系统进行了 **破坏性重构**,核心变更:
15+
16+
1. **全面移除 base64** — 整个消息链路不再使用 base64 编码
17+
2. **文本与二进制接口分离** — REST API 和 SDK 拆分为独立的文本/二进制接口
18+
3. **存储层 TEXT → BLOB** — SQLite payload 列从 TEXT 改为 BLOB,旧数据库不兼容
19+
4. **移除 `payloadEncoding` 字段** — 不再存在编码声明,二进制就是二进制
20+
21+
> ⚠️ **这是破坏性变更**。升级后旧消息数据库不兼容,需要重新初始化。所有向后兼容和迁移代码已移除。
22+
23+
---
24+
25+
## 一、REST API 变更
26+
27+
### 已移除
28+
29+
| 变更 | 说明 |
30+
|------|------|
31+
| `payloadEncoding` 字段 | 发送和收件箱响应中不再存在此字段 |
32+
| `POST /send` 中的 base64 payload | 不再接受 base64 编码的 payload |
33+
34+
### 发送消息(新接口)
35+
36+
**文本消息**`POST /api/v1/messaging/send`(JSON body,仅支持字符串 payload)
37+
38+
```bash
39+
curl -X POST http://localhost:9528/api/v1/messaging/send \
40+
-H "Content-Type: application/json" \
41+
-H "X-Api-Key: YOUR_KEY" \
42+
-d '{
43+
"targetDid": "did:claw:zBob...",
44+
"topic": "my-app/text",
45+
"payload": "Hello, World!"
46+
}'
47+
```
48+
49+
`payload` 字段类型为 `string`,不再接受 base64 编码的二进制。
50+
51+
**二进制消息**`POST /api/v1/messaging/send-binary`(🆕 新增端点)
52+
53+
```bash
54+
curl -X POST http://localhost:9528/api/v1/messaging/send-binary \
55+
-H "Content-Type: application/octet-stream" \
56+
-H "X-Api-Key: YOUR_KEY" \
57+
-H "X-Target-Did: did:claw:zBob..." \
58+
-H "X-Topic: my-app/binary-data" \
59+
-H "X-Compress: true" \
60+
--data-binary @my-file.bin
61+
```
62+
63+
元数据通过 HTTP headers 传递,body 是原始二进制字节:
64+
65+
| Header | 必填 | 说明 |
66+
|--------|------|------|
67+
| `X-Target-Did` || 目标 DID |
68+
| `X-Topic` || 消息 topic |
69+
| `X-Compress` | 可选 | 是否 gzip 压缩(`true`/`false`|
70+
| `X-Encrypt` | 可选 | 是否 E2E 加密(`true`/`false`|
71+
| `X-Priority` | 可选 | 优先级(整数) |
72+
| `X-Ttl` | 可选 | 过期秒数 |
73+
74+
**批量发送** — 两个端点:
75+
76+
- `POST /api/v1/messaging/send/batch` — 文本批量发送(JSON body)
77+
- `POST /api/v1/messaging/send-binary/batch` — 二进制批量发送(`X-Target-Dids` header,逗号分隔)
78+
79+
### 收件箱变更
80+
81+
**`GET /api/v1/messaging/inbox` 响应格式变更**
82+
83+
```json
84+
{
85+
"messages": [{
86+
"messageId": "msg_abc123",
87+
"sourceDid": "did:claw:zAlice...",
88+
"topic": "my-app/text",
89+
"payload": "Hello, World!",
90+
"payloadSize": 13,
91+
"compressed": false,
92+
"encrypted": false,
93+
"receivedAtMs": 1710000000000,
94+
"priority": 1,
95+
"seq": 42
96+
}]
97+
}
98+
```
99+
100+
| 字段变更 | 说明 |
101+
|----------|------|
102+
| `payloadEncoding` | ❌ 已移除 |
103+
| `payload` | 仅在 `compressed: false``encrypted: false` 时存在(UTF-8 文本) |
104+
| `payloadSize` | 🆕 始终存在,表示原始 payload 字节数 |
105+
| `compressed` | 🆕 是否已 gzip 压缩 |
106+
| `encrypted` | 🆕 是否已 E2E 加密 |
107+
108+
**重要**:已压缩或已加密的消息,`payload` 字段 **不存在**。需通过独立端点下载原始字节。
109+
110+
**`GET /api/v1/messaging/inbox/:messageId/payload`**(🆕 新增端点)
111+
112+
下载单条消息的原始二进制 payload:
113+
114+
```bash
115+
curl -s http://localhost:9528/api/v1/messaging/inbox/msg_abc123/payload \
116+
-H "X-Api-Key: YOUR_KEY" \
117+
-o output.bin
118+
```
119+
120+
响应:
121+
- `Content-Type: application/octet-stream`
122+
- `Content-Length` — 字节数
123+
- `X-Compressed: 1` — 如果已压缩
124+
- `X-Encrypted: 1` — 如果已加密
125+
126+
---
127+
128+
## 二、SDK 变更
129+
130+
### 已移除
131+
132+
- `send()` 不再接受 `payloadEncoding` 参数
133+
- `send()``payload` 不再接受 `Uint8Array`,仅接受 `string`
134+
135+
### 新增方法
136+
137+
```typescript
138+
import { ClawNetClient } from '@claw-network/sdk';
139+
140+
const client = new ClawNetClient({
141+
baseUrl: 'http://localhost:9528',
142+
apiKey: 'YOUR_KEY',
143+
});
144+
145+
// ✅ 发送文本(与旧版 send 类似,但 payload 仅 string)
146+
await client.messaging.send({
147+
targetDid: 'did:claw:zBob...',
148+
topic: 'my-app/text',
149+
payload: 'Hello, World!',
150+
});
151+
152+
// ✅ 发送二进制(🆕)
153+
await client.messaging.sendBinary({
154+
targetDid: 'did:claw:zBob...',
155+
topic: 'my-app/binary-data',
156+
payload: new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]),
157+
compress: true,
158+
});
159+
160+
// ✅ 批量发送文本
161+
await client.messaging.sendBatch({
162+
targetDids: ['did:claw:zBob...', 'did:claw:zCharlie...'],
163+
topic: 'broadcast',
164+
payload: 'Hello everyone!',
165+
});
166+
167+
// ✅ 批量发送二进制(🆕)
168+
await client.messaging.sendBinaryBatch({
169+
targetDids: ['did:claw:zBob...', 'did:claw:zCharlie...'],
170+
topic: 'binary-broadcast',
171+
payload: new Uint8Array([0x01, 0x02, 0x03]),
172+
});
173+
174+
// ✅ 下载原始 payload(🆕,返回 ArrayBuffer)
175+
const rawBytes = await client.messaging.downloadPayload('msg_abc123');
176+
```
177+
178+
### InboxMessage 类型变更
179+
180+
```typescript
181+
// v0.6.4 及之前
182+
interface InboxMessage {
183+
messageId: string;
184+
payload: string; // 可能是 base64
185+
payloadEncoding?: string; // "base64" | "utf-8"
186+
// ...
187+
}
188+
189+
// v0.6.5(当前)
190+
interface InboxMessage {
191+
messageId: string;
192+
payload?: string; // 仅纯文本消息存在
193+
payloadSize: number; // 始终存在
194+
compressed: boolean; // 是否已压缩
195+
encrypted: boolean; // 是否已加密
196+
// ...
197+
}
198+
```
199+
200+
---
201+
202+
## 三、存储层变更
203+
204+
| 变更项 | 旧版 | 新版 |
205+
|--------|------|------|
206+
| payload 列类型 | `TEXT`(base64 或 UTF-8) | `BLOB`(原始字节) |
207+
| 编码标记 | `payloadEncoding`| `compressed` + `encrypted` 标志列 |
208+
| 向后兼容 | 自动迁移旧格式 | ❌ 无迁移,旧数据库不兼容 |
209+
210+
> ⚠️ **升级后需删除旧消息数据库并重新初始化**。所有迁移代码已移除。
211+
212+
---
213+
214+
## 四、迁移指南
215+
216+
### 1. 升级依赖
217+
218+
```bash
219+
pnpm add @claw-network/sdk@0.6.5
220+
#
221+
npm install @claw-network/sdk@0.6.5
222+
```
223+
224+
如果直接运行节点:
225+
226+
```bash
227+
pnpm add @claw-network/node@0.6.5
228+
```
229+
230+
### 2. 代码迁移
231+
232+
**发送文本消息** — 无需改动(如果之前用的就是纯文本 payload):
233+
234+
```typescript
235+
// 之前和现在都一样
236+
await client.messaging.send({
237+
targetDid: 'did:claw:zBob...',
238+
topic: 'chat',
239+
payload: '你好',
240+
});
241+
```
242+
243+
**发送二进制数据** — 从 `send()` + base64 迁移到 `sendBinary()`
244+
245+
```typescript
246+
// ❌ 旧版(base64)
247+
await client.messaging.send({
248+
targetDid: 'did:claw:zBob...',
249+
payload: Buffer.from(binaryData).toString('base64'),
250+
payloadEncoding: 'base64',
251+
});
252+
253+
// ✅ 新版(原始二进制)
254+
await client.messaging.sendBinary({
255+
targetDid: 'did:claw:zBob...',
256+
topic: 'binary-data',
257+
payload: binaryData, // Uint8Array
258+
});
259+
```
260+
261+
**读取收件箱** — 处理新字段:
262+
263+
```typescript
264+
const { messages } = await client.messaging.inbox({ topic: 'my-app/*' });
265+
266+
for (const msg of messages) {
267+
if (msg.payload !== undefined) {
268+
// 纯文本消息,直接使用
269+
console.log('Text:', msg.payload);
270+
} else {
271+
// 二进制/压缩/加密消息,需下载原始 payload
272+
const raw = await client.messaging.downloadPayload(msg.messageId);
273+
console.log('Binary payload size:', raw.byteLength);
274+
}
275+
}
276+
```
277+
278+
### 3. REST API 直接调用
279+
280+
如果 TelAgent 直接调用 REST API(非 SDK):
281+
282+
| 旧调用 | 新调用 |
283+
|--------|--------|
284+
| `POST /send` + base64 payload + `payloadEncoding` | `POST /send-binary` + octet-stream body + metadata headers |
285+
| 收件箱中读取 base64 再解码 | `GET /inbox/:messageId/payload` 直接下载原始字节 |
286+
| 通过 `payloadEncoding` 判断类型 | 通过 `compressed`/`encrypted` 字段 + `payload` 是否存在判断 |
287+
288+
### 4. 数据库重建
289+
290+
升级节点后,删除旧消息数据库:
291+
292+
```bash
293+
# 停止节点
294+
systemctl stop clawnetd
295+
296+
# 备份旧数据(如需要)
297+
cp ~/.clawnet/data/messages.db ~/.clawnet/data/messages.db.bak
298+
299+
# 删除旧数据库(节点重启时自动重建)
300+
rm ~/.clawnet/data/messages.db
301+
302+
# 重启节点
303+
systemctl start clawnetd
304+
```
305+
306+
---
307+
308+
## 五、WebSocket 推送变更
309+
310+
WebSocket 收件箱推送(`ws://localhost:9528/api/v1/messaging/subscribe`)同步变更:
311+
312+
- JSON 帧中 `payload` 字段仅在 `compressed: false``encrypted: false` 时包含文本
313+
- 不再包含 `payloadEncoding` 字段
314+
- 新增 `payloadSize``compressed``encrypted` 字段
315+
- 二进制/压缩/加密消息需通过 REST API 的 `GET /inbox/:messageId/payload` 下载原始字节
316+
317+
---
318+
319+
## 六、变更影响范围汇总
320+
321+
| 模块 | 影响 | 迁移复杂度 |
322+
|------|------|:---:|
323+
| `send()` 纯文本 | 无影响(接口不变) ||
324+
| `send()` 发送二进制 | 改用 `sendBinary()` ||
325+
| 收件箱读取 | 新增字段判断 + `downloadPayload()` ||
326+
| WebSocket 推送 | 新字段适配 ||
327+
| REST API 直接调用 | 新端点 + header 传元数据 ||
328+
| 数据库 | 需重建 | 一次性操作 |
329+
330+
---
331+
332+
## 七、后续沟通
333+
334+
如有升级或迁移问题,请随时联系我们。可以安排联调时间或提供更详细的代码示例。

packages/node/src/services/clawnet-transport-service.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ export class ClawNetTransportService {
240240
topic: string;
241241
payload?: string;
242242
payloadSize?: number;
243+
compressed?: boolean;
244+
encrypted?: boolean;
243245
messageId: string;
244246
seq: number;
245247
};
@@ -306,11 +308,13 @@ export class ClawNetTransportService {
306308
}
307309

308310
// All other topics are JSON text messages
309-
if (!data.payload) {
310-
logger.warn('[p2p-transport] Missing payload for topic: %s', data.topic);
311-
return;
311+
// If payload is absent (compressed/encrypted), download raw bytes and decode
312+
let payloadText = data.payload;
313+
if (!payloadText) {
314+
const buf = await this.gateway.client.messaging.downloadPayload(data.messageId);
315+
payloadText = new TextDecoder().decode(buf);
312316
}
313-
const parsed = JSON.parse(data.payload) as Record<string, unknown>;
317+
const parsed = JSON.parse(payloadText) as Record<string, unknown>;
314318
switch (data.topic) {
315319
case TOPIC_ENVELOPE:
316320
await this.callbacks.onEnvelope?.(parsed, data.sourceDid);

0 commit comments

Comments
 (0)