@agentunion/fastaun-browser 0.2.19 → 0.3.0

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.
Files changed (184) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/_packed_docs/CHANGELOG.md +50 -0
  3. package/_packed_docs/agent.md/SCHEMA.md +173 -0
  4. package/_packed_docs/agent.md/examples/codeagent-claudecode.md +61 -0
  5. package/_packed_docs/agent.md/examples/human-developer.md +60 -0
  6. package/_packed_docs/agent.md/examples/openclaw-lobster.md +52 -0
  7. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +43 -0
  8. package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +205 -0
  9. package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +197 -0
  10. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +549 -0
  11. package/_packed_docs/protocol/02-/350/257/201/344/271/246/344/270/216/344/277/241/344/273/273/344/275/223/347/263/273.md +810 -0
  12. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +262 -0
  13. package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +180 -0
  14. package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +164 -0
  15. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1135 -0
  16. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +234 -0
  17. package/_packed_docs/protocol/08-AUN-E2EE-Group.md +900 -0
  18. package/_packed_docs/protocol/08-AUN-E2EE.md +413 -0
  19. package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +316 -0
  20. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +804 -0
  21. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +271 -0
  22. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +329 -0
  23. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +141 -0
  24. package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +170 -0
  25. 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
  26. package/_packed_docs/protocol/README.md +71 -0
  27. package/_packed_docs/protocol/agent.md/SCHEMA.md +118 -0
  28. package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +61 -0
  29. package/_packed_docs/protocol/agent.md/examples/human-developer.md +60 -0
  30. package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +52 -0
  31. package/_packed_docs/protocol/aun-docs-guide.md +49 -0
  32. package/_packed_docs/protocol/index.md +124 -0
  33. package/_packed_docs/protocol//350/215/211/346/241/210-agent.md/347/255/276/345/220/215/345/215/217/350/256/256.md +205 -0
  34. package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +249 -0
  35. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +337 -0
  36. package/_packed_docs/protocol//351/231/204/345/275/225B-/346/211/251/345/261/225/346/200/247/346/214/207/345/215/227.md +80 -0
  37. package/_packed_docs/protocol//351/231/204/345/275/225C-/347/247/201/351/222/245/347/256/241/347/220/206/344/270/216/350/272/253/344/273/275/346/201/242/345/244/215.md +704 -0
  38. package/_packed_docs/protocol//351/231/204/345/275/225D-Root_CA_/346/262/273/347/220/206/346/234/272/345/210/266.md +620 -0
  39. package/_packed_docs/protocol//351/231/204/345/275/225E-Root_CA_/345/207/206/345/205/245/346/265/201/347/250/213.md +605 -0
  40. package/_packed_docs/protocol//351/231/204/345/275/225F-Issuer_CA_/347/224/263/350/257/267/346/265/201/347/250/213.md +548 -0
  41. package/_packed_docs/protocol//351/231/204/345/275/225G-AID_/345/255/244/345/204/277/351/242/204/351/230/262/344/270/216/346/225/221/346/217/264/346/234/272/345/210/266.md +513 -0
  42. package/_packed_docs/protocol//351/231/204/345/275/225H-Identity/346/234/215/345/212/241/345/256/236/347/216/260/346/214/207/345/215/227.md +619 -0
  43. package/_packed_docs/protocol//351/231/204/345/275/225I-/350/267/250/345/237/237/346/266/210/346/201/257/350/267/257/347/224/261/345/256/236/347/216/260/346/214/207/345/215/227.md +492 -0
  44. package/_packed_docs/protocol//351/231/204/345/275/225J-/345/256/242/346/210/267/347/253/257/346/216/245/345/205/245/347/244/272/344/276/213.md +402 -0
  45. package/_packed_docs/protocol//351/231/204/345/275/225K-Agent_Web/345/217/221/347/216/260/345/215/217/350/256/256.md +130 -0
  46. package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +267 -0
  47. package/_packed_docs/protocol//351/231/204/345/275/225M-JWT/350/256/244/350/257/201/345/256/236/347/216/260/346/214/207/345/215/227.md +367 -0
  48. package/_packed_docs/python-sdk-v2-only-changelog.md +189 -0
  49. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +223 -0
  50. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +354 -0
  51. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +172 -0
  52. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +396 -0
  53. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +611 -0
  54. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +1203 -0
  55. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +150 -0
  56. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +89 -0
  57. package/_packed_docs/sdk/09-custody-api-manual.md +445 -0
  58. package/_packed_docs/sdk/09-group-rpc-manual.md +1895 -0
  59. package/_packed_docs/sdk/09-message-rpc-manual.md +597 -0
  60. package/_packed_docs/sdk/09-meta-rpc-manual.md +142 -0
  61. package/_packed_docs/sdk/09-payload-reference.md +702 -0
  62. package/_packed_docs/sdk/09-storage-rpc-manual.md +408 -0
  63. package/_packed_docs/sdk/09-stream-rpc-manual.md +275 -0
  64. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +72 -0
  65. package/_packed_docs/sdk/INDEX.md +131 -0
  66. package/_packed_docs/sdk/README.md +307 -0
  67. package/dist/auth.d.ts +2 -1
  68. package/dist/auth.d.ts.map +1 -1
  69. package/dist/auth.js +33 -14
  70. package/dist/auth.js.map +1 -1
  71. package/dist/bundle.js +14300 -0
  72. package/dist/client.d.ts +200 -178
  73. package/dist/client.d.ts.map +1 -1
  74. package/dist/client.js +3096 -4019
  75. package/dist/client.js.map +1 -1
  76. package/dist/config.d.ts +0 -4
  77. package/dist/config.d.ts.map +1 -1
  78. package/dist/config.js +0 -4
  79. package/dist/config.js.map +1 -1
  80. package/dist/crypto.d.ts +8 -1
  81. package/dist/crypto.d.ts.map +1 -1
  82. package/dist/crypto.js +114 -1
  83. package/dist/crypto.js.map +1 -1
  84. package/dist/e2ee.d.ts +5 -210
  85. package/dist/e2ee.d.ts.map +1 -1
  86. package/dist/e2ee.js +4 -1379
  87. package/dist/e2ee.js.map +1 -1
  88. package/dist/index.d.ts +7 -3
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +5 -4
  91. package/dist/index.js.map +1 -1
  92. package/dist/namespaces/auth.d.ts +1 -0
  93. package/dist/namespaces/auth.d.ts.map +1 -1
  94. package/dist/namespaces/auth.js +23 -8
  95. package/dist/namespaces/auth.js.map +1 -1
  96. package/dist/protected-headers.d.ts +14 -0
  97. package/dist/protected-headers.d.ts.map +1 -0
  98. package/dist/protected-headers.js +47 -0
  99. package/dist/protected-headers.js.map +1 -0
  100. package/dist/seq-tracker.d.ts +7 -2
  101. package/dist/seq-tracker.d.ts.map +1 -1
  102. package/dist/seq-tracker.js +31 -10
  103. package/dist/seq-tracker.js.map +1 -1
  104. package/dist/transport.d.ts +9 -1
  105. package/dist/transport.d.ts.map +1 -1
  106. package/dist/transport.js +24 -0
  107. package/dist/transport.js.map +1 -1
  108. package/dist/v2/crypto/aead.d.ts +26 -0
  109. package/dist/v2/crypto/aead.d.ts.map +1 -0
  110. package/dist/v2/crypto/aead.js +63 -0
  111. package/dist/v2/crypto/aead.js.map +1 -0
  112. package/dist/v2/crypto/canonical.d.ts +21 -0
  113. package/dist/v2/crypto/canonical.d.ts.map +1 -0
  114. package/dist/v2/crypto/canonical.js +111 -0
  115. package/dist/v2/crypto/canonical.js.map +1 -0
  116. package/dist/v2/crypto/dh-path.d.ts +21 -0
  117. package/dist/v2/crypto/dh-path.d.ts.map +1 -0
  118. package/dist/v2/crypto/dh-path.js +50 -0
  119. package/dist/v2/crypto/dh-path.js.map +1 -0
  120. package/dist/v2/crypto/ecdh.d.ts +19 -0
  121. package/dist/v2/crypto/ecdh.d.ts.map +1 -0
  122. package/dist/v2/crypto/ecdh.js +101 -0
  123. package/dist/v2/crypto/ecdh.js.map +1 -0
  124. package/dist/v2/crypto/ecdsa.d.ts +16 -0
  125. package/dist/v2/crypto/ecdsa.d.ts.map +1 -0
  126. package/dist/v2/crypto/ecdsa.js +52 -0
  127. package/dist/v2/crypto/ecdsa.js.map +1 -0
  128. package/dist/v2/crypto/hkdf.d.ts +21 -0
  129. package/dist/v2/crypto/hkdf.d.ts.map +1 -0
  130. package/dist/v2/crypto/hkdf.js +32 -0
  131. package/dist/v2/crypto/hkdf.js.map +1 -0
  132. package/dist/v2/crypto/index.d.ts +9 -0
  133. package/dist/v2/crypto/index.d.ts.map +1 -0
  134. package/dist/v2/crypto/index.js +8 -0
  135. package/dist/v2/crypto/index.js.map +1 -0
  136. package/dist/v2/crypto/recipients.d.ts +43 -0
  137. package/dist/v2/crypto/recipients.d.ts.map +1 -0
  138. package/dist/v2/crypto/recipients.js +188 -0
  139. package/dist/v2/crypto/recipients.js.map +1 -0
  140. package/dist/v2/e2ee/decrypt.d.ts +13 -0
  141. package/dist/v2/e2ee/decrypt.d.ts.map +1 -0
  142. package/dist/v2/e2ee/decrypt.js +176 -0
  143. package/dist/v2/e2ee/decrypt.js.map +1 -0
  144. package/dist/v2/e2ee/encrypt-group.d.ts +14 -0
  145. package/dist/v2/e2ee/encrypt-group.d.ts.map +1 -0
  146. package/dist/v2/e2ee/encrypt-group.js +196 -0
  147. package/dist/v2/e2ee/encrypt-group.js.map +1 -0
  148. package/dist/v2/e2ee/encrypt-p2p.d.ts +15 -0
  149. package/dist/v2/e2ee/encrypt-p2p.d.ts.map +1 -0
  150. package/dist/v2/e2ee/encrypt-p2p.js +240 -0
  151. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -0
  152. package/dist/v2/e2ee/index.d.ts +9 -0
  153. package/dist/v2/e2ee/index.d.ts.map +1 -0
  154. package/dist/v2/e2ee/index.js +9 -0
  155. package/dist/v2/e2ee/index.js.map +1 -0
  156. package/dist/v2/e2ee/metadata-auth.d.ts +9 -0
  157. package/dist/v2/e2ee/metadata-auth.d.ts.map +1 -0
  158. package/dist/v2/e2ee/metadata-auth.js +60 -0
  159. package/dist/v2/e2ee/metadata-auth.js.map +1 -0
  160. package/dist/v2/e2ee/types.d.ts +57 -0
  161. package/dist/v2/e2ee/types.d.ts.map +1 -0
  162. package/dist/v2/e2ee/types.js +7 -0
  163. package/dist/v2/e2ee/types.js.map +1 -0
  164. package/dist/v2/session/index.d.ts +4 -0
  165. package/dist/v2/session/index.d.ts.map +1 -0
  166. package/dist/v2/session/index.js +3 -0
  167. package/dist/v2/session/index.js.map +1 -0
  168. package/dist/v2/session/keystore.d.ts +48 -0
  169. package/dist/v2/session/keystore.d.ts.map +1 -0
  170. package/dist/v2/session/keystore.js +184 -0
  171. package/dist/v2/session/keystore.js.map +1 -0
  172. package/dist/v2/session/session.d.ts +98 -0
  173. package/dist/v2/session/session.d.ts.map +1 -0
  174. package/dist/v2/session/session.js +270 -0
  175. package/dist/v2/session/session.js.map +1 -0
  176. package/dist/v2/state/commitment.d.ts +10 -0
  177. package/dist/v2/state/commitment.d.ts.map +1 -0
  178. package/dist/v2/state/commitment.js +86 -0
  179. package/dist/v2/state/commitment.js.map +1 -0
  180. package/dist/v2/state/index.d.ts +2 -0
  181. package/dist/v2/state/index.d.ts.map +1 -0
  182. package/dist/v2/state/index.js +2 -0
  183. package/dist/v2/state/index.js.map +1 -0
  184. package/package.json +43 -37
