@agentunion/fastaun-browser 0.5.19 → 0.5.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +129 -78
  2. package/_packed_docs/CHANGELOG.md +129 -78
  3. package/_packed_docs/INDEX.md +201 -195
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +93 -90
  5. package/_packed_docs/agent.md/SCHEMA.md +7 -7
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
  7. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
  8. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +676 -676
  9. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +8 -12
  10. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
  11. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  12. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
  13. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  14. package/_packed_docs/protocol/agent.md/SCHEMA.md +1 -1
  15. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
  16. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +67 -63
  17. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  18. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +35 -8
  19. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  20. package/_packed_docs/sdk/09-group-rpc-manual.md +258 -254
  21. package/_packed_docs/sdk/09-message-rpc-manual.md +7 -4
  22. package/_packed_docs/sdk/09-storage-rpc-manual.md +18 -26
  23. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +61 -52
  24. package/_packed_docs/sdk/INDEX.md +92 -80
  25. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  26. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  27. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
  28. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  29. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +300 -0
  30. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +382 -0
  31. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -0
  32. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
  33. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  34. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  35. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +173 -0
  36. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +211 -0
  37. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
  38. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
  39. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
  40. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
  41. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
  42. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
  43. package/dist/agent-md-schema.d.ts +15 -4
  44. package/dist/agent-md-schema.d.ts.map +1 -1
  45. package/dist/agent-md-schema.js +64 -30
  46. package/dist/agent-md-schema.js.map +1 -1
  47. package/dist/agent-md.d.ts +10 -2
  48. package/dist/agent-md.d.ts.map +1 -1
  49. package/dist/agent-md.js +278 -121
  50. package/dist/agent-md.js.map +1 -1
  51. package/dist/aid-store.d.ts +46 -0
  52. package/dist/aid-store.d.ts.map +1 -1
  53. package/dist/aid-store.js +149 -10
  54. package/dist/aid-store.js.map +1 -1
  55. package/dist/aid.d.ts +4 -1
  56. package/dist/aid.d.ts.map +1 -1
  57. package/dist/aid.js +23 -42
  58. package/dist/aid.js.map +1 -1
  59. package/dist/auth.d.ts +1 -1
  60. package/dist/auth.d.ts.map +1 -1
  61. package/dist/auth.js +85 -18
  62. package/dist/auth.js.map +1 -1
  63. package/dist/bundle.js +1581 -781
  64. package/dist/cert-utils.d.ts +49 -1
  65. package/dist/cert-utils.d.ts.map +1 -1
  66. package/dist/cert-utils.js +102 -56
  67. package/dist/cert-utils.js.map +1 -1
  68. package/dist/client/delivery.d.ts +7 -0
  69. package/dist/client/delivery.d.ts.map +1 -1
  70. package/dist/client/delivery.js +243 -28
  71. package/dist/client/delivery.js.map +1 -1
  72. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  73. package/dist/client/rpc-pipeline.js +89 -77
  74. package/dist/client/rpc-pipeline.js.map +1 -1
  75. package/dist/client/v2-e2ee.d.ts.map +1 -1
  76. package/dist/client/v2-e2ee.js +99 -80
  77. package/dist/client/v2-e2ee.js.map +1 -1
  78. package/dist/client.d.ts.map +1 -1
  79. package/dist/client.js +31 -17
  80. package/dist/client.js.map +1 -1
  81. package/dist/crypto.d.ts.map +1 -1
  82. package/dist/crypto.js +8 -6
  83. package/dist/crypto.js.map +1 -1
  84. package/dist/events.d.ts +5 -2
  85. package/dist/events.d.ts.map +1 -1
  86. package/dist/events.js +33 -14
  87. package/dist/events.js.map +1 -1
  88. package/dist/facades.js +5 -5
  89. package/dist/facades.js.map +1 -1
  90. package/dist/index.d.ts +1 -1
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js.map +1 -1
  93. package/dist/org-protocol.d.ts.map +1 -1
  94. package/dist/org-protocol.js +72 -12
  95. package/dist/org-protocol.js.map +1 -1
  96. package/dist/ownership-v21.d.ts +25 -8
  97. package/dist/ownership-v21.d.ts.map +1 -1
  98. package/dist/ownership-v21.js +142 -73
  99. package/dist/ownership-v21.js.map +1 -1
  100. package/dist/ownership-workflow.d.ts +6 -1
  101. package/dist/ownership-workflow.d.ts.map +1 -1
  102. package/dist/ownership-workflow.js +221 -58
  103. package/dist/ownership-workflow.js.map +1 -1
  104. package/dist/tools/cross-sdk-agent.d.ts +2 -1
  105. package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
  106. package/dist/tools/cross-sdk-agent.js +91 -8
  107. package/dist/tools/cross-sdk-agent.js.map +1 -1
  108. package/dist/transport.d.ts +1 -0
  109. package/dist/transport.d.ts.map +1 -1
  110. package/dist/transport.js +2 -0
  111. package/dist/transport.js.map +1 -1
  112. package/dist/version.d.ts +1 -1
  113. package/dist/version.js +1 -1
  114. package/package.json +1 -1
