@agentunion/fastaun-browser 0.2.20 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -26
- package/README.md +0 -1
- package/_packed_docs/CHANGELOG.md +66 -26
- package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +542 -0
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1 -24
- package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +419 -0
- package/_packed_docs/protocol/index.md +13 -3
- package/_packed_docs/python-sdk-v2-only-changelog.md +189 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +39 -16
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +131 -39
- package/_packed_docs/sdk/09-message-rpc-manual.md +30 -67
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +25 -5
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +15042 -0
- package/dist/client.d.ts +179 -187
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3148 -3993
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +0 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -4
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts +8 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +114 -1
- package/dist/crypto.js.map +1 -1
- package/dist/e2ee.d.ts +5 -210
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/e2ee.js +4 -1379
- package/dist/e2ee.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/namespaces/auth.d.ts +9 -0
- package/dist/namespaces/auth.d.ts.map +1 -1
- package/dist/namespaces/auth.js +248 -4
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/protected-headers.d.ts +14 -0
- package/dist/protected-headers.d.ts.map +1 -0
- package/dist/protected-headers.js +47 -0
- package/dist/protected-headers.js.map +1 -0
- package/dist/seq-tracker.d.ts +7 -2
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +33 -13
- package/dist/seq-tracker.js.map +1 -1
- package/dist/transport.d.ts +9 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +262 -10
- package/dist/transport.js.map +1 -1
- package/dist/v2/crypto/aead.d.ts +26 -0
- package/dist/v2/crypto/aead.d.ts.map +1 -0
- package/dist/v2/crypto/aead.js +63 -0
- package/dist/v2/crypto/aead.js.map +1 -0
- package/dist/v2/crypto/canonical.d.ts +21 -0
- package/dist/v2/crypto/canonical.d.ts.map +1 -0
- package/dist/v2/crypto/canonical.js +111 -0
- package/dist/v2/crypto/canonical.js.map +1 -0
- package/dist/v2/crypto/dh-path.d.ts +21 -0
- package/dist/v2/crypto/dh-path.d.ts.map +1 -0
- package/dist/v2/crypto/dh-path.js +50 -0
- package/dist/v2/crypto/dh-path.js.map +1 -0
- package/dist/v2/crypto/ecdh.d.ts +19 -0
- package/dist/v2/crypto/ecdh.d.ts.map +1 -0
- package/dist/v2/crypto/ecdh.js +101 -0
- package/dist/v2/crypto/ecdh.js.map +1 -0
- package/dist/v2/crypto/ecdsa.d.ts +16 -0
- package/dist/v2/crypto/ecdsa.d.ts.map +1 -0
- package/dist/v2/crypto/ecdsa.js +52 -0
- package/dist/v2/crypto/ecdsa.js.map +1 -0
- package/dist/v2/crypto/hkdf.d.ts +21 -0
- package/dist/v2/crypto/hkdf.d.ts.map +1 -0
- package/dist/v2/crypto/hkdf.js +32 -0
- package/dist/v2/crypto/hkdf.js.map +1 -0
- package/dist/v2/crypto/index.d.ts +9 -0
- package/dist/v2/crypto/index.d.ts.map +1 -0
- package/dist/v2/crypto/index.js +8 -0
- package/dist/v2/crypto/index.js.map +1 -0
- package/dist/v2/crypto/recipients.d.ts +43 -0
- package/dist/v2/crypto/recipients.d.ts.map +1 -0
- package/dist/v2/crypto/recipients.js +188 -0
- package/dist/v2/crypto/recipients.js.map +1 -0
- package/dist/v2/e2ee/decrypt.d.ts +13 -0
- package/dist/v2/e2ee/decrypt.d.ts.map +1 -0
- package/dist/v2/e2ee/decrypt.js +176 -0
- package/dist/v2/e2ee/decrypt.js.map +1 -0
- package/dist/v2/e2ee/encrypt-group.d.ts +14 -0
- package/dist/v2/e2ee/encrypt-group.d.ts.map +1 -0
- package/dist/v2/e2ee/encrypt-group.js +196 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -0
- package/dist/v2/e2ee/encrypt-p2p.d.ts +15 -0
- package/dist/v2/e2ee/encrypt-p2p.d.ts.map +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js +240 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -0
- package/dist/v2/e2ee/index.d.ts +9 -0
- package/dist/v2/e2ee/index.d.ts.map +1 -0
- package/dist/v2/e2ee/index.js +9 -0
- package/dist/v2/e2ee/index.js.map +1 -0
- package/dist/v2/e2ee/metadata-auth.d.ts +9 -0
- package/dist/v2/e2ee/metadata-auth.d.ts.map +1 -0
- package/dist/v2/e2ee/metadata-auth.js +60 -0
- package/dist/v2/e2ee/metadata-auth.js.map +1 -0
- package/dist/v2/e2ee/types.d.ts +57 -0
- package/dist/v2/e2ee/types.d.ts.map +1 -0
- package/dist/v2/e2ee/types.js +7 -0
- package/dist/v2/e2ee/types.js.map +1 -0
- package/dist/v2/session/index.d.ts +4 -0
- package/dist/v2/session/index.d.ts.map +1 -0
- package/dist/v2/session/index.js +3 -0
- package/dist/v2/session/index.js.map +1 -0
- package/dist/v2/session/keystore.d.ts +57 -0
- package/dist/v2/session/keystore.d.ts.map +1 -0
- package/dist/v2/session/keystore.js +244 -0
- package/dist/v2/session/keystore.js.map +1 -0
- package/dist/v2/session/session.d.ts +121 -0
- package/dist/v2/session/session.d.ts.map +1 -0
- package/dist/v2/session/session.js +344 -0
- package/dist/v2/session/session.js.map +1 -0
- package/dist/v2/state/commitment.d.ts +10 -0
- package/dist/v2/state/commitment.d.ts.map +1 -0
- package/dist/v2/state/commitment.js +86 -0
- package/dist/v2/state/commitment.js.map +1 -0
- package/dist/v2/state/index.d.ts +2 -0
- package/dist/v2/state/index.d.ts.map +1 -0
- package/dist/v2/state/index.js +2 -0
- package/dist/v2/state/index.js.map +1 -0
- package/package.json +8 -5
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# 15 - 离线推送通知协议 (Push Notification)
|
|
2
|
+
|
|
3
|
+
## 1. 概述
|
|
4
|
+
|
|
5
|
+
当目标 AID 的所有设备均离线时,AUN Gateway 通过事件通知将推送摘要发送给 `push_notify_aid`(推送代理 AID),由其完成最终的平台推送(APNs / FCM / WebPush 等)。
|
|
6
|
+
|
|
7
|
+
**设计原则:**
|
|
8
|
+
|
|
9
|
+
- push_notify_aid 是普通客户端 AID,不是 AUN 内部服务
|
|
10
|
+
- 通过事件通知(非 P2P 消息)下发推送摘要——轻量、实时、不持久化
|
|
11
|
+
- push_token 对 Gateway 完全不透明(opaque),push_notify_aid 自签自验
|
|
12
|
+
- 任意一台设备在线即不触发推送
|
|
13
|
+
- 推送内容仅含元数据,不含消息正文(E2EE 安全保证)
|
|
14
|
+
- 背压控制:串行确认,Gateway 等 push_notify_aid ack 后再发下一批
|
|
15
|
+
- 跨域推送:推送由**目标 AID 所属域**的 Gateway 触发,发送方域不参与
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. 架构
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
graph TB
|
|
23
|
+
Sender[发送方 AID] -->|message.send| GW[Gateway]
|
|
24
|
+
GW -->|目标全部离线| PA[PushAggregator]
|
|
25
|
+
PA -->|event/push.offline_message| GW
|
|
26
|
+
GW -->|事件通知| PNA[push_notify_aid<br/>推送代理客户端]
|
|
27
|
+
PNA -->|push.ack| GW
|
|
28
|
+
PNA -->|平台推送| PLATFORM[APNs / FCM / WebPush]
|
|
29
|
+
PLATFORM -->|通知| Target[目标终端]
|
|
30
|
+
|
|
31
|
+
PNA -.->|connect 到 Gateway 保持在线| GW
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**关键约束:**
|
|
35
|
+
|
|
36
|
+
- push_notify_aid 作为普通客户端 AID 连接到 Gateway,通过事件通知接收推送摘要
|
|
37
|
+
- push_notify_aid 离线时事件丢弃——推送是 best-effort,消息可靠性由 message.pull 保证
|
|
38
|
+
- 目标 AID 有任意一台设备在线时,不触发推送
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. 推送注册流程
|
|
43
|
+
|
|
44
|
+
### 3.1 应用层:客户端向推送代理注册
|
|
45
|
+
|
|
46
|
+
```mermaid
|
|
47
|
+
sequenceDiagram
|
|
48
|
+
participant App as 应用客户端
|
|
49
|
+
participant PNA as push_notify_aid
|
|
50
|
+
|
|
51
|
+
Note over App,PNA: 应用层行为,AUN 协议不规定细节
|
|
52
|
+
App->>PNA: 注册推送(aid, device_id, platform, device_token)
|
|
53
|
+
PNA->>PNA: 生成 push_token(格式自定)
|
|
54
|
+
PNA-->>App: push_token
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
push_token 的格式、签名算法、有效期完全由 push_notify_aid 自行决定。AUN 协议不做任何约束。
|
|
58
|
+
|
|
59
|
+
### 3.2 协议层:connect 时携带推送配置
|
|
60
|
+
|
|
61
|
+
```mermaid
|
|
62
|
+
sequenceDiagram
|
|
63
|
+
participant C as Client
|
|
64
|
+
participant GW as Gateway
|
|
65
|
+
|
|
66
|
+
C->>GW: connect(aid, token, push_notify_aid, push_token)
|
|
67
|
+
GW->>GW: 认证 AID 身份 ✓
|
|
68
|
+
GW->>GW: 持久化 push_config
|
|
69
|
+
GW-->>C: connected
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 4. 离线推送时序
|
|
75
|
+
|
|
76
|
+
```mermaid
|
|
77
|
+
sequenceDiagram
|
|
78
|
+
participant A as Sender AID
|
|
79
|
+
participant GW as Gateway
|
|
80
|
+
participant PA as PushAggregator
|
|
81
|
+
participant PNA as push_notify_aid
|
|
82
|
+
|
|
83
|
+
A->>GW: message.send(to=B, payload=...)
|
|
84
|
+
GW->>GW: 持久化消息 + 查询 B 在线状态
|
|
85
|
+
|
|
86
|
+
alt B 有任意设备在线
|
|
87
|
+
GW->>GW: 投递到 B 的在线 session
|
|
88
|
+
Note over GW: 不触发推送
|
|
89
|
+
else B 全部设备离线
|
|
90
|
+
GW->>PA: enqueue(target=B, from=A, msg_id, ts)
|
|
91
|
+
PA->>PA: 聚合窗口 + 频控 + 背压检查
|
|
92
|
+
|
|
93
|
+
alt 触发推送 且 in_flight < max
|
|
94
|
+
PA->>GW: event/push.offline_message
|
|
95
|
+
GW->>PNA: 事件通知(batch_id, summary, push_token)
|
|
96
|
+
PA->>PA: in_flight++, 启动 ack_timeout
|
|
97
|
+
PNA->>PNA: 验证 push_token ✓ → 发送平台推送
|
|
98
|
+
PNA->>GW: push.ack(batch_id)
|
|
99
|
+
GW->>PA: release(batch_id)
|
|
100
|
+
PA->>PA: in_flight--
|
|
101
|
+
else in_flight = max(背压)
|
|
102
|
+
PA->>PA: 排队等待槽位释放
|
|
103
|
+
else 频控抑制
|
|
104
|
+
PA->>PA: 延迟到下一窗口
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
GW-->>A: ack(msg_id, seq)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 5. 背压与 in-flight 控制
|
|
114
|
+
|
|
115
|
+
```mermaid
|
|
116
|
+
stateDiagram-v2
|
|
117
|
+
[*] --> Idle: 初始状态
|
|
118
|
+
Idle --> Sending: 有待推送 & in_flight < max
|
|
119
|
+
Sending --> WaitAck: 事件已发出, in_flight++
|
|
120
|
+
WaitAck --> Idle: 收到 push.ack, in_flight--
|
|
121
|
+
WaitAck --> Idle: ack_timeout 超时, in_flight--
|
|
122
|
+
WaitAck --> Queued: 新推送到达但 in_flight = max
|
|
123
|
+
Queued --> Sending: 槽位释放
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 参数
|
|
127
|
+
|
|
128
|
+
| 参数 | 默认值 | 说明 |
|
|
129
|
+
|------|--------|------|
|
|
130
|
+
| max_in_flight | 1 | 同一 push_notify_aid 未确认批次上限(串行) |
|
|
131
|
+
| ack_timeout | 30s | 超时未 ack 自动释放槽位 |
|
|
132
|
+
| batch_size | 50 | 每批最多聚合多少个 target_aid 的通知 |
|
|
133
|
+
|
|
134
|
+
### 行为规则
|
|
135
|
+
|
|
136
|
+
- in_flight < max → 发送事件,in_flight++
|
|
137
|
+
- in_flight = max → 排队等待
|
|
138
|
+
- 收到 `push.ack(batch_id)` → in_flight--,触发队列中下一批
|
|
139
|
+
- 超时 → in_flight--,**不重试**(推送是 best-effort,过时推送是噪音)
|
|
140
|
+
- push_notify_aid 离线 → 事件丢弃,in_flight 不增加
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 6. 在线判断规则
|
|
145
|
+
|
|
146
|
+
```mermaid
|
|
147
|
+
flowchart TD
|
|
148
|
+
A[消息到达,目标 AID = B] --> B{B 的在线设备数 > 0?}
|
|
149
|
+
B -->|≥ 1 台在线| C[投递到在线 session,不推送]
|
|
150
|
+
B -->|0 台在线| D{push_config 存在?}
|
|
151
|
+
D -->|是| F[进入 PushAggregator]
|
|
152
|
+
D -->|否| G[仅持久化,等对方上线 pull]
|
|
153
|
+
F --> H[聚合 + 频控 + 背压 → 推送]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**边界情况:**
|
|
157
|
+
|
|
158
|
+
- 目标 AID 上线瞬间:清空该 AID 的聚合桶,取消待发推送
|
|
159
|
+
- 最后一台设备断连:不立即触发推送,只有后续新消息到达时发现全离线才走推送路径
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 7. 跨域推送
|
|
164
|
+
|
|
165
|
+
**核心原则:推送由目标 AID 所属域的 Gateway 触发。**
|
|
166
|
+
|
|
167
|
+
发送方域不感知目标域的 push_config 和白名单,也不需要任何跨域配置同步。
|
|
168
|
+
|
|
169
|
+
```mermaid
|
|
170
|
+
sequenceDiagram
|
|
171
|
+
participant A as alice.domain-a.com
|
|
172
|
+
participant GW_A as Gateway A
|
|
173
|
+
participant GW_B as Gateway B
|
|
174
|
+
participant PA_B as PushAggregator B
|
|
175
|
+
participant PNA as push.domain-b.com<br/>(连在 GW_B)
|
|
176
|
+
|
|
177
|
+
A->>GW_A: message.send(to=bob.domain-b.com)
|
|
178
|
+
GW_A->>GW_A: 持久化消息
|
|
179
|
+
GW_A->>GW_B: 跨域 relay 消息
|
|
180
|
+
GW_B->>GW_B: 查询 bob 本地在线状态
|
|
181
|
+
|
|
182
|
+
alt bob 在本域有设备在线
|
|
183
|
+
GW_B->>GW_B: 投递到 bob 的在线 session
|
|
184
|
+
else bob 本域全部离线
|
|
185
|
+
GW_B->>PA_B: enqueue(target=bob, from=alice.domain-a.com)
|
|
186
|
+
PA_B->>GW_B: event/push.offline_message
|
|
187
|
+
GW_B->>PNA: 事件通知
|
|
188
|
+
PNA->>GW_B: push.ack
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
GW_B-->>GW_A: relay ack
|
|
192
|
+
GW_A-->>A: ack
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**规则:**
|
|
196
|
+
|
|
197
|
+
| 关注点 | 行为 |
|
|
198
|
+
|--------|------|
|
|
199
|
+
| 推送配置存储 | 仅存于目标 AID 所属域,不跨域同步 |
|
|
200
|
+
| 白名单管理 | 各域独立配置 `allowed_notify_aids`,互不影响 |
|
|
201
|
+
| push_notify_aid 必须连接位置 | 目标 AID 所属域的 Gateway |
|
|
202
|
+
| push_notify_aid 跨域时 | 不支持。push_notify_aid 必须与其服务的 AID 同域 |
|
|
203
|
+
| summary.senders 中的跨域 AID | 保留完整 AID(含域名),如 `alice.domain-a.com` |
|
|
204
|
+
|
|
205
|
+
**约束:** push_notify_aid 必须与其服务的目标 AID 处于同一域。如果应用希望在多个域提供推送服务,需要在每个域部署独立的 push_notify_aid 并分别加入白名单。
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 8. 聚合与频控
|
|
210
|
+
|
|
211
|
+
### 8.1 PushAggregator 数据结构
|
|
212
|
+
|
|
213
|
+
每个离线目标 AID 一个聚合桶:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
bucket[target_aid] = {
|
|
217
|
+
push_notify_aid: "push.myapp.com",
|
|
218
|
+
push_token: "eyJhb...",
|
|
219
|
+
pending: [{from, msg_id, ts}, ...],
|
|
220
|
+
first_enqueue_ts: timestamp,
|
|
221
|
+
last_push_ts: timestamp,
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 8.2 去重规则
|
|
226
|
+
|
|
227
|
+
同一 target_aid 在频控窗口内只产生一次推送通知。具体行为:
|
|
228
|
+
|
|
229
|
+
- 首条消息触发推送后,该 target_aid 进入频控冷却期(默认 60s)
|
|
230
|
+
- 冷却期内新消息只更新聚合桶的 summary(unread_count++、senders 去重追加),不产生新推送
|
|
231
|
+
- 冷却期结束时,若桶内有新增未推送内容,合并为一条推送发出
|
|
232
|
+
|
|
233
|
+
效果:无论短时间内收到多少条消息,target_aid 最多每 60s 收到一次推送通知,且 summary 是累积聚合的。
|
|
234
|
+
|
|
235
|
+
### 8.3 触发规则
|
|
236
|
+
|
|
237
|
+
| 规则 | 说明 | 默认值 |
|
|
238
|
+
|------|------|--------|
|
|
239
|
+
| 首条即推 | 桶为空且不在冷却期时,第一条消息立即触发 | 开启 |
|
|
240
|
+
| 窗口聚合 | 首条之后的消息等待窗口合并 | 5 秒 |
|
|
241
|
+
| 数量上限 | 桶内积累 N 条立即触发(不等窗口) | 20 条 |
|
|
242
|
+
|
|
243
|
+
### 8.4 频控规则
|
|
244
|
+
|
|
245
|
+
| 维度 | 限制 | 说明 |
|
|
246
|
+
|------|------|------|
|
|
247
|
+
| 同一 target_aid | 60 秒内最多 1 次推送 | 避免终端刷屏 |
|
|
248
|
+
| 同一 push_notify_aid | 1000 次/分钟 | 保护推送代理服务 |
|
|
249
|
+
| 全局 | 5000 次/分钟 | 系统保护 |
|
|
250
|
+
|
|
251
|
+
频控被触发时消息不丢弃,等窗口过后下一次聚合时一并推送。
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 9. 协议字段
|
|
256
|
+
|
|
257
|
+
### 9.1 connect 扩展字段
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"method": "auth.login",
|
|
262
|
+
"params": {
|
|
263
|
+
"aid": "bob.example.com",
|
|
264
|
+
"challenge_response": "...",
|
|
265
|
+
"push_notify_aid": "push.myapp.com",
|
|
266
|
+
"push_token": "eyJhbGciOiJIUzI1NiJ9..."
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
- `push_notify_aid` 和 `push_token` 均为可选字段
|
|
272
|
+
- 两者必须同时提供或同时不提供
|
|
273
|
+
- 不提供 = 不需要离线推送
|
|
274
|
+
- 每次 connect 覆盖写(最新连接为准)
|
|
275
|
+
|
|
276
|
+
### 9.2 事件通知格式
|
|
277
|
+
|
|
278
|
+
Gateway 向 push_notify_aid 下发的事件通知:
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"method": "event/push.offline_message",
|
|
283
|
+
"params": {
|
|
284
|
+
"batch_id": "uuid-xxx",
|
|
285
|
+
"items": [
|
|
286
|
+
{
|
|
287
|
+
"target_aid": "bob.example.com",
|
|
288
|
+
"push_token": "eyJhbGciOiJIUzI1NiJ9...",
|
|
289
|
+
"summary": {
|
|
290
|
+
"unread_count": 3,
|
|
291
|
+
"senders": ["alice.example.com", "charlie.example.com"],
|
|
292
|
+
"latest_ts": 1716100005,
|
|
293
|
+
"group_ids": ["group-uuid-1"]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**安全约束:** 不包含消息正文。E2EE 场景下 Gateway 无法解密,推送代理只需知道"有 N 条未读来自谁"即可构造推送文案。
|
|
302
|
+
|
|
303
|
+
### 9.3 push.ack RPC
|
|
304
|
+
|
|
305
|
+
push_notify_aid 处理完一批推送后回调确认:
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"method": "push.ack",
|
|
310
|
+
"params": {
|
|
311
|
+
"batch_id": "uuid-xxx"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Gateway 收到后释放 in-flight 槽位,触发下一批。
|
|
317
|
+
|
|
318
|
+
### 9.4 push.update_config RPC(可选)
|
|
319
|
+
|
|
320
|
+
允许客户端在不重连的情况下更新推送配置:
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"method": "push.update_config",
|
|
325
|
+
"params": {
|
|
326
|
+
"push_notify_aid": "push.myapp.com",
|
|
327
|
+
"push_token": "new-token..."
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 10. push_token 鉴权模型
|
|
335
|
+
|
|
336
|
+
### 10.1 push_notify_aid 白名单
|
|
337
|
+
|
|
338
|
+
Gateway 配置允许的 push_notify_aid 白名单,只有白名单内的 AID 才能被设置为推送代理:
|
|
339
|
+
|
|
340
|
+
- 客户端 connect 时携带的 `push_notify_aid` 不在白名单内 → 忽略该字段,不报错,不存储
|
|
341
|
+
- 白名单为空 → 禁用推送功能
|
|
342
|
+
- 白名单配置在 Gateway 启动配置中,运行时不可动态修改
|
|
343
|
+
|
|
344
|
+
```json
|
|
345
|
+
{
|
|
346
|
+
"push": {
|
|
347
|
+
"allowed_notify_aids": [
|
|
348
|
+
"push.myapp.com",
|
|
349
|
+
"push.partner.com"
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 10.2 token 透传模型
|
|
356
|
+
|
|
357
|
+
```mermaid
|
|
358
|
+
sequenceDiagram
|
|
359
|
+
participant PNA as push_notify_aid
|
|
360
|
+
participant GW as Gateway
|
|
361
|
+
|
|
362
|
+
Note over PNA,GW: push_token 生命周期
|
|
363
|
+
PNA->>PNA: 签发 push_token(格式/算法自定)
|
|
364
|
+
Note over GW: Gateway 存储 push_token(opaque bytes)
|
|
365
|
+
Note over GW: 不验签、不解析、不判断过期
|
|
366
|
+
GW->>PNA: event/push.offline_message(..., push_token="xxx")
|
|
367
|
+
PNA->>PNA: 自己验签 push_token
|
|
368
|
+
alt token 有效
|
|
369
|
+
PNA->>PNA: 执行推送
|
|
370
|
+
PNA->>GW: push.ack(batch_id)
|
|
371
|
+
else token 无效/过期
|
|
372
|
+
PNA->>PNA: 丢弃
|
|
373
|
+
PNA->>GW: push.ack(batch_id)
|
|
374
|
+
Note over PNA: 客户端下次 connect 带新 token
|
|
375
|
+
end
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**职责划分:**
|
|
379
|
+
|
|
380
|
+
| 角色 | 职责 |
|
|
381
|
+
|------|------|
|
|
382
|
+
| push_notify_aid | 签发 token、验证 token、决定格式和有效期、处理后 ack |
|
|
383
|
+
| Gateway | 存储 token、透传 token、不解析不验证、管理 in-flight |
|
|
384
|
+
| Client | 向 push_notify_aid 申请 token、connect 时携带 |
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 11. 持久化
|
|
389
|
+
|
|
390
|
+
### push_config 表
|
|
391
|
+
|
|
392
|
+
```sql
|
|
393
|
+
CREATE TABLE push_config (
|
|
394
|
+
aid VARCHAR(255) PRIMARY KEY,
|
|
395
|
+
push_notify_aid VARCHAR(255) NOT NULL,
|
|
396
|
+
push_token TEXT NOT NULL,
|
|
397
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
398
|
+
);
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
- 随 connect 写入/更新(UPSERT)
|
|
402
|
+
- 可加内存缓存(TTL 5 分钟)
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 12. 边界情况
|
|
407
|
+
|
|
408
|
+
| 场景 | 处理 |
|
|
409
|
+
|------|------|
|
|
410
|
+
| push_notify_aid 离线 | 事件丢弃,不持久化。推送是 best-effort,消息可靠性由 pull 保证 |
|
|
411
|
+
| 目标 AID 未配置推送 | 不推送,消息正常持久化等对方上线 pull |
|
|
412
|
+
| 目标 AID 上线瞬间 | 清空聚合桶,取消待发推送 |
|
|
413
|
+
| E2EE 消息 | 推送内容只含元数据(发送者、数量),不含密文 |
|
|
414
|
+
| 群组消息 | 群内每个离线成员独立聚合,summary 含 group_ids |
|
|
415
|
+
| push_token 过期 | push_notify_aid 丢弃但仍 ack,客户端下次 connect 刷新 token |
|
|
416
|
+
| ack 超时 | 释放槽位,不重试。后续新消息触发新批次时会包含累积通知 |
|
|
417
|
+
| 同一 AID 多设备不同推送配置 | 以最后一次 connect 为准(单条记录覆盖) |
|
|
418
|
+
| 跨域消息推送 | 由目标 AID 所属域的 Gateway 触发,发送方域不参与 |
|
|
419
|
+
| push_notify_aid 跨域 | 不支持,必须与目标 AID 同域 |
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| 00 | [00-总览与分层.md](00-总览与分层.md) | 协议目标、核心原则、四层架构、三种连接模式、文档导航 |
|
|
12
12
|
| 01 | [01-身份与凭证协议-auth.md](01-身份与凭证协议-auth.md) | `auth.*` 方法定义、AID/证书/私钥/token 关系、JWT 机制 |
|
|
13
13
|
| 02 | [02-证书与信任体系.md](02-证书与信任体系.md) | AID 体系、四级证书链、信任模型、吊销机制 |
|
|
14
|
-
| 03 | [03-Gateway-连接模式.md](03-Gateway-连接模式.md) | Gateway 模式定位、auth.connect 握手、心跳重连 |
|
|
14
|
+
| 03 | [03-Gateway-连接模式.md](03-Gateway-连接模式.md) | Gateway 模式定位、auth.connect 握手、心跳重连 |
|
|
15
15
|
| 04 | [04-Peer-子协议.md](04-Peer-子协议.md) | `peer.*` 对等认证四步握手、nonce 签名、状态机 |
|
|
16
16
|
| 05 | [05-Relay-子协议.md](05-Relay-子协议.md) | `relay.*` 中继注册转发、透明封装、与 peer.* 关系 |
|
|
17
17
|
| 06 | [06-服务协议.md](06-服务协议.md) | 业务层:message.* / meta.* / search.* / task.* / group.* + 跨域消息路由 |
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
| 10 | [10-Group-子协议.md](10-Group-子协议.md) | `group.*` 群组管理、群消息、邀请码、资源共享、在线状态 |
|
|
23
23
|
| 11 | [11-Storage-子协议.md](11-Storage-子协议.md) | `storage.*` 对象存储、大文件上传下载、预签名 URL |
|
|
24
24
|
| 12 | [12-Stream-子协议.md](12-Stream-子协议.md) | `stream.*` 实时流式传输、WebSocket 推流、HTTP SSE 拉流、跨域拉流 |
|
|
25
|
+
| 15 | [15-离线推送通知协议.md](15-离线推送通知协议.md) | `push.*` 离线推送、push_notify_aid 代理、事件通知 + 背压 ack、白名单与去重 |
|
|
25
26
|
|
|
26
27
|
**附录**:A-术语表 | B-扩展性 | C-私钥管理 | D-Root CA 治理 | E-Root CA 准入 | F-Issuer CA 申请 | G-孤儿 AID | H-Auth 实现 | I-跨域消息路由 | J-客户端接入 | K-Agent Web | L-E2EE 实现 | M-JWT 实现
|
|
27
28
|
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
| JWT Token 机制 | 01 §1.7 |
|
|
42
43
|
| 证书续期与轮转(renew/rekey) | 01 §1.6 |
|
|
43
44
|
| Gateway 连接模式 | 03 |
|
|
44
|
-
| auth.connect 握手 | 03 §3.5 |
|
|
45
|
+
| auth.connect 握手 | 03 §3.5 |
|
|
45
46
|
| Peer 对等认证 | 04 |
|
|
46
47
|
| Relay 中继传输 | 05 |
|
|
47
48
|
| 消息收发(message.*) | 06 §6.2 |
|
|
@@ -61,6 +62,12 @@
|
|
|
61
62
|
| 推流(WebSocket) | 12 §12.6 推流端点 |
|
|
62
63
|
| 拉流(HTTP SSE) | 12 §12.6 拉流端点 |
|
|
63
64
|
| 跨域拉流 | 12 §12.7 |
|
|
65
|
+
| 离线推送通知 | 15 |
|
|
66
|
+
| push_notify_aid 推送代理 | 15 §2, §10.1 |
|
|
67
|
+
| push_token 鉴权 | 15 §10 |
|
|
68
|
+
| 推送背压与 in-flight | 15 §5 |
|
|
69
|
+
| 推送去重与频控 | 15 §8.2, §8.4 |
|
|
70
|
+
| 跨域推送 | 15 §7 |
|
|
64
71
|
| 错误码 | 07 §7.1 |
|
|
65
72
|
| 连接状态机 | 07 §7.3 |
|
|
66
73
|
| 威胁模型与安全 | 09 |
|
|
@@ -81,7 +88,7 @@
|
|
|
81
88
|
AID 格式与命名规则、AUN 网络与根证书管理局、四级证书链(Root CA → Registry CA → Issuer CA → Agent)、终端证书生命周期状态、信任模型与共识场景、CRL/OCSP 吊销机制、CT 公开查询入口概述。
|
|
82
89
|
|
|
83
90
|
### 03-Gateway-连接模式
|
|
84
|
-
Gateway 模式定位与职责、Gateway 发现机制、连接时序(auth.* → auth.connect → READY)、auth.connect 请求/响应格式、心跳与重连策略。
|
|
91
|
+
Gateway 模式定位与职责、Gateway 发现机制、连接时序(auth.* → auth.connect → READY)、auth.connect 请求/响应格式、心跳与重连策略。
|
|
85
92
|
|
|
86
93
|
### 04-Peer-子协议
|
|
87
94
|
`peer.*` 4 个方法:hello、hello_reply、confirm、confirmed。对称 challenge-response 双向认证,不依赖 JWT。状态机、证书链验证规则、nonce 签名规则、Peer 地址发现。
|
|
@@ -112,3 +119,6 @@ Gateway 模式定位与职责、Gateway 发现机制、连接时序(auth.* →
|
|
|
112
119
|
|
|
113
120
|
### 12-Stream-子协议
|
|
114
121
|
`stream.*` 命名空间完整协议规范。控制面通过 JSON-RPC(stream.create / close / get_info / list_active)管理流生命周期,数据面通过独立端口的 WebSocket(推流)和 HTTP SSE(拉流)传输。当前实现以 push/pull URL 中的 token 作为能力凭证,拉流可跨域使用,并支持 Late Joiner 回放、Last-Event-ID 断线续拉、空闲/离线超时自动关闭。
|
|
122
|
+
|
|
123
|
+
### 15-离线推送通知协议
|
|
124
|
+
目标 AID 全部设备离线时的推送机制。push_notify_aid 作为普通客户端 AID 连接 Gateway,通过 `event/push.offline_message` 事件接收推送摘要(仅元数据,不含正文),处理后回 `push.ack` RPC 释放 in-flight 槽位(默认 max_in_flight=1 串行确认,30s 超时不重试)。聚合机制:同一 target_aid 60s 冷却期内合并为一条推送(unread_count++、senders 去重追加)。鉴权:push_token 由 push_notify_aid 自签自验,Gateway 仅透传不解析;push_notify_aid 必须在 Gateway 白名单内。跨域:推送由目标 AID 所属域的 Gateway 触发,push_notify_aid 必须与目标 AID 同域。
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Python SDK V2-Only 改动清单
|
|
2
|
+
|
|
3
|
+
基线:`d8b79f44` (v0.2.20 之后,master_1.0.0 分支)
|
|
4
|
+
日期:2026-05-20
|
|
5
|
+
|
|
6
|
+
## 一、源码改动(`python/src/aun_core/`)
|
|
7
|
+
|
|
8
|
+
### 1. client.py:8588 行 → 4502 行(净删 4086 行)
|
|
9
|
+
|
|
10
|
+
#### 删除的 V1 E2EE 代码(共 ~95 个方法)
|
|
11
|
+
|
|
12
|
+
**V1 P2P 加密:**
|
|
13
|
+
- `_send_encrypted`, `_build_self_sync_copies`, `_build_recipient_device_copies`
|
|
14
|
+
- `_encrypt_copy_payload`, `_ensure_encrypt_result`, `_resolve_self_copy_peer_cert`
|
|
15
|
+
- `_log_e2ee_error`
|
|
16
|
+
|
|
17
|
+
**V1 群组加密:**
|
|
18
|
+
- `_send_group_encrypted`, `_put_group_thought_encrypted`, `_put_message_thought_encrypted`
|
|
19
|
+
- `_call_group_encrypted_rpc`, `_prepare_group_encrypted_rpc_params`
|
|
20
|
+
|
|
21
|
+
**V1 Epoch 管理:**
|
|
22
|
+
- `_recover_group_epoch_key`, `_do_recover_group_epoch_key`, `_try_recover_epoch_key_from_server`
|
|
23
|
+
- `_recover_initial_group_epoch_if_needed`, `_request_group_key_from_candidates`
|
|
24
|
+
- `_request_group_key_from_online`, `_request_group_key_from`
|
|
25
|
+
- `_group_epoch_secret_ready_for_recovery`, `_pending_group_secret_still_current`
|
|
26
|
+
- `_ensure_group_epoch_ready`, `_wait_for_group_membership_epoch_floor`
|
|
27
|
+
- `_committed_group_epoch`, `_committed_group_epoch_state`
|
|
28
|
+
- `_ensure_committed_group_secret_for_send`, `_committed_rotation_membership_gap`
|
|
29
|
+
|
|
30
|
+
**V1 密钥控制面:**
|
|
31
|
+
- `_try_handle_group_key_message`, `_verify_active_group_rotation_distribution`
|
|
32
|
+
- `_verify_group_key_response_epoch`, `_discard_group_distribution_if_stale`
|
|
33
|
+
- `_ack_group_rotation_key`
|
|
34
|
+
|
|
35
|
+
**V1 Epoch 轮换:**
|
|
36
|
+
- `_build_rotation_signature`, `_attach_rotation_id`, `_build_epoch_encrypted_keys`
|
|
37
|
+
- `_distribute_group_epoch_key`, `_heartbeat_group_rotation`, `_abort_group_rotation`
|
|
38
|
+
- `_schedule_group_rotation_retry`, `_sync_epoch_to_server`
|
|
39
|
+
- `_maybe_lead_rotate_group_epoch`, `_ranked_group_rotation_candidates`, `_rotate_group_epoch`
|
|
40
|
+
- `_delayed_rotate_after_join`, `_maybe_backfill_key_to_joined_member`
|
|
41
|
+
- `_distribute_key_to_new_member`, `_start_group_epoch_tasks`
|
|
42
|
+
- `_is_rotation_leader`, `_group_epoch_rotate_loop`, `_group_epoch_cleanup_loop`
|
|
43
|
+
|
|
44
|
+
**V1 解密:**
|
|
45
|
+
- `_decrypt_messages`, `_decrypt_single_message`
|
|
46
|
+
- `_decrypt_group_message`, `_decrypt_group_messages`
|
|
47
|
+
- `_decrypt_group_thoughts`, `_decrypt_message_thoughts`
|
|
48
|
+
|
|
49
|
+
**V1 Pending queue:**
|
|
50
|
+
- `_enqueue_pending_decrypt`, `_schedule_retry_pending_decrypt_msgs`
|
|
51
|
+
- `_retry_pending_decrypt_msgs`, `_schedule_recovery_timeout`, `_cleanup_group_state`
|
|
52
|
+
|
|
53
|
+
**V1 Prekey:**
|
|
54
|
+
- `_upload_prekey`, `_prekey_refresh_loop`, `_start_prekey_refresh_task`
|
|
55
|
+
- `_schedule_prekey_replenish_if_consumed`, `_invalidate_peer_prekey_cache`
|
|
56
|
+
- `_fetch_peer_prekeys`, `_fetch_peer_prekey`, `_normalize_peer_prekeys`
|
|
57
|
+
- `_refresh_peer_prekeys`, `_clear_peer_cert_cache`
|
|
58
|
+
|
|
59
|
+
**V1 错误辅助:**
|
|
60
|
+
- `_is_group_epoch_too_old_error`, `_is_group_epoch_rotation_pending_error`
|
|
61
|
+
- `_is_group_epoch_changed_during_send_error`, `_is_recoverable_group_epoch_error`
|
|
62
|
+
- `_is_expected_group_rotation_skip_error`
|
|
63
|
+
|
|
64
|
+
**V1 成员轮换辅助:**
|
|
65
|
+
- `_membership_rotation_trigger_id`, `_membership_rotation_changed`
|
|
66
|
+
- `_membership_rotation_expected_epoch`, `_extract_group_id_from_result`
|
|
67
|
+
- `_get_group_member_aids`, `_local_group_members_match`, `_extract_group_join_mode`
|
|
68
|
+
- `_group_allows_member_epoch_rotation`, `_group_key_recovery_candidates`
|
|
69
|
+
- `_joined_member_aids_from_payload`
|
|
70
|
+
|
|
71
|
+
**V1 群组推送处理:**
|
|
72
|
+
- `_process_and_publish_group_message`, `_auto_pull_group_messages`, `_fill_group_gap`
|
|
73
|
+
|
|
74
|
+
**V1 公开 API:**
|
|
75
|
+
- `e2ee` property, `group_e2ee` property
|
|
76
|
+
- `send_v2`, `pull_v2`, `ack_v2`, `send_group_v2`, `pull_group_v2`, `ack_group_v2`
|
|
77
|
+
(独立方法合并进 `call()` 路由)
|
|
78
|
+
|
|
79
|
+
#### 删除的 V1 实例变量
|
|
80
|
+
|
|
81
|
+
- `self._e2ee` (E2EEManager)
|
|
82
|
+
- `self._group_e2ee` (GroupE2EEManager)
|
|
83
|
+
- `self._pending_decrypt_msgs`
|
|
84
|
+
- `self._recovery_timeout_scheduled`
|
|
85
|
+
- `self._group_epoch_rotation_inflight`
|
|
86
|
+
- `self._group_epoch_rotation_retry_tasks`
|
|
87
|
+
- `self._group_epoch_recovery_inflight`
|
|
88
|
+
- `self._group_membership_rotation_done`
|
|
89
|
+
- `self._group_member_key_backfill_done`
|
|
90
|
+
|
|
91
|
+
#### 新增/修改的逻辑
|
|
92
|
+
|
|
93
|
+
- `_on_raw_group_message_created`:简化为明文消息透传 + seq 跟踪(不再做 V1 解密)
|
|
94
|
+
- `_process_and_publish_message`:移除 V1 解密调用,明文直接透传
|
|
95
|
+
- `_on_raw_group_changed`:移除 V1 epoch 轮换编排,保留 V2 `_v2_auto_propose_state` + event gap 检测
|
|
96
|
+
- `call()` 中 `message.pull` / `group.pull` 后处理:移除 V1 解密,保留 seq 跟踪 + auto-ack
|
|
97
|
+
- `_stop_background_tasks`:移除 V1 epoch 任务清理
|
|
98
|
+
- `_start_background_tasks`:移除 `_start_group_epoch_tasks()` 调用
|
|
99
|
+
- V2 解密元数据:`e2ee` 字段增加 `encryption_mode` 和 `forward_secrecy`
|
|
100
|
+
- V2 group pull:移除返回值去重(pull 始终返回所有解密成功的消息)
|
|
101
|
+
|
|
102
|
+
### 2. seq_tracker.py:+14 行
|
|
103
|
+
|
|
104
|
+
- `on_pull_result` 新增 `after_seq` 参数
|
|
105
|
+
- gap fill 场景(`after_seq == contiguous_seq`):直接把 pull 到的最大 seq 作为新 `contiguous_seq`,跳过服务端永久空洞
|
|
106
|
+
|
|
107
|
+
### 3. `__init__.py`:无变化(`ProtectedHeaders` 保留)
|
|
108
|
+
|
|
109
|
+
### 4. `e2ee.py`:不动(3544 行保留作为参考)
|
|
110
|
+
|
|
111
|
+
### 5. 保留的 e2ee.py 引用(非 E2EE 加解密)
|
|
112
|
+
|
|
113
|
+
- `from .e2ee import ProtectedHeaders` — 纯数据类,V2 也用
|
|
114
|
+
- `from .e2ee import compute_state_hash` (2处) — 群组 state hash 验证工具函数
|
|
115
|
+
|
|
116
|
+
## 二、测试改动(`python/tests/`)
|
|
117
|
+
|
|
118
|
+
### 删除的测试文件(纯 V1 E2EE)
|
|
119
|
+
|
|
120
|
+
- `e2e_test_epoch_key_server.py` — V1 epoch key 服务端托管
|
|
121
|
+
- `integration_test_e2ee.py` — V1 P2P E2EE 集成
|
|
122
|
+
- `integration_test_multi_device_e2ee.py` — V1 多设备 E2EE
|
|
123
|
+
- `unit/test_client_group_e2ee.py` — V1 群组 E2EE 单元测试(93 个用例)
|
|
124
|
+
|
|
125
|
+
### 删除的测试用例(从现有文件中移除)
|
|
126
|
+
|
|
127
|
+
- `test_client.py`:24 个 V1 测试(prekey、send_encrypted V1、decrypt_group 等)
|
|
128
|
+
- `test_py_issues.py`:`TestPY004PrekeyRefreshLoop`
|
|
129
|
+
- `test_py_issues_batch2.py`:`TestPY001DecryptFailStillAutoAck`、`TestPY002KeyRecoveryRetry`、`TestPY003DissolveCleanup`、`TestPY005RotateLoopLeaderElection`
|
|
130
|
+
- `test_py_issues_batch3.py`:`TestPY002PushedSeqsLimit`、`TestPY005EpochWait`
|
|
131
|
+
|
|
132
|
+
### 修改的测试
|
|
133
|
+
|
|
134
|
+
- `test_client.py`:`test_e2ee_property` 改为断言 `e2ee` 属性不存在
|
|
135
|
+
- `integration_test_storage.py`:自定义 redirect handler 支持 PUT 302 重定向
|
|
136
|
+
|
|
137
|
+
## 三、服务端改动(`extensions/services/`)
|
|
138
|
+
|
|
139
|
+
| 文件 | 改动 |
|
|
140
|
+
|------|------|
|
|
141
|
+
| `gateway/entry.py` | kernel event 订阅补全 V2 事件(fallback);`_handle_event_notification` 路由补全 V2 事件 |
|
|
142
|
+
| `gateway/ws_server.py` | `_dispatch_event_from_service` 白名单补全 `group.state_committed` |
|
|
143
|
+
| `gateway/relay.py` | `_should_forward_event` 补全 `group.state_committed`;`_V2_ONLY_GROUP_METHODS` 补全 `propose_state/confirm_state/get_proposal` |
|
|
144
|
+
| `message/entry.py` | `AUN_DIRECT_EVENT_MESSAGE` 默认改为 True;V2 send 返回 status 对齐 V1 语义(`delivered`/`sent`) |
|
|
145
|
+
| `group/entry.py` | `AUN_DIRECT_EVENT_GROUP` 默认改为 True;`_targets_for_group_event` / `is_client_event` 补全 `group.state_committed`;`_rpc_v2_pull` / `_rpc_v2_ack` 补全成员权限检查 |
|
|
146
|
+
|
|
147
|
+
## 四、Bug 修复(需要其它 SDK 对齐)
|
|
148
|
+
|
|
149
|
+
### BUG-1: gap fill 时 contiguous_seq 卡死
|
|
150
|
+
|
|
151
|
+
**根因:** `on_pull_result` 从 `contiguous_seq` 开始 pull,如果服务端返回的消息跳过了某些 seq(永久空洞:竞态跳跃/未持久化/过期清理),`_try_advance` 逐个检查时会卡在第一个缺失的 seq 上,永远无法推进。新消息被阻塞在 SDK 内部有序队列中,上层收不到。
|
|
152
|
+
|
|
153
|
+
**修复:** `on_pull_result` 新增 `after_seq` 参数。当 `after_seq == contiguous_seq`(gap fill 场景)时,直接把 pull 到的最大 seq 作为新的 `contiguous_seq`。
|
|
154
|
+
|
|
155
|
+
**影响范围:** P2P pull、group pull、event pull 三条路径。
|
|
156
|
+
|
|
157
|
+
### BUG-2: V2 group pull 返回值去重导致手动 pull 拿不到已 push 的消息
|
|
158
|
+
|
|
159
|
+
**根因:** `_pull_group_v2_internal` 中 `_is_published_seq` 去重导致已通过 push 自动 pull 消费的消息在手动 pull 时被跳过,返回空列表。
|
|
160
|
+
|
|
161
|
+
**修复:** pull 返回值不再做 `_is_published_seq` 去重。`_publish_ordered_message` 内部仍做事件投递去重(防止重复触发应用层回调),但 pull 的返回值始终包含所有解密成功的消息。
|
|
162
|
+
|
|
163
|
+
### BUG-3: V2 message.send 返回 status 不对齐 V1 语义
|
|
164
|
+
|
|
165
|
+
**根因:** V2 send 返回 `"status": "accepted"`,但 SDK 和应用层已按 V1 语义(`sent`/`delivered`)实现。
|
|
166
|
+
|
|
167
|
+
**修复:** 服务端 `delivered_count > 0` 时返回 `delivered`,否则返回 `sent`。
|
|
168
|
+
|
|
169
|
+
### BUG-4: V2 解密元数据缺少 encryption_mode
|
|
170
|
+
|
|
171
|
+
**根因:** V2 解密后 `e2ee` 字段只有 `version` + `suite`,缺少 `encryption_mode` 和 `forward_secrecy`,导致依赖这些字段的测试/应用层判断失败。
|
|
172
|
+
|
|
173
|
+
**修复:** 补全 `encryption_mode: "v2_{suite}"` 和 `forward_secrecy: True`。
|
|
174
|
+
|
|
175
|
+
## 五、验证结果
|
|
176
|
+
|
|
177
|
+
| 测试 | 结果 |
|
|
178
|
+
|------|------|
|
|
179
|
+
| 单元测试 | 501 passed |
|
|
180
|
+
| V2 P2P E2EE | 12/12 |
|
|
181
|
+
| V2 Group E2EE | 8/8 |
|
|
182
|
+
| V2 Multi-device | 6/6 |
|
|
183
|
+
| Echo | 5/5 |
|
|
184
|
+
| Message Ack | 4/4 |
|
|
185
|
+
| Storage | 4/4 |
|
|
186
|
+
| 双域明文 | PASS |
|
|
187
|
+
| 双域加密 | PASS |
|
|
188
|
+
| 双域离线 | PASS |
|
|
189
|
+
| 双域群组 | 2/2 |
|