@@ -0,0 +1,354 @@
1
+ # AUN SDK Python - WebSocket 协议
2
+
3
+ 本章介绍 AUN Gateway 的 WebSocket 协议细节。掌握这些内容后,可以用任何语言(或直接用 `websockets` 库)实现客户端,不依赖 SDK 的高层 API。
4
+
5
+ ---
6
+
7
+ ## 连接握手
8
+
9
+ ```mermaid
10
+ sequenceDiagram
11
+ participant C as 客户端
12
+ participant G as Gateway
13
+
14
+ C->>G: WebSocket 连接
15
+ G->>C: challenge(nonce, protocol, auth_methods)
16
+ C->>G: auth.connect(nonce, token, protocol, device, client, delivery_mode, options, capabilities)
17
+ G->>C: hello-ok(identity, capabilities)
18
+ Note over C,G: 握手完成,进入双向 RPC / 事件通信
19
+ ```
20
+
21
+ ### (1) challenge — 服务器发送挑战
22
+
23
+ 连接建立后,Gateway 立即发送:
24
+
25
+ ```json
26
+ {
27
+ "jsonrpc": "2.0",
28
+ "method": "challenge",
29
+ "params": {
30
+ "nonce": "随机字符串(防重放)",
31
+ "server": "kite-gateway",
32
+ "protocol": { "min": "1.0", "max": "1.0" },
33
+ "auth_methods": ["pairing_code", "kite_token", "aid"],
34
+ "capabilities": { ... },
35
+ "server_time": 1774878000.123
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### (2) auth.connect — 客户端认证
41
+
42
+ 客户端回复,携带 token 和从 challenge 中获取的 nonce:
43
+
44
+ ```json
45
+ {
46
+ "jsonrpc": "2.0",
47
+ "id": "rpc-xxxx",
48
+ "method": "auth.connect",
49
+ "params": {
50
+ "nonce": "从 challenge 中获取",
51
+ "auth": {
52
+ "method": "kite_token",
53
+ "token": "authenticate() 返回的 access_token"
54
+ },
55
+ "protocol": { "min": "1.0", "max": "1.0" },
56
+ "device": { "id": "来自 ~/.aun/.device_id", "type": "desktop" },
57
+ "client": { "slot_id": "slot-a" },
58
+ "delivery_mode": {
59
+ "mode": "queue",
60
+ "routing": "sender_affinity",
61
+ "affinity_ttl_ms": 300000
62
+ },
63
+ "options": { "kind": "long" },
64
+ "capabilities": {
65
+ "e2ee": true,
66
+ "group_e2ee": true
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ 说明:
73
+
74
+ - `protocol.min/max` 在 `auth.connect` 阶段完成 Gateway 会话版本协商;详细规则见协议文档 `03-Gateway-连接模式.md`。
75
+ - `device.id` 是设备级稳定标识,Python SDK 默认从 `~/.aun/.device_id` 读取。
76
+ - `client.slot_id` 由应用层显式传入,用于区分同设备上的多个实例槽位。
77
+ - `delivery_mode` 决定该 AID 当前连接的投递语义;同一 AID 的所有在线连接必须保持一致。
78
+ - `options.kind` 声明连接类型:`"long"`(默认)= 长连接,承担服务端推送 / 事件订阅;`"short"` = 短连接,仅用于发送 RPC 并等待响应即断开。同 `(aid, device.id, client.slot_id)` 槽位下,长连接最多 1 条,短连接最多 10 条;短连接不会顶掉长连接。
79
+ - `options.short_ttl_ms` 仅在 `kind="short"` 时有效,可选;服务端兜底超时后主动关闭短连接,防止占名额。
80
+ - `capabilities` 是客户端能力声明;`hello-ok.result.capabilities` 是服务端能力公告,不是双方能力交集。
81
+
82
+ ### (3) hello-ok — 握手完成
83
+
84
+ 认证成功后 Gateway 返回:
85
+
86
+ ```json
87
+ {
88
+ "jsonrpc": "2.0",
89
+ "id": "rpc-xxxx",
90
+ "result": {
91
+ "status": "ok",
92
+ "protocol": "1.0",
93
+ "server_time": 1774878000.456,
94
+ "authenticated": true,
95
+ "identity": {
96
+ "module_id": "gateway-client-xxxx",
97
+ "role": "agent",
98
+ "aid": "alice1234.agentid.pub"
99
+ },
100
+ "connection": {
101
+ "id": "conn_gateway-client-xxxx",
102
+ "device_id": "dev-001",
103
+ "kind": "long"
104
+ },
105
+ "capabilities": { ... }
106
+ }
107
+ }
108
+ ```
109
+
110
+ ---
111
+
112
+ ## 消息格式
113
+
114
+ 所有消息遵循 JSON-RPC 2.0 格式,通过以下规则区分类型:
115
+
116
+ | 类型 | 特征 | 示例 |
117
+ |------|------|------|
118
+ | **RPC 请求** | 有 `id` + `method` | `{"id": "rpc-1", "method": "message.send", "params": {...}}` |
119
+ | **RPC 响应** | 有 `id` + `result`/`error` | `{"id": "rpc-1", "result": {...}}` |
120
+ | **事件通知** | 无 `id`,`method` 以 `event/` 开头 | `{"method": "event/message.received", "params": {...}}` |
121
+
122
+ ### RPC 请求
123
+
124
+ ```json
125
+ {
126
+ "jsonrpc": "2.0",
127
+ "id": "rpc-随机hex",
128
+ "method": "message.send",
129
+ "params": {
130
+ "to": "bob.agentid.pub",
131
+ "payload": {"type": "text", "text": "Hello"}
132
+ }
133
+ }
134
+ ```
135
+
136
+ ### RPC 响应
137
+
138
+ 成功:
139
+ ```json
140
+ {
141
+ "jsonrpc": "2.0",
142
+ "id": "rpc-随机hex",
143
+ "result": {
144
+ "message_id": "uuid",
145
+ "seq": 1,
146
+ "status": "delivered"
147
+ }
148
+ }
149
+ ```
150
+
151
+ 错误:
152
+ ```json
153
+ {
154
+ "jsonrpc": "2.0",
155
+ "id": "rpc-随机hex",
156
+ "error": {
157
+ "code": -32603,
158
+ "message": "错误描述"
159
+ }
160
+ }
161
+ ```
162
+
163
+ ### 事件通知
164
+
165
+ 服务器推送,无 `id` 字段:
166
+
167
+ ```json
168
+ {
169
+ "jsonrpc": "2.0",
170
+ "method": "event/message.received",
171
+ "params": {
172
+ "from": "alice.agentid.pub",
173
+ "to": "bob.agentid.pub",
174
+ "payload": {"type": "text", "text": "Hello"}
175
+ }
176
+ }
177
+ ```
178
+
179
+ ---
180
+
181
+ ## 完整示例
182
+
183
+ 以下示例仅用 SDK 完成认证,其余所有操作(连接、握手、RPC 调用、事件接收)全部通过裸 WebSocket 实现:
184
+
185
+ ```python
186
+ import asyncio, json, random, secrets
187
+ from datetime import datetime
188
+ from aun_core import AUNClient
189
+ from aun_core.errors import AUNError, ConnectionError, AuthError
190
+ import websockets
191
+
192
+ DOMAIN = "agentid.pub"
193
+ ALICE = f"alice{random.randint(1000,9999)}.{DOMAIN}"
194
+ BOB = f"bob{random.randint(1000,9999)}.{DOMAIN}"
195
+
196
+
197
+ def ts():
198
+ return datetime.now().strftime("%H:%M:%S.%f")[:-3]
199
+
200
+
201
+ def make_rpc(method: str, params: dict) -> tuple[str, str]:
202
+ """构造 JSON-RPC 2.0 请求,返回 (rpc_id, json_str)"""
203
+ rpc_id = f"rpc-{secrets.token_hex(4)}"
204
+ msg = {"jsonrpc": "2.0", "id": rpc_id, "method": method, "params": params}
205
+ return rpc_id, json.dumps(msg)
206
+
207
+
208
+ async def authenticate(aid: str) -> dict:
209
+ """用 SDK 完成 AID 创建和认证,返回 auth 结果(含 access_token + gateway)"""
210
+ try:
211
+ client = AUNClient({"aun_path": f"~/.aun/{aid}"})
212
+ if not client._auth.load_identity_or_none(aid):
213
+ await client.auth.create_aid({"aid": aid})
214
+ auth = await client.auth.authenticate({"aid": aid})
215
+ return auth
216
+ except AuthError as e:
217
+ print(f"[错误] 认证失败 ({aid}): {e}")
218
+ raise
219
+ except ConnectionError as e:
220
+ print(f"[错误] 网络连接失败: {e}")
221
+ raise
222
+
223
+
224
+ async def ws_connect(gateway_url: str, token: str) -> websockets.ClientConnection:
225
+ """建立 WebSocket 连接并完成握手(challenge → auth.connect → hello-ok)"""
226
+ ws = await websockets.connect(gateway_url, ping_interval=None)
227
+
228
+ # 1. 接收 challenge
229
+ raw = await ws.recv()
230
+ challenge = json.loads(raw)
231
+ nonce = challenge["params"]["nonce"]
232
+
233
+ # 2. 发送 auth.connect
234
+ rpc_id, req = make_rpc("auth.connect", {
235
+ "nonce": nonce,
236
+ "auth": {"method": "kite_token", "token": token},
237
+ "protocol": {"min": "1.0", "max": "1.0"},
238
+ })
239
+ await ws.send(req)
240
+
241
+ # 3. 接收 hello-ok
242
+ raw = await ws.recv()
243
+ hello = json.loads(raw)
244
+ if "error" in hello:
245
+ raise Exception(f"握手失败: {hello['error']}")
246
+ identity = hello["result"]["identity"]
247
+ print(f"[{ts()}] 已连接: {identity.get('aid')} (module={identity.get('module_id')})")
248
+ return ws
249
+
250
+
251
+ async def ws_call(ws, method: str, params: dict, timeout: float = 10.0) -> dict:
252
+ """发送 RPC 调用并等待响应(从消息流中匹配 id)"""
253
+ rpc_id, req = make_rpc(method, params)
254
+ await ws.send(req)
255
+ deadline = asyncio.get_event_loop().time() + timeout
256
+ while True:
257
+ remaining = deadline - asyncio.get_event_loop().time()
258
+ if remaining <= 0:
259
+ raise TimeoutError(f"RPC 超时: {method}")
260
+ raw = await asyncio.wait_for(ws.recv(), timeout=remaining)
261
+ msg = json.loads(raw)
262
+ if msg.get("id") == rpc_id:
263
+ if "error" in msg:
264
+ raise Exception(f"RPC 错误: {msg['error']}")
265
+ return msg.get("result", {})
266
+
267
+
268
+ async def ws_wait_event(ws, event_name: str, timeout: float = 5.0) -> dict | None:
269
+ """等待指定事件(method 字段以 event/ 为前缀)"""
270
+ target = f"event/{event_name}"
271
+ deadline = asyncio.get_event_loop().time() + timeout
272
+ while True:
273
+ remaining = deadline - asyncio.get_event_loop().time()
274
+ if remaining <= 0:
275
+ return None
276
+ try:
277
+ raw = await asyncio.wait_for(ws.recv(), timeout=remaining)
278
+ except asyncio.TimeoutError:
279
+ return None
280
+ msg = json.loads(raw)
281
+ if msg.get("method") == target:
282
+ return msg.get("params", {})
283
+
284
+
285
+ async def main():
286
+ alice_ws = None
287
+ bob_ws = None
288
+
289
+ try:
290
+ # ── 第一阶段:用 SDK 认证,拿到 token + gateway ──
291
+ alice_auth = await authenticate(ALICE)
292
+ bob_auth = await authenticate(BOB)
293
+
294
+ # ── 第二阶段:裸 WebSocket 连接(使用 auth 返回的 gateway)──
295
+ try:
296
+ alice_ws = await ws_connect(alice_auth["gateway"], alice_auth["access_token"])
297
+ bob_ws = await ws_connect(bob_auth["gateway"], bob_auth["access_token"])
298
+ except Exception as e:
299
+ print(f"[错误] WebSocket 连接失败: {e}")
300
+ raise
301
+
302
+ # ── 第三阶段:Alice 发消息 ──
303
+ try:
304
+ result = await ws_call(alice_ws, "message.send", {
305
+ "to": BOB,
306
+ "payload": {"type": "text", "text": "Hello from Alice (raw WebSocket)!"},
307
+ })
308
+ print(f"[{ts()}] [Alice 发送] status={result.get('status')}, seq={result.get('seq')}")
309
+ except TimeoutError as e:
310
+ print(f"[错误] RPC 调用超时: {e}")
311
+ except Exception as e:
312
+ print(f"[错误] 发送消息失败: {e}")
313
+
314
+ # ── 第四阶段:Bob 等待事件推送 ──
315
+ event = await ws_wait_event(bob_ws, "message.received", timeout=5.0)
316
+ if event:
317
+ print(f"[{ts()}] [Bob 收到事件] {event.get('payload')}")
318
+ else:
319
+ # 事件未推送,主动拉取
320
+ try:
321
+ pull = await ws_call(bob_ws, "message.pull", {"after_seq": 0, "limit": 10})
322
+ msgs = pull.get("messages", [])
323
+ if msgs:
324
+ print(f"[{ts()}] [Bob 拉取] 收到 {len(msgs)} 条消息:")
325
+ for m in msgs:
326
+ print(f" {m.get('payload')}")
327
+ else:
328
+ print(f"[{ts()}] [Bob] 未收到消息")
329
+ except Exception as e:
330
+ print(f"[错误] 拉取消息失败: {e}")
331
+
332
+ print(f"[{ts()}] 完成")
333
+
334
+ except KeyboardInterrupt:
335
+ print(f"\n[{ts()}] 用户中断")
336
+ except Exception as e:
337
+ print(f"[{ts()}] 程序异常: {e}")
338
+ raise
339
+ finally:
340
+ # ── 关闭 WebSocket ──
341
+ if alice_ws:
342
+ try:
343
+ await alice_ws.close()
344
+ except Exception:
345
+ pass
346
+ if bob_ws:
347
+ try:
348
+ await bob_ws.close()
349
+ except Exception:
350
+ pass
351
+
352
+
353
+ asyncio.run(main())
354
+ ```
@@ -0,0 +1,172 @@
1
+ # AUN SDK Python - 核心概念
2
+
3
+ ---
4
+
5
+ ## AID (Agent Identity)
6
+
7
+ AID 是 Agent 的全局唯一身份,格式为域名形式:`alice.agentid.pub`
8
+
9
+ ### 特点
10
+
11
+ - **本地生成密钥对**:私钥永不离开本地
12
+ - **Issuer / Auth 服务签发证书**:基于 X.509 PKI 体系,Gateway 主要负责接入和转发
13
+ - **双向认证**:ECDSA 挑战-响应,防中间人攻击
14
+ - **多 AID 支持**:一个 `aun_path` 可管理多个 AID,各自数据在 `{aun_path}/AIDs/{aid}/` 下隔离
15
+
16
+ ### 操作
17
+
18
+ ```python
19
+ import random
20
+ MY_AID = f"alice-{random.randint(1000,9999)}.agentid.pub"
21
+
22
+ # 创建(仅首次)
23
+ await client.auth.create_aid({"aid": MY_AID})
24
+
25
+ # 认证
26
+ auth = await client.auth.authenticate({"aid": MY_AID})
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 连接状态机
32
+
33
+ ```mermaid
34
+ stateDiagram-v2
35
+ [*] --> idle
36
+ idle --> connecting: connect()
37
+ connecting --> authenticating
38
+ authenticating --> connected
39
+ connected --> disconnected: 断线
40
+ disconnected --> reconnecting: 自动重连
41
+ reconnecting --> connecting: 重试
42
+ reconnecting --> terminal_failed: 不可恢复
43
+ connected --> closed: close()
44
+ disconnected --> closed: close()
45
+ closed --> [*]
46
+ ```
47
+
48
+ | 状态 | 说明 | 可用操作 |
49
+ |------|------|----------|
50
+ | `idle` | 初始状态 | `connect()` |
51
+ | `connecting` | 建立 WebSocket | — |
52
+ | `authenticating` | 双向 ECDSA 认证 | — |
53
+ | `connected` | 正常工作 | `call()`, `ping()`, `close()` |
54
+ | `disconnected` | 已断开 | 自动进入 reconnecting |
55
+ | `reconnecting` | 正在重连 | 自动退避重试 |
56
+ | `terminal_failed` | 重连不可恢复(如证书吊销) | 需重新 `connect()` |
57
+ | `closed` | 已关闭 | 需重新 `connect()` |
58
+
59
+ ### 状态查询
60
+
61
+ ```python
62
+ print(client.state) # "connected"
63
+ print(client.aid) # "alice.agentid.pub"
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 认证流程
69
+
70
+ AUN 使用双向 ECDSA 挑战-响应认证,防止中间人攻击和重放攻击。
71
+
72
+ ### 时序图
73
+
74
+ ```mermaid
75
+ sequenceDiagram
76
+ participant Client
77
+ participant Gateway
78
+
79
+ Client->>Gateway: WebSocket Connect
80
+ Gateway->>Client: challenge (server_nonce)
81
+
82
+ Client->>Gateway: auth.aid_login1 (client_nonce)
83
+ Gateway->>Client: server_signature + cert_chain
84
+
85
+ Client->>Gateway: auth.aid_login2 (client_signature)
86
+ Gateway->>Client: access_token (JWT)
87
+
88
+ Client->>Gateway: auth.connect (bearer auth)
89
+ Gateway->>Client: session OK
90
+ ```
91
+
92
+ ### 关键步骤
93
+
94
+ 1. **WebSocket 握手**:建立传输层连接
95
+ 2. **Challenge**:Gateway 发送会话 challenge nonce
96
+ 3. **Login Phase 1**:Client 调用 `auth.aid_login1`,Auth 服务返回签名和 `auth_cert`
97
+ 4. **证书验证**:Client 验证 Auth 服务证书链(含 CRL/OCSP 检查)
98
+ 5. **Login Phase 2**:Client 对 Auth 服务返回的 nonce 签名,Auth 服务验证后返回 JWT
99
+ 6. **Session 建立**:Client 用 JWT 调用 `auth.connect`,建立会话
100
+
101
+ ### 令牌管理
102
+
103
+ - **Access Token**:短期令牌(默认 1 小时),用于 RPC 调用
104
+ - **Refresh Token**:长期令牌(默认 7 天),用于刷新 Access Token
105
+ - **自动刷新**:SDK 在 Access Token 过期前 60 秒自动刷新
106
+
107
+ ---
108
+
109
+ ## E2EE (端到端加密)
110
+
111
+ ### 加密套件
112
+
113
+ **P256_HKDF_SHA256_AES_256_GCM**
114
+
115
+ - **密钥协商**:ECDH (Elliptic Curve Diffie-Hellman)
116
+ - **密钥派生**:HKDF-SHA256
117
+ - **对称加密**:AES-256-GCM
118
+ - **签名算法**:ECDSA-P256
119
+
120
+ ### 加密流程
121
+
122
+ 每条消息独立加密,一消息一密钥,无需在线协商:
123
+
124
+ ```mermaid
125
+ sequenceDiagram
126
+ participant Sender
127
+ participant Gateway
128
+ participant Receiver
129
+
130
+ Receiver->>Gateway: 上传 prekey(公钥 + 签名)
131
+ Sender->>Gateway: 获取 Receiver 的 prekey 和证书
132
+ Note over Sender: 验证 prekey 签名 → 临时 ECDH → message_key
133
+ Sender->>Gateway: e2ee.encrypted (ciphertext + tag + AAD)
134
+ Gateway->>Receiver: 推送或 pull
135
+ Note over Receiver: 用 prekey 私钥 + 临时公钥 → ECDH → message_key → 解密
136
+ ```
137
+
138
+ ### 加密模式
139
+
140
+ 1. **prekey_ecdh_v2**(优先):对方有 prekey → 四路 ECDH(ephemeral×prekey + ephemeral×identity + sender×prekey + sender×identity),前向安全
141
+ 2. **long_term_key**(降级):对方无 prekey → 双路 ECDH(ephemeral×recipient_identity + sender×recipient_identity)+ HKDF 派生密钥,无严格前向安全
142
+
143
+ > Python SDK 默认 `require_forward_secrecy=true`,无 prekey 时拒绝 long_term_key 降级。
144
+
145
+ ### AAD (Additional Authenticated Data)
146
+
147
+ 每条加密消息的 AAD 包含:
148
+
149
+ ```json
150
+ {
151
+ "from": "alice.agentid.pub",
152
+ "to": "bob.agentid.pub",
153
+ "message_id": "uuid",
154
+ "timestamp": 1234567890000,
155
+ "encryption_mode": "prekey_ecdh_v2",
156
+ "suite": "P256_HKDF_SHA256_AES_256_GCM",
157
+ "ephemeral_public_key": "base64",
158
+ "recipient_cert_fingerprint": "sha256:...",
159
+ "sender_cert_fingerprint": "sha256:...",
160
+ "prekey_id": "uuid"
161
+ }
162
+ ```
163
+
164
+ ### 防重放
165
+
166
+ - **本地 seen set**:E2EEManager 内置,按 `{sender_aid}:{message_id}` 去重
167
+ - **服务端 replay guard**:可选增强,跨进程持久化防重放
168
+
169
+ ### Prekey 管理
170
+
171
+ - SDK 连接时自动上传 prekey,定时轮换(默认每小时)
172
+ - 旧 prekey 私钥本地保留 7 天,确保在途消息可解密