@@ -0,0 +1,299 @@
1
+ # ProtectedHeaders 用法专题
2
+
3
+ > 状态:按当前仓库实现整理
4
+ >
5
+ > 更新时间:2026-10-07
6
+ >
7
+ > 适用范围:Python、TypeScript、JavaScript、Go SDK 的 E2EE V2 消息、群消息和 thought RPC
8
+
9
+ ## 1. 一句话理解
10
+
11
+ `protected_headers` 是 E2EE 信封中的元数据字典,位置类似 HTTP headers,业务正文仍放在 `payload`。它会随信封暴露给 Gateway 和消息服务,因此**可见但不可在不知道消息密钥时无损篡改**;它提供完整性保护,不提供机密性。
12
+
13
+ 典型用途是传递 `trace_id`、`request_id`、`app_name`、设备展示信息等需要让接收端识别、又不应被中间服务改写的元数据。访问令牌、私钥、业务正文、需要保密的会话上下文必须放在加密的 `payload` 或其它端到端加密字段中。
14
+
15
+ ## 2. 支持范围和字段位置
16
+
17
+ 当前 SDK 的实例级 `protected_headers` 自动合并只针对以下四个方法:
18
+
19
+ | RPC | 用途 | E2EE V2 信封 |
20
+ | --- | --- | --- |
21
+ | `message.send` | P2P 可靠消息 | `e2ee.p2p_encrypted` |
22
+ | `group.send` | 群可靠消息 | `e2ee.group_encrypted` |
23
+ | `message.thought.put` | P2P thought | `e2ee.p2p_encrypted` |
24
+ | `group.thought.put` | 群 thought | `e2ee.group_encrypted` |
25
+
26
+ 调用参数中的字段位于 RPC 参数顶层,与 `payload` 同级:
27
+
28
+ ```json
29
+ {
30
+ "to": "bob.agentid.pub",
31
+ "payload": {"type": "text", "text": "hello"},
32
+ "protected_headers": {"trace_id": "req-42"}
33
+ }
34
+ ```
35
+
36
+ `headers` 是历史兼容别名。新代码应使用 `protected_headers`;两个字段同时出现时,SDK 优先读取 `protected_headers`。
37
+
38
+ `protected_headers` 不属于业务 `payload`,也不应放进 `context`。`context.type + context.id` 是 thought 的服务端 selector;`chat_id`、`thread_id`、`client_context` 等应用上下文应保留在加密 payload 内。
39
+
40
+ ## 3. 两种使用方式
41
+
42
+ ### 3.1 实例级默认值
43
+
44
+ 实例级值保存在当前 `AUNClient` 内存中,只在四个消息/thought RPC 上自动合并。它适合稳定的 SDK、应用或设备元数据。
45
+
46
+ Python:
47
+
48
+ ```python
49
+ client.set_protected_headers({"app_name": "desk", "trace_id": "req-42"})
50
+
51
+ await client.call("message.send", {
52
+ "to": "bob.agentid.pub",
53
+ "payload": {"type": "text", "text": "hello"},
54
+ })
55
+ ```
56
+
57
+ TypeScript / JavaScript:
58
+
59
+ ```ts
60
+ client.setProtectedHeaders({ app_name: 'desk', trace_id: 'req-42' });
61
+
62
+ await client.call('message.send', {
63
+ to: 'bob.agentid.pub',
64
+ payload: { type: 'text', text: 'hello' },
65
+ });
66
+ ```
67
+
68
+ Go:
69
+
70
+ ```go
71
+ client.SetProtectedHeaders(map[string]string{
72
+ "app_name": "desk",
73
+ "trace_id": "req-42",
74
+ })
75
+ ```
76
+
77
+ 读取实例级值时,Python、TypeScript、JavaScript 返回副本;Go 的 `GetProtectedHeaders()` 也返回副本。Python/TypeScript/JavaScript 传 `None`/`null` 清空;Go 传空 map 清空。
78
+
79
+ ### 3.2 单次调用覆盖
80
+
81
+ 单次调用可以直接传普通字典/map。单次值只影响这一条调用,并且同名 key 会覆盖实例级默认值:
82
+
83
+ ```python
84
+ await client.call("group.send", {
85
+ "group_id": "group.example.com/g-1",
86
+ "payload": {"type": "text", "text": "group message"},
87
+ "protected_headers": {"trace_id": "req-43", "view": "compact"},
88
+ })
89
+ ```
90
+
91
+ Python、TypeScript、JavaScript 还可以使用 `ProtectedHeaders` 包装对象;Go 可以使用 `ProtectedHeaders` 包装对象作为 `Call` 参数:
92
+
93
+ ```python
94
+ from aun_core import ProtectedHeaders
95
+
96
+ headers = ProtectedHeaders().set("trace_id", "req-44").set("view", "compact")
97
+ await client.call("message.send", {
98
+ "to": "bob.agentid.pub",
99
+ "payload": {"type": "text", "text": "hello"},
100
+ "protected_headers": headers,
101
+ })
102
+ ```
103
+
104
+ ```ts
105
+ import { ProtectedHeaders } from '@agentunion/fastaun';
106
+
107
+ const headers = ProtectedHeaders.from({ trace_id: 'req-44' });
108
+ await client.call('message.send', {
109
+ to: 'bob.agentid.pub',
110
+ payload: { type: 'text', text: 'hello' },
111
+ protected_headers: headers,
112
+ });
113
+ ```
114
+
115
+ Go 包装对象的构造、修改和传入:
116
+
117
+ ```go
118
+ headers, err := aun.NewProtectedHeaders(map[string]any{"trace_id": "req-44"})
119
+ if err != nil { /* 处理错误 */ }
120
+ _, err = client.Call(ctx, "message.send", map[string]any{
121
+ "to": "bob.agentid.pub", "payload": map[string]any{"type": "text"},
122
+ "protected_headers": headers,
123
+ })
124
+ ```
125
+
126
+ ### 3.3 thought 的额外要求
127
+
128
+ `message.thought.put` 和 `group.thought.put` 同样支持 `protected_headers`,但必须同时提供顶层 `context.type` 和 `context.id`。`protected_headers` 不会代替 thought selector,也不会参与服务端 thought 定位。
129
+
130
+ ## 4. 实例级和单次值的合并规则
131
+
132
+ RPC 前置管线在进入 E2EE 路由前完成合并,等价于:
133
+
134
+ ```text
135
+ effective = instance_headers
136
+ explicit = params.protected_headers ?? params.headers
137
+ effective.update(explicit)
138
+ ```
139
+
140
+ 因此优先级从高到低是:
141
+
142
+ 1. 当前调用的 `protected_headers`;
143
+ 2. 当前调用的兼容别名 `headers`;
144
+ 3. `set_protected_headers` / `setProtectedHeaders` / `SetProtectedHeaders` 设置的实例级值。
145
+
146
+ 合并只影响四个消息/thought RPC。`meta.*`、`storage.*`、普通 `event` 通知、`message.pull`、`message.v2.*` 等调用不会因为设置了实例级值而自动附加它。
147
+
148
+ 如果同时传入 `protected_headers` 和 `headers`,不要依赖两者的合并结果;SDK 的读取入口把 `protected_headers` 视为主字段,建议只保留一个字段。
149
+
150
+ ## 5. Key 和 value 规范化
151
+
152
+ ### 5.1 Key
153
+
154
+ E2EE V2 加密引擎最终按以下规则规范化 key:
155
+
156
+ - 去除首尾空白并转为小写;
157
+ - 只能匹配 `[a-z0-9_-]+`;
158
+ - `_auth` 是 SDK 保留字段,应用不能设置;
159
+ - 不符合规则的 key 在加密路径会报错。
160
+
161
+ 实例级 setter 在 Python、TypeScript、Go 中会对不合法 key 静默跳过;当前 JavaScript 实例 setter 只过滤 `_auth`,其它非法 key 可能延迟到加密时才失败。为了四端一致,应用应直接使用小写的 ASCII key,例如 `trace_id`、`app_name`、`device_name`。
162
+
163
+ ### 5.2 Value
164
+
165
+ 跨 SDK 最稳妥的做法是传字符串。当前实现的转换规则是:
166
+
167
+ | 输入 | E2EE V2 加密时的结果 |
168
+ | --- | --- |
169
+ | 字符串 | 原样保留 |
170
+ | `null` / `None` | 空字符串 |
171
+ | 其它 JSON 值 | 使用跨语言 canonical JSON 转为字符串 |
172
+
173
+ `ProtectedHeaders` 包装类的 `set` / `Set` 会提前把值转为字符串;普通字典/map 则在 E2EE 加密阶段规范化。不要依赖 Python `str(dict)`、JavaScript 对象默认字符串化或 Go `fmt.Sprint` 产生的语言特定格式传递嵌套对象。
174
+
175
+ ### 5.3 SDK 自动字段
176
+
177
+ 应用不需要手工填写以下字段,V2 加密引擎会自动处理:
178
+
179
+ - `payload_type`:从明文 `payload.type` 补入;
180
+ - `sdk_lang`:当前 SDK 语言;
181
+ - `sdk_version`:当前 SDK 版本。
182
+
183
+ 当前实现会在 V2 信封中始终注入 `sdk_lang` 和 `sdk_version`,即使应用没有传自定义 header。显式传入的 `sdk_lang`、`sdk_version` 会被 SDK 的真实值覆盖;`payload_type` 也不应由应用伪造。
184
+
185
+ ## 6. E2EE 信封和完整性保护
186
+
187
+ 加密后的信封中,`protected_headers` 与 `ciphertext` 是并列字段,示意如下:
188
+
189
+ ```json
190
+ {
191
+ "type": "e2ee.p2p_encrypted",
192
+ "version": "v2",
193
+ "payload_type": "text",
194
+ "protected_headers": {
195
+ "trace_id": "req-42",
196
+ "payload_type": "text",
197
+ "sdk_lang": "python",
198
+ "sdk_version": "0.5.x",
199
+ "_auth": {
200
+ "alg": "HMAC-SHA256",
201
+ "tag": "base64..."
202
+ }
203
+ },
204
+ "ciphertext": "..."
205
+ }
206
+ ```
207
+
208
+ `_auth` 不是应用字段,而是 SDK 生成的元数据认证标签。当前四端使用同一套计算规则:
209
+
210
+ ```text
211
+ metadata_key = HMAC-SHA256(master_key, "aun-envelope-metadata-key-v1")
212
+ body = protected_headers 去掉 _auth
213
+ sign_input = "aun-protected-headers-v1" + "\0" + canonical_json(body)
214
+ tag = HMAC-SHA256(metadata_key, sign_input)
215
+ ```
216
+
217
+ `canonical_json` 使用 UTF-8、稳定 key 顺序和紧凑分隔符。`context` 采用同一派生方式,但 domain 是 `aun-protected-context-v1`,不要把两者的 `_auth` 混用。
218
+
219
+ 为兼容旧信封,`protected_headers` 不加入原有正文 AEAD 的整体 AAD,而是单独使用上述 HMAC 做完整性校验。消息服务可以看到 header 内容,但没有 `master_key` 时不能生成有效标签来修改保留下来的字段。该机制不防止服务删除整个 header,也不提供 header 内容保密。
220
+
221
+ ## 7. 接收端读取和信任边界
222
+
223
+ 解密流程先恢复本条消息的 `master_key`,再验证 `protected_headers._auth`;验证通过后解密正文。SDK 返回给应用层时会去掉 `_auth`,常见结果形态如下:
224
+
225
+ ```json
226
+ {
227
+ "payload": {"type": "text", "text": "hello"},
228
+ "payload_type": "text",
229
+ "protected_headers": {"trace_id": "req-42", "sdk_lang": "python"},
230
+ "e2ee": {
231
+ "payload_type": "text",
232
+ "protected_headers": {"trace_id": "req-42", "sdk_lang": "python"}
233
+ }
234
+ }
235
+ ```
236
+
237
+ Python、TypeScript、JavaScript、Go 都会把没有 `_auth` 的业务字段暴露在消息顶层和 `e2ee.protected_headers` 元数据中;原始 envelope 或调试日志中仍可能看到带 `_auth` 的版本。
238
+
239
+ `message.undecryptable` / `group.message_undecryptable` 等失败事件会尽量透传原始 envelope 的元数据以便排障。此时 header 可能尚未通过本地验证,只能当作诊断信息,不能当作可信业务输入。只有成功解密并通过 `_auth` 校验后的消息元数据才可用于业务判断。
240
+
241
+ 以下情况会使当前消息进入解密失败路径:header 不是对象、带正文却缺少 `_auth`、算法不是 `HMAC-SHA256`、tag 缺失或格式错误、tag 校验失败。接收端不应自行忽略这些错误后继续信任 header。
242
+
243
+ ## 8. 明文、裸 RPC 和预构造信封边界
244
+
245
+ `message.send`、`group.send` 默认走 SDK E2EE V2。显式 `encrypt: false` 时,SDK 仍可能把顶层 `protected_headers` / `headers` 透传给明文 RPC,但不会为它们生成 `_auth`;这时它们只是普通传输字段,不能称为受保护元数据。
246
+
247
+ 如果直接调用 `message.v2.*`、`group.v2.*` 或自行通过裸 WebSocket 发送已经构造好的 E2EE envelope,实例级 setter 不会替调用方自动补 header。调用方需要自行完成 key/value 规范化和 `_auth` 生成,或者继续使用 `message.send` / `group.send` 的 SDK 路由,让 SDK 负责构造 envelope。
248
+
249
+ 不要把 header 放进 `payload` 期待 SDK 自动提升;放进 payload 后它只是业务数据,语义和接收位置都会改变。
250
+
251
+ ## 9. 四端公开 API 对照
252
+
253
+ | 能力 | Python | TypeScript / JavaScript | Go |
254
+ | --- | --- | --- | --- |
255
+ | 实例级设置 | `client.set_protected_headers(dict \| None)` | `client.setProtectedHeaders(object \| null)` | `client.SetProtectedHeaders(map[string]string)` |
256
+ | 实例级读取 | `get_protected_headers()`,返回 `dict \| None` | `getProtectedHeaders()`,返回对象或 `null` | `GetProtectedHeaders()`,返回 map 副本 |
257
+ | 包装对象 | `ProtectedHeaders`:`set/get/remove/to_dict/from_dict` | `ProtectedHeaders`:`set/get/remove/toObject/toJSON/from` | `NewProtectedHeaders`、`Set/Get/Remove/ToMap` |
258
+ | 单次 RPC 字段 | `protected_headers` 或兼容别名 `headers` | `protected_headers` 或兼容别名 `headers` | `protected_headers` 或兼容别名 `headers` |
259
+ | 公开导出 | `aun_core.ProtectedHeaders` | 包入口导出 `ProtectedHeaders` 和 `ProtectedHeadersInput` | `aun.ProtectedHeaders` 和 `aun.NewProtectedHeaders` |
260
+
261
+ TypeScript / JavaScript 的 `ProtectedHeaders` 类方法使用 camelCase API,但 RPC 参数名仍使用协议字段 `protected_headers`。Go 的 `NewProtectedHeaders` 和 `Set` 会返回错误,应用应在发送前处理错误。
262
+
263
+ ## 10. 推荐约定和反例
264
+
265
+ 推荐:
266
+
267
+ - 使用小写 snake_case 或短横线 key,例如 `trace_id`、`app_name`、`device_name`;
268
+ - 把 `trace_id`、`request_id` 等链路信息放入 header,把 `chat_id`、`thread_id`、`client_context` 放进 payload;
269
+ - SDK 版本、语言和 `payload_type` 交给 SDK 自动注入;
270
+ - 需要跨语言互操作时全部使用字符串 value;
271
+ - 接收端把 header 当作已验证的辅助元数据,不把它当作权限凭证。
272
+
273
+ 反例:
274
+
275
+ ```text
276
+ protected_headers = {"access_token": "..."} # 仍会暴露给服务端
277
+ protected_headers = {"_auth": "自定义值"} # 保留字段,不能由应用设置
278
+ payload = {"protected_headers": {...}} # 只是业务 payload,不会进入信封 header
279
+ headers + protected_headers 同时传入 # 主字段优先,容易造成误判
280
+ ```
281
+
282
+ ## 11. 代码和文档索引
283
+
284
+ 核心文档:
285
+
286
+ - `docs/sdk/05-E2EE加密通信.md`:ProtectedHeaders 语义、示例和 HMAC 规则;
287
+ - `docs/sdk/06-API手册.md`:四端实例级 setter/getter;
288
+ - `docs/sdk/08-最佳实践.md`:实例级使用建议;
289
+ - `docs/sdk/09-payload-reference.md`:信封字段与 payload 的边界;
290
+ - `docs/sdk/09-message-rpc-manual.md`、`docs/sdk/09-group-rpc-manual.md`:消息和群 RPC 参数。
291
+
292
+ 实现位置:
293
+
294
+ - Python:`python/src/aun_core/e2ee.py`、`python/src/aun_core/client.py`、`python/src/aun_core/_client/rpc_pipeline.py`、`python/src/aun_core/v2/e2ee/encrypt_p2p.py`、`python/src/aun_core/v2/e2ee/decrypt.py`;
295
+ - TypeScript:`ts/src/protected-headers.ts`、`ts/src/client/rpc-pipeline.ts`、`ts/src/v2/e2ee/encrypt-p2p.ts`;
296
+ - JavaScript:`js/src/protected-headers.ts`、`js/src/client/rpc-pipeline.ts`、`js/src/v2/e2ee/encrypt-p2p.ts`;
297
+ - Go:`go/e2ee.go`、`go/client_rpc_pipeline.go`、`go/v2/e2ee/metadata_auth.go`、`go/v2/e2ee/encrypt_p2p.go`。
298
+
299
+ 相关测试覆盖实例级合并、别名优先级、key/value 规范化、HMAC 验证、篡改失败和四端互操作,包括 Python `tests/unit/test_client_protected_headers.py`、Go `v2/e2ee/metadata_auth_test.go`、TypeScript/JavaScript 的 `rpc-pipeline` 与 V2 E2EE 单元测试。
@@ -0,0 +1,173 @@
1
+ # 凭证与组织服务 SDK 应用集成指南
2
+
3
+ > 适用版本:四套 AUN SDK `0.5.19`,组织服务协议 `0.8`。
4
+ >
5
+ > 本文面向使用 SDK 的应用开发者。它描述当前代码已经提供的本地协议助手、facade 和服务端闭环;字段级冻结契约以 [组织服务 0.8 详细设计](../../../extensions/services/docs/org/0.8/17-凭证与组织服务详细设计.md) 为准。`14-App接入.md` 是产品界面设计,`11-RPC与SDK.md` 仍包含提出稿方法面,不能单独作为当前实现清单。
6
+
7
+ ## 1. 先确认交付边界
8
+
9
+ SDK 提供两类入口:
10
+
11
+ | 入口 | 作用 | 当前可用方法 |
12
+ |---|---|---|
13
+ | `credential` | 创建、离线验签、完整验证、登记、吊销、状态和吊销列表 | `register`、`revoke`、`status`、`revocationList` |
14
+ | `org` | 组织生命周期、成员、组织侧凭证状态、审计、授权检查和请求准备 | 以服务 manifest 与本指南第 5 节为准 |
15
+
16
+ Credential 和 Org 是业务服务;`credential.register/revoke/status`(携带 `credential_cid` 时)/`credential.revocationList` 及组织业务 RPC 均经 Gateway service-plane 调用,Kernel 只承载控制面生命周期和 health/status 诊断,不承载凭证、成员或权限业务 RPC。四端 facade 存在不表示目标服务已经部署。探活应使用部署探针或受控的控制面诊断接口;不要把 `credential.status` 一概当作探活:携带 `credential_cid` 的 `credential.status` 是业务状态查询,走 service-plane。服务缺失、证书不可用、数据库不可用时,不能把本地 helper 成功当作服务端授权成功。
17
+
18
+ 当前服务端明确保留但尚未实现的入口只有 `org.resource.create`、`org.task.create`、`org.host.add`、`org.workflow.create`;Python 和 Go 仍暴露对应子 facade 以保持统一入口,调用这四个方法时服务端返回业务码 `NOT_IMPLEMENTED`。其它 `org.resource.*`、`org.task.*`、`org.host.*`、`org.workflow.*` 方法不在当前 dispatcher 中,调用时按 JSON-RPC `Method not found` 处理。TS/JS 可通过 `client.call` 访问这些方法,结果遵循同一规则。
19
+
20
+ ## 2. 运行前准备
21
+
22
+ 应用需要准备:
23
+
24
+ 1. 一个已登录的 AUN `AUNClient` 和当前 AID。请求的认证主体来自当前会话,不能用 `from` 参数冒充另一个主体。
25
+ 2. 颁发者或组织实体的本地私钥、证书和证书指纹。SDK 的 signer 至少提供 `aid`、`certPem`、`certFingerprint` 和 `sign`。
26
+ 3. 一个按证书指纹返回受信 X.509 证书的 resolver。resolver 可返回 PEM、DER 字节,或严格标准 Base64 DER。
27
+ 4. 完整验证所需的状态、成员关系和 Ownership resolver。验证失败、状态未知或 resolver 不可用都按拒绝处理。
28
+ 5. 若创建或转让组织,使用 `AIDStore` 保存组织实体的私钥和证书。组织服务不保存组织私钥,转让也不能把旧私钥复制给新 owner。
29
+
30
+ 连接认证、AIDStore 和 token 生命周期沿用现有 SDK 文档;本文只覆盖凭证和组织业务。
31
+
32
+ ## 3. 凭证的应用流程
33
+
34
+ ### 3.1 创建并保留完整凭证
35
+
36
+ 凭证是签名对象 `aun-cap/1`。应用只向 `credential.create` 传入未签名字段和颁发者 signer;SDK 会补 `cid`、签发时间、根凭证默认字段和签名。`credential.sub` 必须是真正持有并使用凭证的 AID,不能改成 owner、代理人或服务 AID。
37
+
38
+ ```ts
39
+ const credential = await client.credential.create({
40
+ sub: holderAid, aud: [serviceAid], exp, key_version: signer.certFingerprint,
41
+ cap: [{ ns: 'files', action: 'read', resource: '/reports', constraints: [] }]
42
+ }, signer);
43
+ ```
44
+
45
+ 成员角色凭证不要手工拼装。先创建或取得完整 `membership_binding`,再调用 `org.member.createCredential`(Python/Go 为 `create_credential`/`CreateCredential`);helper 会固定 `iss`、`sub`、`aud`、`role.*` 能力和 `ext` 成员绑定字段,并拒绝冲突覆盖。
46
+
47
+ ### 3.2 选择正确的验证级别
48
+
49
+ `verifySignature` 只验证凭证结构、签名、证书指纹和可选时间/受众。它适合展示或接收前的快速预检,不能得出“当前请求有权执行”的结论。
50
+
51
+ 完整 `verify` 必须提供:
52
+
53
+ - `audience`、实际 `subject`、`ns/action/resource`;
54
+ - 实际 `method/target/params/request_id/nonce/request_binding`;
55
+ - `resolveCertificate`;
56
+ - 根/父凭证状态 resolver。存在 `parent` 时,父链必须使用受信强 `resolveStatusProof` resolver 返回的完整状态 proof,不能把原始 RPC 响应或字符串 `active` 当作父链证明。resolver 必须预先验证 Credential 服务签名、服务证书信任链和当前撤销状态,并确认 proof 对应实际查询的 `request_id`、nonce 和 target;SDK 再检查 proof 字段、CID/hash、状态及证明时间窗;
57
+ - 成员凭证的当前 `membershipBinding` 和 `resolveMembershipStatus`;
58
+ - 代表调用的 `actor`、`for`、`representationProof`、当前 Ownership resolver 和 actor 状态 resolver。
59
+
60
+ 完整验证依次检查签名、时间、audience、父链收窄、当前状态、能力约束、请求绑定、成员 binding 和代表关系。返回的 `signature_valid=true` 不覆盖任何后续检查;`CREDENTIAL_STATUS_UNKNOWN`、`CREDENTIAL_REQUEST_BINDING_INVALID`、`MEMBERSHIP_STATE_INVALID` 和 `ROLE_ACCESS_DENIED` 均应停止副作用操作。
61
+
62
+ ### 3.3 登记、回执和吊销
63
+
64
+ Credential Service 只接受完整已签名凭证,不提供公开 `credential.prepare`。登记流程是:
65
+
66
+ 1. 颁发者直接调用 `credential.register`,或签署 `registration_authorization` 让指定服务转交。
67
+ 2. 检查返回的 `registrationReceipt`:服务签名、`service_aid`、`registrar_aid=credential.iss`、CID、完整凭证摘要、原 `request_id`/nonce、状态和有效期必须全部匹配。
68
+ 3. Org 入组最终化前再次验证回执。登记成功不等于成员已经 `active`。
69
+ 4. 失败补偿使用原 CID、原操作上下文和明确吊销授权调用 `credential.revoke`;`NOT_FOUND` 或未知状态不能当作补偿完成。
70
+
71
+ `credential.register`、`credential.revoke`、携带 `credential_cid` 的 `credential.status` 及 `credential.revocationList` 请求都必须携带 `request_id`、nonce 和 `request_binding`,绑定真实 method、target、参数摘要和时间窗。`credential.revocationList` 没有单一凭证目标,其 `request_binding.credential_cid` 固定为空字符串;`params_hash` 仍覆盖 `issuer_aid`、`cursor`、`limit`、`since_event_seq` 等全部业务参数。除该列表方法外,凡要求 `request_binding` 的 Credential 业务请求都必须绑定非空凭证 CID。状态 proof 有独立 `asOf/proofExpiresAt` 窗口;父链状态 proof 还必须包含 `credential_hash`、`request_id`、nonce、target、responder 和签名。状态未知时应用不得放行写操作。`revocationList` 用于增量补齐吊销事件,断序或证明过期时暂停受影响凭证的副作用操作。
72
+
73
+ ## 4. 组织成员流程
74
+
75
+ ### 4.1 组织创建
76
+
77
+ 创建组织需要两个身份:当前会话 owner signer,以及本地生成并保存的组织实体 signer。创建入口按 SDK 分别为 Python `client.org.lifecycle.create`、TypeScript/JavaScript `client.createOrg`、Go `client.CreateOrg`;TypeScript/JavaScript 的生命周期 facade 在提供 `aidStore` 时会转入高层创建流程,否则直接调用 `org.lifecycle.create`。按创建流程提供完整 binding、CAP 和授权材料,并完成实体主签、当前 owner token 上传的组织名片发布;低层请求应先用各端的 `org.prepareRequest`/`org.prepare_request` 准备,再调用对应 facade。
78
+
79
+ 组织名片使用 `aid=org_id`、`type=org`、当前 `owner` 和实体证书指纹。实体私钥生成主签并显式 signer_aid=org_id,当前 owner 以本人 token 上传,无需实体登录;可选一个或多个当前 owner 附签,存在就必须全块验证,取消实体额外 V2 上传授权。现代实体指纹严格匹配,Org 不开放旧实体上传;通用 Ownership facade 对组织实体返回 not_applicable。文件签验与 HTTP 多签协商见 [Ownership 应用指南](Ownership2.1-SDK应用集成指南.md#34-通用签验与多签名片) 及 [API 手册](06-API手册.md#通用-sign--verify)。
80
+
81
+ ### 4.2 申请加入与邀请
82
+
83
+ 申请模式:
84
+
85
+ 1. 成员创建 `membership_binding`,由成员 signer 签 `member_signature`。
86
+ 2. owner/admin 对同一不可变条款签 `org_signature`,并准备完整成员 CAP 和登记授权。
87
+ 3. 调用 `org.member.approveJoin`;服务登记 CAP、验证回执,然后完成成员关系。
88
+
89
+ 邀请模式:
90
+
91
+ 1. 组织 signer 为每个目标 AID 创建独立 binding、nonce、request ID 和 `org_signature`。
92
+ 2. 目标成员签 `member_signature` 后调用 `org.member.inviteAccept`。
93
+ 3. 服务完成登记和最终化。每个目标的 binding、CAP 和授权包不得互换。
94
+
95
+ `org.member.join`/`inviteAccept` 的首次入组均使用以 `membership_id`、原 `request_id` 和 nonce 绑定的专用 admission request binding,不带 `credential_cid`,也不要求已有 active 成员 CAP。本人直接调用和代表调用都必须由目标成员私钥提供 `member_signature`;仅当 `for != signer.aid` 代表另一个目标时,才由认证 actor signer 签 admission representation proof,并通过当前 ownership/status 校验。本人直接 `inviteAccept` 复用邀请保存的组织签名、CAP 模板和登记授权,SDK 不重签或重造这些材料。owner 的代表证明不能替代目标成员签名,也不能把 `credential.sub` 改成 owner。
96
+
97
+ ### 4.3 查询、状态变更与续期
98
+
99
+ 成员状态优先于角色和能力:`banned`/`ended` 拒绝授权,`frozen` 拒绝普通写操作但允许离职收尾,`offboarding` 只允许收尾动作。应用显示成员状态应使用 `org.member.get` 返回的签名 `membership_status`,不要仅凭历史 binding 或 CAP 状态判断当前成员资格。
100
+
101
+ 成员离职/移除的正常顺序是 `leave` 或 `kick` → `offboard(confirm)` → 持久化 `ended` → 吊销本组织明确拥有撤销权限的成员 CAP → 删除 active projection。失败时保留后台续跑记录。
102
+
103
+ 原关系内换发使用 `org.member.renewCredential`:提交旧 CID、当前版本、完整新 binding、组织签名 CAP 和登记授权;新 CAP 登记并确认后才撤旧 CAP,失败只撤新 CAP。不要把 renew 当成重新加入,也不要给旧 CAP 全局豁免。
104
+
105
+ ## 5. 组织请求与跨设备代表
106
+
107
+ ### 5.1 统一请求准备
108
+
109
+ 需要业务授权或 request binding 的请求应调用 `prepareRequest`/`prepare_request`/`PrepareRequest`,不论它是读还是写。`org.member.list`、`org.member.get`、`org.credential.getStatus` 属于受保护业务读取,必须提供当前成员 binding 和对应 CAP,并准备 AAD、`request_binding` 与 `client_signature`。公开组织的 `org.lifecycle.list/get` 按可见性规则处理;`org.health/status` 是控制面诊断路径。首次 `org.member.join` 和 `org.member.inviteAccept` 无论本人还是代表调用,都使用 membership binding 中的 `request_id`/nonce 和 `membership_id` 专用 admission `request_binding`,不带 `credential_cid`;本人调用不需要 admission representation proof,也不要求已有 active 成员 CAP,`inviteAccept` 复用邀请保存的组织签名、CAP 模板和登记授权。只有代表目标节点(`for != signer.aid`)才生成 admission representation proof,并通过当前 ownership/status 校验。准备函数负责:
110
+
111
+ - 自动生成或复用 `request_id`、nonce;锚定入组流程时必须与 binding 一致;
112
+ - 为所有 `org.*` 请求加入 `aad.org_id` 并拒绝不匹配的已有 AAD;仅 `org.lifecycle.list` 及 health/status 路径允许 `org_id:null`,`org.member.list` 仍必须绑定具体 `org_id`;
113
+ - 生成 `request_binding`,其 `params_hash` 覆盖真实业务参数、AAD 和授权包,排除 `request_binding`、`client_signature`、`_` 前缀字段,并排除 `credentials.representation_proof` 避免循环;
114
+ - 在需要时生成 membership binding、member CAP、registration authorization 和 representation proof;已有签名或 binding 冲突直接拒绝,不重写原材料。
115
+
116
+ 随后把返回 params 原样交给 `client.call` 或对应 facade。客户端 RPC 管线会为 `org.*` 自动生成 `client_signature`;应用不要把该字段提前复制到另一个请求,也不要改写已准备的摘要。
117
+
118
+ ### 5.2 代表操作
119
+
120
+ `for` 是业务目标,不是认证身份。当前会话 actor 代表另一个 AID 时,SDK 必须绑定:`from`、`for`、`represented`、实际服务 target、method、params_hash、nonce 和时间窗;使用已有 CAP 的代表请求绑定 credential CID,首次入组 admission 请求绑定 `membership_id`。服务端还要通过受信 Ownership 和 actor 状态 resolver 确认 actor 当前有权代表目标;封禁、状态未知或 resolver 缺失均拒绝。
121
+
122
+ ### 5.3 服务事件
123
+
124
+ Credential/Org 事件先独立验证 responder、目标 AID、时间窗、服务证书和签名,再进入 SDK 去重/handler。去重键包含 responder、event_id 和当前 AID;事件过期、证书不可信或未知凭证事件不能触发本地授权更新。
125
+
126
+ ## 6. 所有权转让与实体材料
127
+
128
+ 转让不是一次普通 RPC:
129
+
130
+ 1. 目标 owner 在本地持久生成新的 P-256 私钥。
131
+ 2. `transferAccept(stage=rekey)` 提交新 SPKI 公钥和旧 owner 降级预检材料。
132
+ 3. 服务返回新证书和 `pending_finalization`;目标 owner 导入新证书。
133
+ 4. 用新实体 signer 重签双方 binding/CAP,准备新的登记授权,以独立请求标识调用 `stage=finalize`。
134
+ 5. 成功后原 owner 降为 member,旧 CAP 持久吊销;新 owner 重新上传实体名片。
135
+
136
+ 四端高层流程会把输入摘要、proposal、阶段 params 和私钥 checkpoint 保存到本地。恢复时 transfer ID、owner、输入摘要、公钥和阶段上下文必须一致;材料变化返回 conflict,不发新 RPC。服务断线不应重新生成另一把 key 或重复创建另一份 proposal。
137
+
138
+ 解散高层入口以当前 signer 和调用方提供的当前成员 binding/CAP 准备业务授权请求,补齐 request ID、nonce、AAD 和 `request_binding`,核验已有请求绑定并保留原成员材料。仅在服务确认 `dissolved` 后,四端记录当前实体证书指纹失效并清理创建/转让 checkpoint、待上传名片和当前设备/slot 恢复状态;保留磁盘私钥,已加载的 AID/signer 不追溯失效,后续 `AIDStore.load` 拒绝该证书。创建或转让后的名片待办保存完整多签原文、原 CAS、固定 owner 与实体证书指纹,由应用显式重试,重连不自动 PUT。重试核对当前生命周期与证书代际,原条件完整且同代际全文/ETag 已证明提交时只观察完成;转移、rekey 或解散后停止旧待办,不换身份、字节或条件重签。专属 create/transfer 自身 checkpoint 恢复机制不变。取消额外 V2 授权及其过期窗口,组织成员 binding/CAP 签名规则不变。
139
+
140
+ ## 7. 四端入口速查
141
+
142
+ | 能力 | Python | TypeScript | JavaScript | Go |
143
+ |---|---|---|---|---|
144
+ | 凭证 facade | `client.credential` | `client.credential` | `client.credential` | `client.Credential()` |
145
+ | 组织 facade | `client.org` | `client.org` | `client.org` | `client.Org()` |
146
+ | 请求准备 | `org.prepare_request` | `org.prepareRequest` | `org.prepareRequest` | `org.PrepareRequest` |
147
+ | binding | `create_binding/sign_binding` | `createBinding/signBinding` | `createBinding/signBinding` | `CreateBinding/SignBinding` |
148
+ | 成员 CAP | `create_credential` | `createCredential` | `createCredential` | `CreateCredential` |
149
+ | 完整验证 | `credential.verify` | `credential.verify` | `credential.verify` | `Credential().Verify` |
150
+ | 转让签名 | `sign_transfer_acceptance` | `signTransferAcceptance` | `signTransferAcceptance` | `SignTransferAcceptance` |
151
+
152
+ Python RPC helper 是 async;浏览器 JS 的密码学 helper 返回 Promise;Node.js TypeScript helper 同步;Go 返回 `(value, error)`。协议字段和错误码必须保持一致,不能把语言异步差异写入 wire 格式。
153
+
154
+ ## 8. 错误与重试
155
+
156
+ - 只重试读请求,或同一 `request_id`、同一 method、同一摘要的幂等重试。
157
+ - `CREDENTIAL_IDEMPOTENCY_KEY_REUSE`、签名错误、权限拒绝、binding 冲突和成员状态拒绝不重试。
158
+ - `CREDENTIAL_STATUS_UNKNOWN`、服务不可用、数据库不可用可在状态窗口内重试,但重试前重新确认 resolver 和证书;不要降级为 allow。
159
+ - 记录 CID、receipt_id、membership_id、request_id 和摘要即可;不把私钥、token 或完整凭证写入普通日志。
160
+ - RPC 成败看结构化 error/exit code,不解析展示文本。
161
+
162
+ ## 9. 验收清单
163
+
164
+ 应用上线前至少验证:
165
+
166
+ - 离线签名预检与完整 Verify 的分界;父链强 status proof;成员状态和代表状态未知均拒绝;
167
+ - 登记回执的服务签名、CID/hash、registrar、request_id/nonce 和有效期;失败补偿能得到明确 revoked 结果;
168
+ - join 和 invite 的双方签名、每目标独立 binding、首次 admission 的 target signature;
169
+ - client signature、AAD、request binding 的摘要重算与跨语言向量一致;
170
+ - transfer rekey/finalize 断线恢复、私钥 checkpoint、旧 owner 降级和名片重新上传;
171
+ - 服务未部署、服务证书无效、Credential 状态未知、组织数据库不可用时均 fail-closed。
172
+
173
+ 相关协议、共同向量和服务验收见 [组织服务 0.8 详细设计](../../../extensions/services/docs/org/0.8/17-凭证与组织服务详细设计.md)、[TDD 实施计划](../../../extensions/services/docs/org/0.8/18-凭证与组织服务TDD实施计划.md) 和 [四 SDK 对齐变更清单](../../python/CHANGES_FOR_PARITY.md)。