@agentunion/fastaun-browser 0.2.19 → 0.2.20
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 +26 -0
- package/_packed_docs/CHANGELOG.md +26 -0
- package/_packed_docs/agent.md/SCHEMA.md +173 -0
- package/_packed_docs/agent.md/examples/codeagent-claudecode.md +61 -0
- package/_packed_docs/agent.md/examples/human-developer.md +60 -0
- package/_packed_docs/agent.md/examples/openclaw-lobster.md +52 -0
- package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +43 -0
- package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +205 -0
- 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
- 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
- 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
- package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +262 -0
- package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +180 -0
- package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +164 -0
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1135 -0
- 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
- package/_packed_docs/protocol/08-AUN-E2EE-Group.md +900 -0
- package/_packed_docs/protocol/08-AUN-E2EE.md +413 -0
- package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +316 -0
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +804 -0
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +271 -0
- package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +329 -0
- package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +141 -0
- 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
- package/_packed_docs/protocol/README.md +71 -0
- package/_packed_docs/protocol/agent.md/SCHEMA.md +118 -0
- package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +61 -0
- package/_packed_docs/protocol/agent.md/examples/human-developer.md +60 -0
- package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +52 -0
- package/_packed_docs/protocol/aun-docs-guide.md +49 -0
- package/_packed_docs/protocol/index.md +114 -0
- 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
- 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
- package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +337 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +223 -0
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +354 -0
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +172 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +373 -0
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +611 -0
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +1152 -0
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +150 -0
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +89 -0
- package/_packed_docs/sdk/09-custody-api-manual.md +445 -0
- package/_packed_docs/sdk/09-group-rpc-manual.md +1895 -0
- package/_packed_docs/sdk/09-message-rpc-manual.md +597 -0
- package/_packed_docs/sdk/09-meta-rpc-manual.md +142 -0
- package/_packed_docs/sdk/09-payload-reference.md +702 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +408 -0
- package/_packed_docs/sdk/09-stream-rpc-manual.md +275 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +72 -0
- package/_packed_docs/sdk/INDEX.md +131 -0
- package/_packed_docs/sdk/README.md +307 -0
- package/dist/auth.d.ts +2 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +13 -11
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +38 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +179 -97
- package/dist/client.js.map +1 -1
- package/dist/namespaces/auth.d.ts +1 -0
- package/dist/namespaces/auth.d.ts.map +1 -1
- package/dist/namespaces/auth.js +20 -6
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/transport.d.ts +9 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +24 -0
- package/dist/transport.js.map +1 -1
- package/package.json +40 -37
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# 11. Storage 子协议
|
|
2
|
+
|
|
3
|
+
> **适用版本**:AUN 1.0 | **状态**:Draft
|
|
4
|
+
|
|
5
|
+
Storage 服务是 AUN 协议的应用层扩展,提供对象存储能力。负责大文件、附件、二进制数据的上传、下载与持久保存。客户端通过 `storage.*` 命名空间方法访问,小对象(≤64KB)通过 RPC 内联传输,大对象通过预签名 URL 直接 HTTP 传输。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 11.1 设计原则
|
|
10
|
+
|
|
11
|
+
- **控制面与数据面分离**:小对象内联 RPC;大对象通过 `create_upload_session` / `create_download_ticket` 获取预签名 URL,走 HTTP 数据面
|
|
12
|
+
- **per-AID 隔离**:每个 AID 拥有独立的存储命名空间和配额,跨 AID 访问受 `is_private` 控制
|
|
13
|
+
- **对象键路径化**:`object_key` 支持 `/` 分隔的层级路径,便于组织和前缀查询
|
|
14
|
+
- **版本化**:每次写入递增 `version`,支持 `expected_version` 做 CAS(Compare-And-Swap)并发控制
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 11.2 约束与限制
|
|
19
|
+
|
|
20
|
+
| 约束 | 默认值 | 说明 |
|
|
21
|
+
|------|--------|------|
|
|
22
|
+
| 内联内容上限 | 64 KB | `put_object` / `get_object` 的 base64 内容上限 |
|
|
23
|
+
| 单对象上限 | 10 MB | 含数据面上传 |
|
|
24
|
+
| 对象键长度 | ≤ 1024 字节 | 不含 `..`,不含前导/尾随/连续 `/` |
|
|
25
|
+
| 列表分页上限 | 200 条/页 | `list_objects` 单次最大返回 |
|
|
26
|
+
| 预签名 URL 有效期 | 3600 秒 | upload / download ticket |
|
|
27
|
+
|
|
28
|
+
对象键合法字符:字母、数字、`.`、`_`、`/`、`-`。
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 11.3 控制面方法
|
|
33
|
+
|
|
34
|
+
### `storage.put_object`
|
|
35
|
+
|
|
36
|
+
内联写入小对象(≤ 内联上限)。
|
|
37
|
+
|
|
38
|
+
**参数**:
|
|
39
|
+
|
|
40
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
41
|
+
|------|------|:----:|------|
|
|
42
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
43
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
44
|
+
| `object_key` | string | ✅ | 对象路径 |
|
|
45
|
+
| `content` | string | ✅ | base64 编码的内容 |
|
|
46
|
+
| `content_type` | string | ❌ | MIME 类型,默认 `application/octet-stream` |
|
|
47
|
+
| `is_private` | boolean | ❌ | 默认 `true`,公开对象可被其他 AID 读取 |
|
|
48
|
+
| `overwrite` | boolean | ❌ | 默认 `true`,`false` 时对象已存在则拒绝 |
|
|
49
|
+
| `expected_version` | integer | ❌ | CAS 并发控制,版本不匹配则拒绝 |
|
|
50
|
+
| `expire_in_seconds` | integer | ❌ | 过期时间,`0` 表示永不过期 |
|
|
51
|
+
| `metadata` | object | ❌ | 用户自定义元数据 |
|
|
52
|
+
|
|
53
|
+
**响应**:`{ owner_aid, bucket, object_key, size_bytes, content_type, sha256, version, etag, updated_at }`
|
|
54
|
+
|
|
55
|
+
**权限**:仅对象所有者可写(`requester_aid == owner_aid`)。
|
|
56
|
+
|
|
57
|
+
**副作用**:成功后发布 `event/storage.object_changed`(`action: "put"`)。
|
|
58
|
+
|
|
59
|
+
### `storage.get_object`
|
|
60
|
+
|
|
61
|
+
内联读取小对象。对象超过内联上限时须使用 `create_download_ticket`。
|
|
62
|
+
|
|
63
|
+
**参数**:
|
|
64
|
+
|
|
65
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
66
|
+
|------|------|:----:|------|
|
|
67
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
68
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
69
|
+
| `object_key` | string | ✅ | 对象路径 |
|
|
70
|
+
|
|
71
|
+
**响应**:`{ owner_aid, bucket, object_key, content (base64), content_type, size_bytes, sha256, version, updated_at }`
|
|
72
|
+
|
|
73
|
+
**权限**:私有对象仅所有者可读;公开对象(`is_private=false`)任何 AID 可读。
|
|
74
|
+
|
|
75
|
+
### `storage.head_object`
|
|
76
|
+
|
|
77
|
+
查询对象元数据,不返回内容。
|
|
78
|
+
|
|
79
|
+
**参数**:同 `get_object`。
|
|
80
|
+
|
|
81
|
+
**响应**:`{ owner_aid, bucket, object_key, content_type, size_bytes, sha256, version, etag, is_private, expire_at, created_at, updated_at }`
|
|
82
|
+
|
|
83
|
+
**权限**:同 `get_object`。
|
|
84
|
+
|
|
85
|
+
### `storage.delete_object`
|
|
86
|
+
|
|
87
|
+
删除单个对象。
|
|
88
|
+
|
|
89
|
+
**参数**:同 `get_object`。
|
|
90
|
+
|
|
91
|
+
**响应**:`{ deleted (bool), owner_aid, object_key }`
|
|
92
|
+
|
|
93
|
+
**权限**:仅所有者可删。
|
|
94
|
+
|
|
95
|
+
**副作用**:成功后发布 `event/storage.object_changed`(`action: "delete"`)。
|
|
96
|
+
|
|
97
|
+
### `storage.list_objects`
|
|
98
|
+
|
|
99
|
+
列出对象,支持前缀过滤和分页。
|
|
100
|
+
|
|
101
|
+
**参数**:
|
|
102
|
+
|
|
103
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
104
|
+
|------|------|:----:|------|
|
|
105
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
106
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
107
|
+
| `prefix` | string | ❌ | 路径前缀过滤 |
|
|
108
|
+
| `page` | integer | ❌ | 页码,默认 1 |
|
|
109
|
+
| `size` | integer | ❌ | 每页条数,默认 50,最大 200 |
|
|
110
|
+
| `marker` | string | ❌ | 深度分页游标;传入时优先按游标分页 |
|
|
111
|
+
|
|
112
|
+
**响应**:`{ items: [{ object_key, size_bytes, content_type, sha256, version, is_private, updated_at }...], total, page, size, marker, next_marker }`
|
|
113
|
+
|
|
114
|
+
**权限**:仅所有者可列。
|
|
115
|
+
|
|
116
|
+
### `storage.list_prefixes`
|
|
117
|
+
|
|
118
|
+
列出指定前缀下的子目录(类似 S3 CommonPrefixes)。
|
|
119
|
+
|
|
120
|
+
**参数**:
|
|
121
|
+
|
|
122
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
123
|
+
|------|------|:----:|------|
|
|
124
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
125
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
126
|
+
| `prefix` | string | ❌ | 路径前缀 |
|
|
127
|
+
| `size` | integer | ❌ | 返回上限,默认 50,最大 200 |
|
|
128
|
+
|
|
129
|
+
**响应**:`{ prefixes: [string...], count, size }`
|
|
130
|
+
|
|
131
|
+
**权限**:仅所有者可列。
|
|
132
|
+
|
|
133
|
+
### `storage.get_quota`
|
|
134
|
+
|
|
135
|
+
查询存储配额使用情况。
|
|
136
|
+
|
|
137
|
+
**参数**:
|
|
138
|
+
|
|
139
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
140
|
+
|------|------|:----:|------|
|
|
141
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
142
|
+
|
|
143
|
+
**响应**:`{ owner_aid, used_bytes, object_count, quota_bytes }`
|
|
144
|
+
|
|
145
|
+
**权限**:仅所有者可查。
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 11.4 数据面方法
|
|
150
|
+
|
|
151
|
+
用于超过内联上限的大对象传输。客户端通过 RPC 获取预签名 URL,然后直接 HTTP PUT/GET。
|
|
152
|
+
|
|
153
|
+
### `storage.create_upload_session`
|
|
154
|
+
|
|
155
|
+
申请上传预签名 URL。
|
|
156
|
+
|
|
157
|
+
**参数**:
|
|
158
|
+
|
|
159
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
160
|
+
|------|------|:----:|------|
|
|
161
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
162
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
163
|
+
| `object_key` | string | ✅ | 对象路径 |
|
|
164
|
+
| `size_bytes` | integer | ❌ | 预期大小(客户端追踪用) |
|
|
165
|
+
| `content_type` | string | ❌ | 默认 `application/octet-stream` |
|
|
166
|
+
| `expected_version` | integer | ❌ | CAS 版本检查(签 URL 前校验) |
|
|
167
|
+
| `expire_in_seconds` | integer | ❌ | URL 有效期,默认 3600 秒 |
|
|
168
|
+
|
|
169
|
+
**响应**:`{ upload_url, blob_key, expire_at, owner_aid, bucket, object_key, content_type, size_bytes }`
|
|
170
|
+
|
|
171
|
+
**权限**:仅所有者可申请。
|
|
172
|
+
|
|
173
|
+
**流程**:客户端获取 `upload_url` 后,直接 HTTP PUT 上传文件内容,完成后调用 `complete_upload` 确认。
|
|
174
|
+
|
|
175
|
+
**当前实现补充**:`create_upload_session` 签发 URL 时不做配额与最终文件大小的强校验;配额、`max_file_size_bytes`、SHA-256/size 校验在 `complete_upload` 阶段执行。
|
|
176
|
+
|
|
177
|
+
### `storage.complete_upload`
|
|
178
|
+
|
|
179
|
+
确认上传完成,将 blob 关联为正式对象。
|
|
180
|
+
|
|
181
|
+
**参数**:
|
|
182
|
+
|
|
183
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
184
|
+
|------|------|:----:|------|
|
|
185
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
186
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
187
|
+
| `object_key` | string | ✅ | 对象路径 |
|
|
188
|
+
| `content_type` | string | ❌ | 默认 `application/octet-stream` |
|
|
189
|
+
| `is_private` | boolean | ❌ | 默认 `true` |
|
|
190
|
+
| `sha256` | string | ❌ | 期望哈希,服务端校验 |
|
|
191
|
+
| `size_bytes` | integer | ❌ | 期望大小,服务端校验 |
|
|
192
|
+
| `expire_in_seconds` | integer | ❌ | 过期时间,默认永不过期 |
|
|
193
|
+
| `metadata` | object | ❌ | 用户自定义元数据 |
|
|
194
|
+
| `expected_version` | integer | ❌ | CAS 并发控制 |
|
|
195
|
+
|
|
196
|
+
**响应**:`{ owner_aid, bucket, object_key, size_bytes, content_type, sha256, version, etag, updated_at }`
|
|
197
|
+
|
|
198
|
+
**权限**:仅所有者可确认。
|
|
199
|
+
|
|
200
|
+
### `storage.create_download_ticket`
|
|
201
|
+
|
|
202
|
+
申请下载预签名 URL。
|
|
203
|
+
|
|
204
|
+
**参数**:
|
|
205
|
+
|
|
206
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
207
|
+
|------|------|:----:|------|
|
|
208
|
+
| `owner_aid` | string | ❌ | 默认调用者 AID |
|
|
209
|
+
| `bucket` | string | ❌ | 默认 `"default"` |
|
|
210
|
+
| `object_key` | string | ✅ | 对象路径 |
|
|
211
|
+
| `expire_in_seconds` | integer | ❌ | URL 有效期,默认 3600 秒 |
|
|
212
|
+
|
|
213
|
+
**响应**:`{ download_url, expire_at, file_name, size_bytes, content_type, sha256, version, etag }`
|
|
214
|
+
|
|
215
|
+
**权限**:私有对象仅所有者;公开对象任何 AID 可申请。
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 11.6 事件
|
|
220
|
+
|
|
221
|
+
### `event/storage.object_changed`
|
|
222
|
+
|
|
223
|
+
对象变更时推送给所有者。
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"jsonrpc": "2.0",
|
|
228
|
+
"method": "event/storage.object_changed",
|
|
229
|
+
"params": {
|
|
230
|
+
"module_id": "storage",
|
|
231
|
+
"action": "put",
|
|
232
|
+
"owner_aid": "alice.aid.pub",
|
|
233
|
+
"object_key": "docs/report.pdf"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
| 字段 | 类型 | 说明 |
|
|
239
|
+
|------|------|------|
|
|
240
|
+
| `action` | string | `"put"` 或 `"delete"` |
|
|
241
|
+
| `owner_aid` | string | 对象所有者 AID |
|
|
242
|
+
| `object_key` | string | 变更的对象路径 |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 11.7 错误码
|
|
247
|
+
|
|
248
|
+
| 错误码 | 含义 | 说明 |
|
|
249
|
+
|--------|------|------|
|
|
250
|
+
| -32008 | Object not found | 对象不存在 |
|
|
251
|
+
| -32009 | Version conflict | CAS 版本不匹配 |
|
|
252
|
+
| -32004 | Permission denied | 非所有者访问私有对象 |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 11.8 大对象上传流程
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
客户端 Storage 服务 HTTP 数据面
|
|
260
|
+
│ │ │
|
|
261
|
+
│─ storage.create_upload_session ►│ │
|
|
262
|
+
│◄─ { upload_url, blob_key } ───│ │
|
|
263
|
+
│ │ │
|
|
264
|
+
│─────────── HTTP PUT upload_url ─────────────────────────────► │
|
|
265
|
+
│◄──────────────── 200 OK ─────────────────────────────────────│
|
|
266
|
+
│ │ │
|
|
267
|
+
│─ storage.complete_upload ─────►│ │
|
|
268
|
+
│◄─ { object_key, sha256, ... } ─│ │
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
大对象下载流程类似:`create_download_ticket` 获取 `download_url`,客户端直接 HTTP GET。
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# 12. Stream 子协议
|
|
2
|
+
|
|
3
|
+
> **适用版本**:AUN 1.0 | **状态**:Draft
|
|
4
|
+
|
|
5
|
+
Stream 服务是 AUN 协议的应用层扩展,提供实时流式数据传输能力。典型场景包括 LLM 逐字输出、实时数据推送、JSON 对象流等。Stream 服务采用控制面与数据面分离架构:控制面通过 `stream.*` JSON-RPC 方法管理流的生命周期,数据面通过独立端口的 WebSocket(推流)和 HTTP SSE(拉流)传输数据。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 12.1 设计原则
|
|
10
|
+
|
|
11
|
+
- **控制面与数据面分离**:`stream.*` RPC 方法管理流的创建、关闭和查询;实际数据通过独立的 WebSocket / HTTP SSE 端点传输
|
|
12
|
+
- **能力 URL 鉴权**:当前实现中,推流和拉流都以 token 作为主要能力凭证;`target_aid` 仅对拉流侧提供可选附加约束
|
|
13
|
+
- **URL 即凭证**:`stream.create` 返回的 push_url / pull_url 内含 token,持有 URL 即可操作流,便于通过 `message.send` 传递给接收方
|
|
14
|
+
- **无协商流程**:不需要 accept/reject,创建即可推流,适合 LLM 等单向输出场景
|
|
15
|
+
- **内存缓冲 + 实时广播**:推送的数据帧先写入内存缓冲区,同时广播到所有在线拉流端;后加入的拉流端先回放缓冲区再接收实时数据
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 12.2 架构与角色
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
sequenceDiagram
|
|
23
|
+
participant Pusher as 推流方 (本域 AID)
|
|
24
|
+
participant Gateway as Gateway
|
|
25
|
+
participant Stream as Stream 服务
|
|
26
|
+
participant Puller as 拉流方 (可跨域)
|
|
27
|
+
|
|
28
|
+
Pusher->>Gateway: 1. stream.create (RPC)
|
|
29
|
+
Gateway->>Stream: 转发
|
|
30
|
+
Stream-->>Gateway: {push_url, pull_url}
|
|
31
|
+
Gateway-->>Pusher: 返回 URL
|
|
32
|
+
|
|
33
|
+
Pusher->>Puller: 2. message.send(pull_url)
|
|
34
|
+
|
|
35
|
+
Pusher->>Stream: 3. WebSocket 推流<br/>stream.{issuer}:9490/push/xxx
|
|
36
|
+
Puller->>Stream: 4. HTTP SSE 拉流<br/>stream.{issuer}:9490/pull/xxx
|
|
37
|
+
|
|
38
|
+
Pusher->>Stream: 5. cmd: close
|
|
39
|
+
Stream-->>Puller: event: done
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- **Stream 服务**:独立模块,控制面通过 JSON-RPC 2.0 提供流管理方法,数据面监听独立端口(默认 9490)
|
|
43
|
+
- **推流方**:创建流的 AID,通过 WebSocket 连接 push_url 发送数据帧
|
|
44
|
+
- **拉流方**:持有 pull_url 的任意客户端,通过 HTTP SSE 接收数据
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 12.3 约束与限制
|
|
49
|
+
|
|
50
|
+
| 约束 | 默认值 | 说明 |
|
|
51
|
+
|------|--------|------|
|
|
52
|
+
| 最大并发流数 | 200 | 服务全局活跃流上限 |
|
|
53
|
+
| 单帧大小 | 无显式限制 | 受 WebSocket 帧上限约束(64 MB) |
|
|
54
|
+
| 流空闲超时 | 300 秒 | 无推送也无拉取时自动关闭 |
|
|
55
|
+
| 推流端离线超时 | 120 秒 | 推流 WebSocket 断开后等待重连 |
|
|
56
|
+
| SSE 心跳间隔 | 10 秒 | 无数据时发送 `: keep-alive` 注释 |
|
|
57
|
+
| 单流最大拉流端 | 10 | 同一流的并发拉流连接数 |
|
|
58
|
+
| push_token / pull_token | 32 字节 hex | 随机生成,流关闭即失效 |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 12.4 数据模型
|
|
63
|
+
|
|
64
|
+
### StreamSession 对象
|
|
65
|
+
|
|
66
|
+
| 字段 | 类型 | 说明 |
|
|
67
|
+
|------|------|------|
|
|
68
|
+
| `stream_id` | string | 流唯一 ID(16 位 hex) |
|
|
69
|
+
| `creator_aid` | string | 创建者 AID |
|
|
70
|
+
| `content_type` | string | 内容类型(如 `text/plain`、`application/json-stream`) |
|
|
71
|
+
| `metadata` | object | 自定义元数据 |
|
|
72
|
+
| `status` | string | `"waiting"` / `"active"` / `"done"` |
|
|
73
|
+
| `is_online` | boolean | 推流端是否在线 |
|
|
74
|
+
| `seq` | integer | 当前最大序列号 |
|
|
75
|
+
| `frames_pushed` | integer | 已推送帧数 |
|
|
76
|
+
| `bytes_pushed` | integer | 已推送字节数 |
|
|
77
|
+
| `puller_count` | integer | 当前拉流端数量 |
|
|
78
|
+
| `age_seconds` | float | 流存活时间 |
|
|
79
|
+
| `idle_seconds` | float | 最近一次活动距今时间 |
|
|
80
|
+
|
|
81
|
+
### 流状态说明
|
|
82
|
+
|
|
83
|
+
| 状态 | 含义 |
|
|
84
|
+
|------|------|
|
|
85
|
+
| `waiting` | 已创建,推流端尚未连接 |
|
|
86
|
+
| `active` | 推流端在线,正在传输数据 |
|
|
87
|
+
| `done` | 流已关闭(推流方主动关闭或超时) |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 12.5 控制面方法(JSON-RPC 2.0,通过 Gateway)
|
|
92
|
+
|
|
93
|
+
### `stream.create`
|
|
94
|
+
|
|
95
|
+
创建一条新的流,返回推流和拉流 URL。
|
|
96
|
+
|
|
97
|
+
**参数**:
|
|
98
|
+
|
|
99
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
100
|
+
|------|------|:----:|------|
|
|
101
|
+
| `content_type` | string | ❌ | 内容类型,默认 `"text/plain"` |
|
|
102
|
+
| `metadata` | object | ❌ | 自定义元数据 |
|
|
103
|
+
| `target_aid` | string | ❌ | 绑定拉流方 AID。当前实现中,只有拉流方显式提供 `aid` 时才会做该匹配校验 |
|
|
104
|
+
|
|
105
|
+
**返回**:
|
|
106
|
+
|
|
107
|
+
| 字段 | 类型 | 说明 |
|
|
108
|
+
|------|------|------|
|
|
109
|
+
| `stream_id` | string | 流 ID |
|
|
110
|
+
| `push_url` | string | 推流 WebSocket URL(含 push_token) |
|
|
111
|
+
| `pull_url` | string | 拉流 HTTP SSE URL(含 pull_token) |
|
|
112
|
+
| `push_token` | string | 推流凭证 |
|
|
113
|
+
| `pull_token` | string | 拉流凭证(便于单独传递) |
|
|
114
|
+
| `push_headers` | object | 推流推荐使用的 Header(`{"Authorization": "Bearer {push_token}"}`) |
|
|
115
|
+
| `pull_headers` | object | 拉流推荐使用的 Header(`{"Authorization": "Bearer {pull_token}"}`) |
|
|
116
|
+
|
|
117
|
+
**示例**:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
// 请求
|
|
121
|
+
{"jsonrpc":"2.0","id":"1","method":"stream.create","params":{"content_type":"text/plain"}}
|
|
122
|
+
|
|
123
|
+
// 响应
|
|
124
|
+
{
|
|
125
|
+
"jsonrpc":"2.0","id":"1",
|
|
126
|
+
"result":{
|
|
127
|
+
"stream_id":"4d5067f203cf42ba",
|
|
128
|
+
"push_url":"wss://stream.aid.com:9490/push/4d5067f203cf42ba?token=ec80...",
|
|
129
|
+
"pull_url":"https://stream.aid.com:9490/pull/4d5067f203cf42ba?token=c438...",
|
|
130
|
+
"push_token":"ec80...",
|
|
131
|
+
"pull_token":"c438953be0ca887b...",
|
|
132
|
+
"push_headers":{"Authorization":"Bearer ec80..."},
|
|
133
|
+
"pull_headers":{"Authorization":"Bearer c438953be0ca887b..."}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### `stream.close`
|
|
141
|
+
|
|
142
|
+
关闭流。仅流的创建者可调用。关闭后所有拉流端收到 `event: done`。
|
|
143
|
+
|
|
144
|
+
**参数**:
|
|
145
|
+
|
|
146
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
147
|
+
|------|------|:----:|------|
|
|
148
|
+
| `stream_id` | string | ✅ | 流 ID |
|
|
149
|
+
|
|
150
|
+
**返回**:`{ "success": true }`
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### `stream.get_info`
|
|
155
|
+
|
|
156
|
+
获取流的状态和统计信息。
|
|
157
|
+
|
|
158
|
+
**参数**:
|
|
159
|
+
|
|
160
|
+
| 参数 | 类型 | 必需 | 说明 |
|
|
161
|
+
|------|------|:----:|------|
|
|
162
|
+
| `stream_id` | string | ✅ | 流 ID |
|
|
163
|
+
|
|
164
|
+
**返回**:StreamSession 对象(见 12.4)。
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### `stream.list_active`
|
|
169
|
+
|
|
170
|
+
列出当前 AID 创建的所有活跃流。
|
|
171
|
+
|
|
172
|
+
**参数**:无。
|
|
173
|
+
|
|
174
|
+
**返回**:`{ "streams": [StreamSession, ...] }`
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 12.6 数据平面(独立端口)
|
|
179
|
+
|
|
180
|
+
Stream 服务在独立端口(默认 9490)上监听,提供推流和拉流端点。
|
|
181
|
+
|
|
182
|
+
### 推流端点
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
GET /push/{stream_id}?token={push_token} → WebSocket 升级
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**认证**:当前实现优先读取 `Authorization: Bearer {push_token}`,回退兼容 `?token={push_token}` 查询参数。
|
|
189
|
+
|
|
190
|
+
**WebSocket 帧格式**(客户端 → 服务端):
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{"cmd": "data", "data": "chunk内容", "seq": 1}
|
|
194
|
+
{"cmd": "data", "data": "chunk内容", "seq": 2}
|
|
195
|
+
{"cmd": "close"}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
| 字段 | 类型 | 必需 | 说明 |
|
|
199
|
+
|------|------|:----:|------|
|
|
200
|
+
| `cmd` | string | ✅ | `"data"` 或 `"close"` |
|
|
201
|
+
| `data` | string | `data`时✅ | 数据内容,无大小限制(受 WS 帧 64MB 上限约束) |
|
|
202
|
+
| `seq` | integer | ❌ | 序列号,不提供则服务端自增 |
|
|
203
|
+
|
|
204
|
+
**关闭流**:发送 `{"cmd": "close"}` 后服务端关闭流并通知所有拉流端。
|
|
205
|
+
|
|
206
|
+
**断线重连**:推流 WebSocket 断开后,服务端保留流状态最多 120 秒(`pusher_offline_timeout`),期间重连可继续推送。
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### 拉流端点
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
GET /pull/{stream_id}?token={pull_token}&aid={puller_aid} → HTTP SSE
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**认证**:
|
|
217
|
+
- `token`:当前实现优先读取 `Authorization: Bearer {pull_token}`,回退兼容 `?token={pull_token}`
|
|
218
|
+
- `aid`:当前实现优先读取 `X-Stream-AID` 请求头,回退兼容 `?aid={puller_aid}`
|
|
219
|
+
- `target_aid`:若流配置了 `target_aid` 且请求方显式提供了 `aid`,二者必须匹配;未提供 `aid` 时当前实现仍以 `pull_token` 为准
|
|
220
|
+
|
|
221
|
+
**SSE 响应格式**:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
HTTP/1.1 200 OK
|
|
225
|
+
Content-Type: text/event-stream
|
|
226
|
+
Cache-Control: no-cache
|
|
227
|
+
Connection: keep-alive
|
|
228
|
+
|
|
229
|
+
data: Hello
|
|
230
|
+
|
|
231
|
+
data: World
|
|
232
|
+
|
|
233
|
+
event: done
|
|
234
|
+
data: {}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- `data:` 字段是推流方发送的原始数据内容
|
|
238
|
+
- 流结束时发送 `event: done`
|
|
239
|
+
- 无数据期间每 10 秒发送 `: keep-alive` 注释(SSE 标准心跳)
|
|
240
|
+
|
|
241
|
+
**断线续拉**:当前实现支持标准 SSE `Last-Event-ID`。服务端在每个 SSE 数据块中输出 `id: {seq}`,客户端重连时可携带 `Last-Event-ID`,服务端会跳过 `seq <= Last-Event-ID` 的缓冲数据后继续推送。
|
|
242
|
+
|
|
243
|
+
**当前实现补充**:历史仅保留在内存缓冲中。早于当前缓冲区最小 seq 的缺口不会返回显式 gap 错误,服务端会从仍保留的最早帧继续推送。
|
|
244
|
+
|
|
245
|
+
**Late Joiner**:拉流端连接时,先回放缓冲区中的历史数据,再切换为实时接收。
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
### 健康检查
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
GET /health → JSON
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
返回:`{"status": "healthy", "active_streams": 0, "uptime_seconds": 123}`
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 12.7 典型流程
|
|
260
|
+
|
|
261
|
+
### 流程一:LLM 流式输出
|
|
262
|
+
|
|
263
|
+
```mermaid
|
|
264
|
+
sequenceDiagram
|
|
265
|
+
participant Alice as Alice (aid.com)
|
|
266
|
+
participant Stream as Stream Service
|
|
267
|
+
participant Bob as Bob (aid.net)
|
|
268
|
+
|
|
269
|
+
Alice->>Stream: stream.create
|
|
270
|
+
Stream-->>Alice: {push_url, pull_url}
|
|
271
|
+
|
|
272
|
+
Alice->>Bob: message.send(pull_url)
|
|
273
|
+
|
|
274
|
+
Alice->>Stream: WS /push/xxx (data frames)
|
|
275
|
+
Bob->>Stream: SSE /pull/xxx
|
|
276
|
+
Stream-->>Bob: data:Hello
|
|
277
|
+
Stream-->>Bob: data:World
|
|
278
|
+
|
|
279
|
+
Alice->>Stream: WS cmd:close
|
|
280
|
+
Stream-->>Bob: event:done
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 流程二:Late Joiner 回放
|
|
284
|
+
|
|
285
|
+
```mermaid
|
|
286
|
+
sequenceDiagram
|
|
287
|
+
participant Alice
|
|
288
|
+
participant Stream as Stream Service
|
|
289
|
+
participant Bob
|
|
290
|
+
|
|
291
|
+
Alice->>Stream: push seq:1 "A"
|
|
292
|
+
Alice->>Stream: push seq:2 "B"
|
|
293
|
+
Note over Stream: buffer: [1:A, 2:B]
|
|
294
|
+
|
|
295
|
+
Bob->>Stream: SSE /pull/xxx
|
|
296
|
+
Stream-->>Bob: 回放 data:A
|
|
297
|
+
Stream-->>Bob: 回放 data:B
|
|
298
|
+
|
|
299
|
+
Alice->>Stream: push seq:3 "C"
|
|
300
|
+
Stream-->>Bob: 实时 data:C
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## 12.8 安全考量
|
|
306
|
+
|
|
307
|
+
1. **推流域限制**:push_token 在 `stream.create` 时生成,仅返回给创建者(本域已认证 AID),外域无法获取
|
|
308
|
+
2. **拉流 token 鉴权**:pull_token 为 32 字节随机 hex,持有即可拉流;`target_aid` 在当前实现里是附加校验,而不是替代 token 的强绑定
|
|
309
|
+
3. **URL 传递安全**:pull_url 应通过 E2EE 加密的 `message.send` 传递,避免 token 泄露;新客户端优先使用 `push_headers` / `pull_headers`,减少 token 出现在日志、Referer 和浏览器历史中的机会
|
|
310
|
+
4. **传输加密**:数据平面支持 TLS(wss:// / https://),生产环境必须启用
|
|
311
|
+
5. **资源保护**:单流最大 10 个拉流端,全局最大 200 条活跃流,空闲自动关闭
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## 12.9 错误码
|
|
316
|
+
|
|
317
|
+
| code | message | 说明 |
|
|
318
|
+
|------|---------|------|
|
|
319
|
+
| -33401 | Stream not found | stream_id 无效或流已被清理 |
|
|
320
|
+
| -33402 | Stream limit exceeded | 活跃流数超过上限 |
|
|
321
|
+
| -33403 | Stream permission denied | 非创建者执行受限操作 |
|
|
322
|
+
| -33404 | Stream already closed | 流已关闭 |
|
|
323
|
+
| -33405 | Stream invalid params | 参数无效(如缺少 stream_id) |
|
|
324
|
+
| -33406 | Stream rate limited | 速率限制 |
|
|
325
|
+
| -33407 | Stream internal error | 服务内部错误 |
|
|
326
|
+
| HTTP 403 | — | push/pull token 无效,或请求方显式提供的 `aid` 与 `target_aid` 不匹配 |
|
|
327
|
+
| HTTP 404 | — | 数据平面找不到流 |
|
|
328
|
+
| HTTP 410 | — | 流已关闭 |
|
|
329
|
+
| HTTP 429 | — | 拉流端数量已达上限 |
|