@agentunion/fastaun 0.5.20 → 0.5.22
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 +104 -0
- package/_packed_docs/CHANGELOG.md +104 -0
- package/_packed_docs/INDEX.md +59 -34
- package/_packed_docs/KITE_DOCS_GUIDE.md +59 -52
- package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/344/270/216/346/215/242owner/347/274/226/346/216/222/346/226/271/346/241/210.md +530 -0
- package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/345/256/214/346/225/264/350/256/276/350/256/241/344/270/216/345/256/236/346/226/275/350/256/241/345/210/222.md +685 -0
- package/_packed_docs/agent.md/working/agent.md-schema/344/270/216/347/255/276/345/217/221/351/252/214/347/255/276/346/265/201/347/250/213/346/276/204/346/270/205.md +349 -0
- package/_packed_docs/agent.md/working//346/234/254/345/234/260Docker/346/265/213/350/257/225Bug/346/270/205/345/215/225.md +40 -0
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +307 -67
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +41 -15
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1433 -1392
- package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +30 -8
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +2 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +33 -22
- package/_packed_docs/sdk/0.5.21-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +368 -0
- package/_packed_docs/sdk/0.5.21-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +350 -0
- package/_packed_docs/sdk/0.5.22-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +212 -0
- package/_packed_docs/sdk/0.5.22-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +199 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +4 -1
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +42 -39
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +163 -14
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +48 -47
- package/_packed_docs/sdk/09-group-rpc-manual.md +15 -2
- package/_packed_docs/sdk/09-message-rpc-manual.md +250 -160
- package/_packed_docs/sdk/09-storage-rpc-manual.md +64 -7
- package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +73 -0
- package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +90 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +33 -25
- package/_packed_docs/sdk/INDEX.md +67 -33
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +74 -57
- 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 +312 -300
- 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 +322 -382
- 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 -708
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +28 -20
- 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 +190 -97
- 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 +158 -127
- package/dist/agent-md-schema.js +6 -4
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +22 -2
- package/dist/agent-md.js +92 -68
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +13 -0
- package/dist/aid-store.js +50 -15
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +22 -2
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +1 -0
- package/dist/cert-utils.js +13 -0
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +87 -1
- package/dist/client/delivery.js +1387 -139
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +3 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -1
- package/dist/client/rpc-pipeline.js +149 -37
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +12 -2
- package/dist/client/v2-e2ee.js +831 -304
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +26 -2
- package/dist/client.js +412 -68
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +18 -1
- package/dist/events.js +148 -9
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +43 -1
- package/dist/facades.js +363 -7
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.d.ts +1 -0
- package/dist/group-fs.js +37 -19
- package/dist/group-fs.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +6 -1
- package/dist/keystore/aid-db.js +184 -42
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +14 -0
- package/dist/keystore/local-token-store.d.ts +9 -1
- package/dist/keystore/local-token-store.js +79 -14
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +46 -0
- package/dist/org-protocol.js +802 -12
- package/dist/org-protocol.js.map +1 -1
- package/dist/ownership-v21.d.ts +36 -5
- package/dist/ownership-v21.js +168 -52
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +24 -2
- package/dist/ownership-workflow.js +682 -41
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/secret-store/file-store.js +84 -0
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +2 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/storage/lowlevel.d.ts +6 -0
- package/dist/storage/lowlevel.js +24 -7
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/storage/read-cache.d.ts +70 -0
- package/dist/storage/read-cache.js +625 -0
- package/dist/storage/read-cache.js.map +1 -0
- package/dist/storage/vfs.d.ts +2 -0
- package/dist/storage/vfs.js +50 -15
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +86 -3
- package/dist/tools/cross-sdk-agent.js +758 -46
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +3 -0
- package/dist/transport.js +104 -46
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/v2/e2ee/encrypt-group.js +1 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/e2ee/types.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# AUN SDK 0.5.22 开发与移植指南
|
|
2
|
+
|
|
3
|
+
面向 SDK 维护者及其它语言实现者,基于 [0.5.21 移植指南](0.5.21-SDK开发与移植指南.md)。本文对应 2026-10-23 的未发布源码;应用接线见 [应用集成指南](0.5.22-SDK应用集成指南.md),Rust 定位见第 7 节。
|
|
4
|
+
|
|
5
|
+
## 1. 范围与参考
|
|
6
|
+
|
|
7
|
+
对齐后台同步、群状态、Ownership 2.1、撤销同步、组织恢复和默认读缓存。沿用原 peer 同步、batch、提交后 ACK、签验和 checkpoint 契约;各端实现与验证记录见 [parity v0.5.22](../../python/CHANGES_FOR_PARITY.md#v0522--相对于-v0521-发布基线的变更未发布)。
|
|
8
|
+
|
|
9
|
+
## 2. 后台同步退避与群终止
|
|
10
|
+
|
|
11
|
+
### 2.1 按 namespace 退避
|
|
12
|
+
|
|
13
|
+
P2P、群消息、群事件、peer 目录和按 peer 恢复分别保存失败次数/重试时间,采用有上限的指数退避;已有等效机制可保留时长配置。
|
|
14
|
+
|
|
15
|
+
- Push/hint、分页和补拉合并需求,不覆盖未到期的退避。
|
|
16
|
+
- 等待时释放 Pull Gate,允许其它 namespace 和前台继续;前台保持原返回契约。
|
|
17
|
+
- 成功清除本 namespace 失败状态;身份、设备、slot 或连接换代时清旧状态并丢弃旧结果。
|
|
18
|
+
|
|
19
|
+
### 2.2 群错误分类与停止
|
|
20
|
+
|
|
21
|
+
优先使用布尔 `error.data.retryable`,缺失时按错误码/文本兼容判断:
|
|
22
|
+
|
|
23
|
+
| 错误类型 | 处理方式 |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| 临时网络或服务错误 | 按 namespace 退避重试 |
|
|
26
|
+
| 非成员、封禁、群不存在或已解散等终态 | 停止该群消息及事件 namespace,清理补拉、分页和 tail 待办 |
|
|
27
|
+
|
|
28
|
+
群别名共用停止状态,Push/hint 和扫描不重启;手动拉取成功、身份或连接换代后恢复。保留本地消息/游标,失败页不确认。字段见 [Message](09-message-rpc-manual.md)/[Group RPC](09-group-rpc-manual.md)。
|
|
29
|
+
|
|
30
|
+
## 3. 群身份恢复与提案确认
|
|
31
|
+
|
|
32
|
+
### 3.1 自动任务检查群状态
|
|
33
|
+
|
|
34
|
+
在自动恢复身份、绑定和确认提案的入口,跳过明确非 `active` 的群(包括 `suspended`),保留身份、消息及游标;缺少 `status` 时沿用兼容行为。
|
|
35
|
+
|
|
36
|
+
### 3.2 确认提案前复查角色
|
|
37
|
+
|
|
38
|
+
收到 `state_proposed` 或直接确认已有提案时,重查当前角色,仅 owner/admin 继续。按群串行,对成功 proposal 去重,保留身份、连接代次、proposal base 和 state hash 校验。
|
|
39
|
+
|
|
40
|
+
## 4. Ownership 共识
|
|
41
|
+
|
|
42
|
+
### 4.1 以 NS fresh 检查为权威
|
|
43
|
+
|
|
44
|
+
关系版本和 `lock` 以 NS fresh 响应为准:保留 `X-AUN-Ownership-Check: 1`、禁用缓存及 multi-v1 协商。验证 `agent.md`、Storage `owner.md` 和关系声明,保留原文/摘要、存在性、ETag、30 秒观察窗口及 CAS 检查。
|
|
45
|
+
|
|
46
|
+
### 4.2 冲突时立即重查一次
|
|
47
|
+
|
|
48
|
+
NS 元数据与已验签名片不一致,或 lock 与 owner.md 的 release/缺失状态冲突时,额外 fresh 重查并重验材料;两类冲突共用一次额度。
|
|
49
|
+
|
|
50
|
+
| 重查结果 | 处理方式 |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| NS 与文件状态一致,材料稳定 | 按新鲜 NS 状态继续 |
|
|
53
|
+
| 仍有冲突、材料变化或权威服务不可用 | 返回可重试的 `unavailable`,保留任务和文件 |
|
|
54
|
+
| 签名或关系材料无效 | 返回 `invalid` |
|
|
55
|
+
|
|
56
|
+
流程末尾仍作稳定性复读。NS 仍 locked 而 owner.md 已 release/缺失时返回 `unavailable`;清理、重绑先确认解锁。配套 NS 须在权威 owner.md 缺失或 release 验签且复核稳定时返回 fresh `unlocked`,读取/验签失败返回错误。
|
|
57
|
+
|
|
58
|
+
### 4.3 消息拉取与内部处理
|
|
59
|
+
|
|
60
|
+
P2P 后台拉取要求普通消息订阅(`message.received/recalled/batch`),仅 `ownership.*` 不足。连接前注册或 READY 后补注册均可恢复积压,仍遵守 `background_sync=false`、背压、退避和身份/连接代际。
|
|
61
|
+
|
|
62
|
+
Ownership 走正常 P2P 路径,由 SDK 解密、验签、持久消费;不为它绕过积压、另存普通消息待投递记录或改变提交/ACK 时序。接线见 [应用指南](Ownership2.1-SDK应用集成指南.md#32-两端分别装载自己的身份)。
|
|
63
|
+
|
|
64
|
+
### 4.4 按发起方发布绑定原件
|
|
65
|
+
|
|
66
|
+
两种绑定发起前均须 fresh 确认无锁,`invalid/unavailable` 不能视为无锁;已有锁只能由当前 owner 释放或转移。legacy 任务沿用原恢复路径。
|
|
67
|
+
|
|
68
|
+
| 阶段 | 移植要求 |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| sub 准备 | 持久化本轮审批、目标 owner/版本、完整签名 `agent.md` 及原 If-Match/If-None-Match 后再 PUT |
|
|
71
|
+
| Stage 预检 | 带 Check 的 fresh GET/HEAD 须返回 `X-AUN-Ownership-Stage: 1`;缺失即停止,`Protocol: 2.1` 不能替代 |
|
|
72
|
+
| Stage 提交 | `PUT /agent.md` 带 Stage 头;核对响应同一能力头、`X-AUN-Ownership-Status: pending`、`Lock: unlocked` 及候选版本 |
|
|
73
|
+
| pending 映射 | 公共共识为 `single_sided`、`owner_aid=null`,不新增公共状态,不按 released 或有效 owner 授权 |
|
|
74
|
+
| owner 同意 | 固定 pending 原文/摘要/ETag,再签发匹配本轮的 owner declare;旧 owner/版本文件不能代替审批 |
|
|
75
|
+
| 验收 | owner 等检查方触发 NS fresh,核验两份原件后清 pending、建立锁并同步副本;sub 可离线,无须再次 PUT/重签 |
|
|
76
|
+
| sub-first 拒绝 | 仅条件清理本轮原文/摘要/ETag 匹配且仍 pending/unlocked 的候选;预存清理签字节和原 CAS,丢响应复用,候选变化或已验收即停止 |
|
|
77
|
+
| owner-first | 持久化签名 owner.md/原 Storage 条件 → 条件发布确认 → 发 `bind.request`;sub fresh 验证、明确同意后持久化 agent.md/原 NS 条件,直接 PUT;不用 Stage,无 bind.accept/owner.ready 往返,NS fresh 验收 |
|
|
78
|
+
| owner-first 拒绝 | sub 不上传;owner fresh 确认无锁及原件匹配,以最后读取并核对的当前 `version` 作 `expected_version` 删除邀请;版本冲突停止 |
|
|
79
|
+
|
|
80
|
+
checkpoint 区分起始原件与候选,不能仅凭 owner/数字版本复用审批。reset 仅归档本端发起的未完成任务,不删远端候选、不解锁;新申请重新审批,真实 released 关系先清旧字段。本地 revision、NS 412、Storage `-32009` 分别按 [CAS 恢复边界](Ownership2.1-SDK开发与移植指南.md#103-cas-冲突分类与恢复边界) 处理,不换条件重试旧原件。
|
|
81
|
+
|
|
82
|
+
NS 的 `ownership_pending` 与名片 CAS 一起持久化;pending 名片是当前权威原件,Storage 副本在 fresh 验收后同步。协议见 [读取、并发与接口](../protocol/Ownership协议定义2.1.md#33-读取并发与接口)。
|
|
83
|
+
|
|
84
|
+
## 5. 凭证撤销与成员续期
|
|
85
|
+
|
|
86
|
+
### 5.1 验证并合并推送
|
|
87
|
+
|
|
88
|
+
将 `credential.revoked`、`credential.revocationList` 合并到持久撤销快照:
|
|
89
|
+
|
|
90
|
+
- 验证服务签名、authority、接收身份、有效时间和撤销材料。
|
|
91
|
+
- 按 authority/CID 保留最大 `event_seq`,推送与分页并发合并,不覆盖新记录。
|
|
92
|
+
- 异步验证/持久化前后核对身份和连接代次,丢弃旧结果;用于完整凭证和父证明链验证。
|
|
93
|
+
|
|
94
|
+
四端使用 tokenstore/keystore 的 `credential_revocations_v1` metadata(浏览器 IndexedDB),按 authority 保存 CID 序号、分页 checkpoint 和证明时间窗。它不是 JSONL 流水,缺少 CID 不证明有效;结构见 [撤销快照](凭证与组织服务SDK开发与移植指南.md#54-撤销同步与持久快照0522)。
|
|
95
|
+
|
|
96
|
+
### 5.2 保持分页连续
|
|
97
|
+
|
|
98
|
+
单条推送只更新 CID,保留 `since_event_seq`/cursor。已验证列表页覆盖当前 checkpoint 时才前推,迟到页不回退。
|
|
99
|
+
|
|
100
|
+
### 5.3 每小时补拉
|
|
101
|
+
|
|
102
|
+
连接一小时后首次补拉,此后每小时执行;重连重新计时,断线/身份变化取消旧调度。与 Ownership、长期凭证续期等每分钟任务分开调度。
|
|
103
|
+
|
|
104
|
+
### 5.4 同步保存成员续期结果
|
|
105
|
+
|
|
106
|
+
续期成功立即保存凭证、绑定、条款、登记回执、服务端版本及权威信息,供 `credential.current` 读取完整成员包,不等待事件。仅更新当前成员账户;`long_term=false` 跳过后台及写前自动续期。保留加密存储、CAS、历史和防回滚检查。
|
|
107
|
+
|
|
108
|
+
### 5.5 配套 Credential / Org 服务修复
|
|
109
|
+
|
|
110
|
+
部署依赖:
|
|
111
|
+
|
|
112
|
+
- `credential.revoke` 按原 request ID/参数摘要恢复持久结果,校验 CID 和 revoked 状态;冲突/损坏不能返回成功。
|
|
113
|
+
- 创建补偿遇 `CREDENTIAL_IDEMPOTENCY_KEY_REUSE` 持久保存冲突并停止,不换 request ID 重试。
|
|
114
|
+
- 转让换钥前复核证书/公钥代际,新公钥用规范 DER Base64;已有 checkpoint 恢复原请求。
|
|
115
|
+
- 原子保存新旧 owner 成员包与定向 `org.member.changed` 通知;失败通知保留待办并按组织轮转。SDK 仍验完整包,续期保存不依赖通知。
|
|
116
|
+
|
|
117
|
+
详见 [Credential RPC](09-凭证服务RPC手册.md#credentialrevoke)、[Org RPC](09-组织服务RPC手册.md#组织生命周期)及 [服务实现要求](凭证与组织服务SDK开发与移植指南.md#7-服务端兼容实现要点)。
|
|
118
|
+
|
|
119
|
+
## 6. 默认读缓存、语言专项修复与版本
|
|
120
|
+
|
|
121
|
+
### 6.1 Storage / Group FS 默认读缓存
|
|
122
|
+
|
|
123
|
+
同时接入读取入口、后端和失效机制,元数据/正文分别判定:
|
|
124
|
+
|
|
125
|
+
| 条件 | 移植要求 |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| 无 `read_cache`,或为 `null` | 元数据默认 60 秒;完整正文实际 ≤65,536 字节且 SHA-256 校验通过时默认缓存 |
|
|
128
|
+
| 显式策略 | 须 `reusable: true`,正文另须 `inline: true`;省略 `max_age_ms` 默认 60 秒,有限正数最多 60 秒;显式禁止、空对象、null/非正数/非法 TTL 不复用 |
|
|
129
|
+
| 单次刷新 | TS/JS `noCache: true`、Python `no_cache=True`、Go Options `NoCache: true` 强制回源;TS/JS 底层 RPC/Group FS 兼容 `no_cache`,选项不传为业务字段 |
|
|
130
|
+
| 条件读取 | 过期/刷新可带 `if_sha256`;`not_modified` 须与已有已验证完整正文摘要匹配,无正文不能返回空文件。校验不受后续缓存许可影响;结果仍可缓存,刷新不是 no-store |
|
|
131
|
+
| 权威/特殊读取 | 支持 `consistency: "authoritative"` 的底层 RPC、范围及显式预签读取旁路缓存 |
|
|
132
|
+
| `owner.md` | 普通读默认缓存;Ownership 管理/fresh 共识读权威原件。resolve 展示快照独立维护,不能授权 |
|
|
133
|
+
|
|
134
|
+
有限次授权的 inline 读取仅返回正文时计次;本地命中、HEAD、无正文 `not_modified` 不计次,额度耗尽仍可匹配 SHA 条件验证。大对象按 URL 签发/授权 302 计次,同 URL 有效期内为一次授权,不统计 OSS GET;联网仍核验撤销、过期、身份及权限。服务端 TTL 扣除元数据年龄和解析耗时,累计新鲜度不超过 60 秒及对象/授权有效期。
|
|
135
|
+
|
|
136
|
+
#### 持久层与容量
|
|
137
|
+
|
|
138
|
+
索引与正文分离,不迁入业务数据、tokenstore 或 Ownership 任务:
|
|
139
|
+
|
|
140
|
+
| SDK | 缓存后端 |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| TypeScript / Python / Go | `{aunPath}/.cache/storage/index.sqlite3`(WAL):`cache_entries` 存元数据/验证/过期时间,`cache_blobs` 存摘要/大小/访问时间;正文为 `blobs/{sha256前2位}/{sha256}` |
|
|
143
|
+
| 浏览器 JavaScript | `aun-keystore` IndexedDB 的 `storage_cache_index`/`storage_cache_blobs` |
|
|
144
|
+
|
|
145
|
+
默认预算:内存/持久正文 32/256 MiB、元数据 16 MiB,索引 999/9,999 条;正文按最近访问淘汰,索引按容量/时间淘汰。热命中、重启不续期,重启核验摘要。后端故障、正文丢失/损坏时降级内存或联网,不解释为远端不存在。
|
|
146
|
+
|
|
147
|
+
#### 隔离、失效与新鲜度
|
|
148
|
+
|
|
149
|
+
- 键含实际身份、网关、RPC 方法和规范化参数,隔离范围/token/share/ACL;排除刷新选项、条件摘要和重签材料。
|
|
150
|
+
- 合并同键冷读;写删、移动、ACL/token/share 和相关群权限变更使缓存失效。失效或身份/连接换代前的旧结果不得填回。
|
|
151
|
+
- 仅存可复用元数据及已验证正文,正文记录去掉下载 URL、临时票据、认证 token 和签名;下载时重新申请票据。
|
|
152
|
+
- 热命中不联网,远端变化到刷新/TTL 到期才被观察;管理/授权仍用权威入口。
|
|
153
|
+
|
|
154
|
+
仅缓存读取白名单;四端 `storage.list_share_links/check_access/list_tokens` 始终回源且不使其它缓存失效。响应契约见 [Storage 手册](09-storage-rpc-manual.md#sdk-默认读缓存),跨端验证见 [parity 块⑦](../../python/CHANGES_FOR_PARITY.md#块⑦-storage--group-fs-默认读缓存)。
|
|
155
|
+
|
|
156
|
+
### 6.2 语言专项修复与版本
|
|
157
|
+
|
|
158
|
+
| 适用端 | 需要完成的修改 |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| Python 共享身份目录 | 非 owner 跳过群恢复保留有效证书;owner 清除误失效标记前核对名片 owner、原证书指纹及现有私钥 |
|
|
161
|
+
| Python 名片 HEAD | Gateway 404 后回源 NS,再判缺失 |
|
|
162
|
+
| Go 存储错误 | `MapStorageError` 穿透 AUNError/包装错误,保留 Code/Data、not-found、retry_safe |
|
|
163
|
+
| Python 组织转让 | 原候选私钥丢失时保留 checkpoint,返回 `persistence_unavailable`,不生成替代密钥 |
|
|
164
|
+
| Go 组织转让 | 导入前核对证书指纹与 CA 的 `entity_cert_fingerprint` |
|
|
165
|
+
| TS/JS 组织转让 | 保存 completed,同 owner/转让 ID/原参数可重放;重连跳过已完成任务 |
|
|
166
|
+
| TS/JS 证书缓存 | `cleanExpiredCaches` 遵循 `chainCacheTtl`(默认 86,400 秒);JS PKI CRL/OCSP 用秒比较,与 Credential 小时补拉分离 |
|
|
167
|
+
| TS 当前实体证书 | 验证本次 PKI current 响应及新鲜元数据,不另按指纹取响应替代 |
|
|
168
|
+
| Python 文件展示 | CLI `fs cat` 无 Content-Type 且 ≤64 KiB 时尝试 UTF-8,失败回退二进制 |
|
|
169
|
+
|
|
170
|
+
发布同步包、运行时、测试/项目配置及 V2 envelope 的版本为 `0.5.22`,保留历史文档版本。
|
|
171
|
+
|
|
172
|
+
## 7. Rust 移植清单
|
|
173
|
+
|
|
174
|
+
路径以 `acp-app/aun-so/rust/` 为根;复用现有分页退避、Pull Gate、群在途控制和验签。缓存按 §6.1 实现,其它定位如下:
|
|
175
|
+
|
|
176
|
+
| 项目 | 需要完成的修改 | 代码定位 |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| 群错误/hint | 按 §2 保留 retryable、退避及群终态联动 | `crates/aun-auth/src/short_rpc.rs`:service_rpc_error;`crates/aun-client/src/actor.rs`:cleanup_failed_rpc、Tail demand |
|
|
179
|
+
| 群状态/提案 | 按 §3 检查状态、角色并去重 | `crates/aun-client/src/actor.rs`:群恢复、handle_group_v2_state_proposed |
|
|
180
|
+
| Ownership | 按 §4 接入 fresh、Stage、原件/CAS、pending 及两种绑定恢复 | `crates/aun-sdk/src/ownership_v21_workflow.rs` |
|
|
181
|
+
| 撤销推送 | 按 §5.1–5.2 验签、合并快照和分页 | `crates/aun-sdk/src/client.rs`:spawn_router;`credential_org.rs`:sync_revocations/快照 |
|
|
182
|
+
| 小时补拉 | 按 §5.3 独立调度 | `crates/aun-sdk/src/ownership_v21_backend.rs`:spawn_worker |
|
|
183
|
+
| 转让证书 | 导入前核对 `entity_cert_fingerprint` | `crates/aun-sdk/src/org_lifecycle.rs`:complete_org_transfer |
|
|
184
|
+
| 成员续期 | 按 §5.4 保存完整包、跳过短期、不误写组织账户 | `crates/aun-sdk/src/org_workflows.rs`:execute_org_workflow、renew_due_org_memberships、ensure_org_membership_fresh;`org_membership_package.rs`;`credential_org.rs`:current |
|
|
185
|
+
| 发布版本 | 同步 crate 与 `aun_types::SDK_VERSION`,envelope 复用常量 | `Cargo.toml`;`crates/aun-types/src/lib.rs` |
|
|
186
|
+
|
|
187
|
+
保留 Rust 现有私钥丢失保护、completed 重放及直连 AID 名片读取。跨域发布/恢复用上传者所属域 token,并在该域刷新,由目标 NS 权威校验;保留原 pending/CAS。Python 共享目录和 Go 错误链修复仅在有对应路径时采用。
|
|
188
|
+
|
|
189
|
+
## 8. 移植与验收顺序
|
|
190
|
+
|
|
191
|
+
| 顺序 | 验收要求 |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| 后台/群 | hint 不重置退避,前台可继续;终态联动停止和恢复;非 active 跳过,当前角色及 proposal 去重 |
|
|
194
|
+
| Ownership | 一次冲突重查、Stage 预检/响应、两种绑定、sub 离线、精确拒绝清理、原件变化、三类 CAS、丢响应、reset、晚订阅/背压 |
|
|
195
|
+
| 凭证组织 | 乱序合并、分页连续、旧代次丢弃、小时补拉;缺私钥/指纹不符保留现场;完成重放、无事件续期包、短期跳过 |
|
|
196
|
+
| 缓存 | 缺省及显式策略、65,536 字节边界、摘要/刷新/条件复用、失效、重启、淘汰、故障降级、三类查询回源、普通/权威 owner.md |
|
|
197
|
+
| 专项与互操作 | 对应语言修复、版本一致、NS 解锁投影及群错误行为 |
|
|
198
|
+
|
|
199
|
+
测试入口与已执行范围见 [parity](../../python/CHANGES_FOR_PARITY.md)。缓存专项为 [16×4 矩阵](../../tests/cross-sdk/cases.storage-cache.json)/[runner](../../tests/cross-sdk/runner/run_storage_cache_e2e.py)、[Python E2E](../../python/tests/e2e_test_storage_cache.py)、[浏览器](../../js/tests/e2e-browser/storage.spec.ts)。联网按 [单域](../aun测试运行指南.md)/[公网](../aun公网测试运行指南.md)/[双域](../aun分布式测试运行指南.md) 执行,首失败修复后从该项续跑。
|
|
@@ -87,7 +87,7 @@ async def main():
|
|
|
87
87
|
try:
|
|
88
88
|
await asyncio.wait_for(received.wait(), timeout=5)
|
|
89
89
|
except asyncio.TimeoutError:
|
|
90
|
-
pull = await bob.call("message.pull", {"
|
|
90
|
+
pull = await bob.call("message.pull", {"sender_aid": ALICE, "limit": 200})
|
|
91
91
|
for msg in pull.get("messages", []):
|
|
92
92
|
print(f"[{ts()}] Bob 拉取: {msg.get('payload')}")
|
|
93
93
|
|
|
@@ -124,7 +124,8 @@ sequenceDiagram
|
|
|
124
124
|
"group_e2ee": true,
|
|
125
125
|
"supported_p2p_e2ee": ["e2ee_v2"],
|
|
126
126
|
"supported_group_e2ee": ["group_e2ee_v2"],
|
|
127
|
-
"inline_realtime_payload_v1": true
|
|
127
|
+
"inline_realtime_payload_v1": true,
|
|
128
|
+
"p2p_peer_pull_v1": true
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
```
|
|
@@ -136,6 +137,9 @@ sequenceDiagram
|
|
|
136
137
|
| `supported_p2p_e2ee` | 明确支持的 P2P 加密版本列表 |
|
|
137
138
|
| `supported_group_e2ee` | 明确支持的群组加密版本列表 |
|
|
138
139
|
| `inline_realtime_payload_v1` | **支持实时推送内联消息载荷**(0.5.6+ 新增) |
|
|
140
|
+
| `p2p_peer_pull_v1` | 支持共享原 seq、按完整 sender 隔离 Pull/ACK 游标及 pending peer 目录;必须为布尔值 `true`,字符串/数字不生效 |
|
|
141
|
+
|
|
142
|
+
新 SDK 自动声明 `p2p_peer_pull_v1`,不兼容旧服务端;新服务端同时服务未声明能力的旧 SDK。Gateway 将该能力绑定到认证连接并传入可信上下文,按 peer 隔离 Push/ACK 通知,上线提示由 SDK 查询 `message.peers` 后恢复同步。能力属于当前连接,不需要收件人在线时才记录消息。当前 peer 模式只支持单 Message 服务实例,多实例支持留待 AUN 分布式 2.0;完整 API 见 [Message RPC](09-message-rpc-manual.md#messagepeers)。
|
|
139
143
|
|
|
140
144
|
### inline_realtime_payload_v1 详解
|
|
141
145
|
|
|
@@ -36,50 +36,50 @@ assert me.is_private_key_valid()
|
|
|
36
36
|
| `AID` | 身份值对象,负责签名、验签、agent.md 签验 | 否 |
|
|
37
37
|
| `AUNClient` | 会话对象,负责认证、连接、重连、事件和 RPC | 是 |
|
|
38
38
|
|
|
39
|
-
`AUNClient` 不再通过配置字典持有某个字符串 AID;它只接收已加载并校验过私钥的 AID 对象。
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## device_id 与 slot_id
|
|
44
|
-
|
|
45
|
-
`device_id` 是设备级稳定标识,默认写在 `{aun_path}/.device_id`,用于 token、实例状态、V2 设备密钥和消息游标的一级隔离。它是单段安全 token,只允许字母、数字、`.`、`_`、`-`。
|
|
46
|
-
|
|
47
|
-
`slot_id` 是同一设备下的连接/消费槽位,允许用 `/`、`:`、空格表达共享隔离键。隔离键取第一个分隔符前的部分:
|
|
48
|
-
|
|
49
|
-
| slot_id | slotIsolationKey |
|
|
50
|
-
|---------|------------------|
|
|
51
|
-
| `evolclaw daemon` | `evolclaw` |
|
|
52
|
-
| `evolclaw cli` | `evolclaw` |
|
|
53
|
-
| `evolclaw/netcheck` | `evolclaw` |
|
|
54
|
-
| `evolclaw-daemon` | `evolclaw-daemon` |
|
|
55
|
-
|
|
56
|
-
SDK 在 `message.pull` / `message.ack` 中自动注入当前 `device_id` / `slot_id`,并按隔离键校验显式传入的 `slot_id`。因此共享消费槽位时应使用 `/`、`:` 或空格作为分隔符。
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## 消息实时窗口与历史拉取
|
|
61
|
-
|
|
62
|
-
P2P Message
|
|
63
|
-
|
|
64
|
-
| 水位 | 含义 |
|
|
65
|
-
|------|------|
|
|
66
|
-
| `A / ack` | Forward
|
|
67
|
-
| `T / tail` | Tail 已扫描实时窗口的左边界 |
|
|
68
|
-
| `H / head` | Tail 已扫描实时窗口的右边界 |
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
冷启动缺少发送方 IK 时,SDK 会将同发送端请求 single-flight 合并,最多同步等待 3 秒执行 bootstrap;成功后立即重试当前消息解密,失败或超时才转入 pending
|
|
75
|
-
|
|
76
|
-
每个 `AUNClient`
|
|
77
|
-
|
|
78
|
-
A/T/H
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## 连接状态机
|
|
39
|
+
`AUNClient` 不再通过配置字典持有某个字符串 AID;它只接收已加载并校验过私钥的 AID 对象。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## device_id 与 slot_id
|
|
44
|
+
|
|
45
|
+
`device_id` 是设备级稳定标识,默认写在 `{aun_path}/.device_id`,用于 token、实例状态、V2 设备密钥和消息游标的一级隔离。它是单段安全 token,只允许字母、数字、`.`、`_`、`-`。
|
|
46
|
+
|
|
47
|
+
`slot_id` 是同一设备下的连接/消费槽位,允许用 `/`、`:`、空格表达共享隔离键。隔离键取第一个分隔符前的部分:
|
|
48
|
+
|
|
49
|
+
| slot_id | slotIsolationKey |
|
|
50
|
+
|---------|------------------|
|
|
51
|
+
| `evolclaw daemon` | `evolclaw` |
|
|
52
|
+
| `evolclaw cli` | `evolclaw` |
|
|
53
|
+
| `evolclaw/netcheck` | `evolclaw` |
|
|
54
|
+
| `evolclaw-daemon` | `evolclaw-daemon` |
|
|
55
|
+
|
|
56
|
+
SDK 在 `message.pull` / `message.ack` 中自动注入当前 `device_id` / `slot_id`,并按隔离键校验显式传入的 `slot_id`。因此共享消费槽位时应使用 `/`、`:` 或空格作为分隔符。
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 消息实时窗口与历史拉取
|
|
61
|
+
|
|
62
|
+
P2P Message 按完整 sender 隔离消费游标,Group Message 沿用原群游标;各自维护三个本地持久化水位,P2P 的 seq 仍使用接收设备共享原序号:
|
|
63
|
+
|
|
64
|
+
| 水位 | 含义 |
|
|
65
|
+
|------|------|
|
|
66
|
+
| `A / ack` | Forward 已提交的前缀水位;peer 同时要求覆盖证明和实际交付,只有 A 允许提交 ACK |
|
|
67
|
+
| `T / tail` | Tail 已扫描实时窗口的左边界 |
|
|
68
|
+
| `H / head` | Tail 已扫描实时窗口的右边界 |
|
|
69
|
+
|
|
70
|
+
P2P 上线或 Push 提示先使 pending peer 目录重新检查,随后按 peer 并发同步;只有已验证自身 `agent.md.type=human` 时自动优先 Tail 再 Forward,其他或未知类型直接 Forward。peer 原 seq 可以稀疏,SDK 根据 `scan_after_seq` / `covered_through_seq` / `next_after_seq` / 固定 `head_seq` 维护覆盖,不通过相邻数字推断 Gap;推送可用 `prev_peer_seq` 判断是否与本地前缀相接。Group 保留原 Tail/Forward 与页内永久空号规则。
|
|
71
|
+
|
|
72
|
+
peer 水位由通过校验的覆盖证明与实际交付共同决定:未解密或应用回调未成功的消息不能被 A/ACK 越过;运行中的服务端 ACK 不能代替本地交付。合法 retention floor 可解释永久不可取得的前缀。首次 peer 初始化使用服务端冻结的旧 ACK 基线,之后不维护旧 global A/T/H。History 只解密返回,不修改实时 A/T/H、不 ACK、也不触发实时消息事件;Group 的原终态规则保持不变。
|
|
73
|
+
|
|
74
|
+
冷启动缺少发送方 IK 时,SDK 会将同发送端请求 single-flight 合并,最多同步等待 3 秒执行 bootstrap;成功后立即重试当前消息解密,失败或超时才转入 pending 后台重试,peer 不会仅凭扫描区间跳过这条未交付消息。
|
|
75
|
+
|
|
76
|
+
每个 `AUNClient` 只有一个网络 Pull Gate。四 SDK 的 P2P Message、Group Message、Group Event 共用 N=4;同 namespace 单飞,不同 namespace 最多 4 个网络请求并行。P2P namespace 为 `p2p.peer.v1/` 加完整 sender UTF-8 的无 padding base64url;群沿用 `group:{群 ID}`、`group_event:{群 ID}`。相同请求 key 折叠,可运行前台 FIFO 优先于后台 FIFO;同 namespace 串行与共享 N 名额分开维护;每页响应或错误返回即释放共享名额,producer、本地处理及应用回调不跨页持有名额,本地处理与下一页预取可重叠。同一 client 各 peer 与群的应用消息事件共享 FIFO,排队消息容量由 SDK 内部 `page_size × N` 动态计算,默认 `200 × 4 = 800` 条;不是每个 namespace 各有 800 条,单次请求调小 `limit` 也不改变内部页容量。后台 `max_pages` 只限定工作片,达到 100 页仍需自主续调度到持久/volatile 工作排空,不同 client 不共享 Gate。
|
|
77
|
+
|
|
78
|
+
A/T/H 不是应用业务提交账本。SDK 在当前运行实例内协调 P2P 并发交付,以最多 50,000 条成功绑定记录去重;身份包含接收 AID、设备、slot、事件类别与原正 delivery seq,并校验 sender/message_id,不包含 namespace 或 Push/Pull/Tail 来源。所需应用 handler 全部成功后才完成内存标记,再依据有效 proof 可靠提交页 A/T/H,ACK 只使用该已提交 A。热路径不逐条读写持久 receipt,自定义 store 不要求 receipt 扩展;内存淘汰、重建 client、崩溃或版本切换后允许重放,由应用在业务事务中持久幂等。多个 client/进程无跨实例首次回调 claim;跨 peer 不保证全局回调顺序。History/force/Tail 返回列表不因内存去重删行,History 不进入实时发布;volatile 与 Group 保持各自原去重边界。目录/统计事件见 [Message RPC](09-message-rpc-manual.md#messagepeers_snapshot)。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 连接状态机
|
|
83
83
|
|
|
84
84
|
```mermaid
|
|
85
85
|
stateDiagram-v2
|
|
@@ -187,25 +187,25 @@ client.on("state_change", lambda e: print(e["state"]))
|
|
|
187
187
|
client.on("message.received", lambda e: print(e["payload"]))
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
RPC 方法参数见 `09-message-rpc-manual.md`、`09-group-rpc-manual.md`、`09-storage-rpc-manual.md` 等专项手册。
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
## Group Index 观察模型
|
|
195
|
-
|
|
196
|
-
`group.index` 是群公告、群规则、入群要求和附件稳定引用的签名索引。它由 owner/admin 侧 SDK 生成并签名,Group 服务只校验、CAS 保存和在响应 `_meta.group_indexes` 中注入版本提示。
|
|
197
|
-
|
|
198
|
-
关键语义:
|
|
199
|
-
|
|
200
|
-
- `_meta.group_indexes` 只包含 `etag`、`last_modified`、`schema`,不包含 index 正文。
|
|
201
|
-
- SDK 观察到远端 `etag` 变化后只记录观察到的远端版本;etag 不一致只表示本地与远端不同步,不表示远端一定更新,也不表示本地一定应被覆盖。
|
|
202
|
-
- `getAnnouncement` / `getRules` / `getJoinRequirements` 只返回 SDK 本地缓存;本地没有对应值时才读取相应 settings 初始化缓存,不会因为 etag 不一致自动 pull 远端。
|
|
203
|
-
- 应用层调用 `checkGroupIndex` 判断是否不同步;如选择远端为准,再调用 `getGroupIndex` 从服务端摘取当前 index 并同步本地缓存。
|
|
204
|
-
- owner/admin 修改 indexed settings 时调用 `updateGroupIndex`;SDK 会先读取当前 index,再基于 `expected_index_etag` CAS push 新签名版本。
|
|
205
|
-
- Gateway/Message 不生成、不校验、不更新 `group.index`,最多转发或合并 `_meta`。
|
|
206
|
-
- `getGroupIndex` pull 验签通过后必须持久化本地视图:Python / TypeScript(Node) / Go 写入 `{aun_path}/AIDs/{local_aid}/groups/{group_aid}/index.jsonl` 和同目录 `group-index-cache.json`;浏览器 JavaScript 写入 IndexedDB `group_index_cache`,字段包含 `index_jsonl`、`local_etag`、`remote_meta`、`settings`、`entry_etags`。
|
|
207
|
-
- 本地持久化目录按 `local_aid + group_aid` 隔离,不是 `{aun_path}/AIDs/{group_aid}/`;`index.jsonl` 保存签名 `group.index.body` 原文,SDK cache envelope 不再使用旧单文件 `group-index.json`。
|
|
208
|
-
|
|
209
|
-
这个模型与 `agent.md` 的版本观察机制对齐:先观察变化,再由应用层决定以本地还是远端为准,显式 pull、merge 或 push。
|
|
190
|
+
RPC 方法参数见 `09-message-rpc-manual.md`、`09-group-rpc-manual.md`、`09-storage-rpc-manual.md` 等专项手册。
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Group Index 观察模型
|
|
195
|
+
|
|
196
|
+
`group.index` 是群公告、群规则、入群要求和附件稳定引用的签名索引。它由 owner/admin 侧 SDK 生成并签名,Group 服务只校验、CAS 保存和在响应 `_meta.group_indexes` 中注入版本提示。
|
|
197
|
+
|
|
198
|
+
关键语义:
|
|
199
|
+
|
|
200
|
+
- `_meta.group_indexes` 只包含 `etag`、`last_modified`、`schema`,不包含 index 正文。
|
|
201
|
+
- SDK 观察到远端 `etag` 变化后只记录观察到的远端版本;etag 不一致只表示本地与远端不同步,不表示远端一定更新,也不表示本地一定应被覆盖。
|
|
202
|
+
- `getAnnouncement` / `getRules` / `getJoinRequirements` 只返回 SDK 本地缓存;本地没有对应值时才读取相应 settings 初始化缓存,不会因为 etag 不一致自动 pull 远端。
|
|
203
|
+
- 应用层调用 `checkGroupIndex` 判断是否不同步;如选择远端为准,再调用 `getGroupIndex` 从服务端摘取当前 index 并同步本地缓存。
|
|
204
|
+
- owner/admin 修改 indexed settings 时调用 `updateGroupIndex`;SDK 会先读取当前 index,再基于 `expected_index_etag` CAS push 新签名版本。
|
|
205
|
+
- Gateway/Message 不生成、不校验、不更新 `group.index`,最多转发或合并 `_meta`。
|
|
206
|
+
- `getGroupIndex` pull 验签通过后必须持久化本地视图:Python / TypeScript(Node) / Go 写入 `{aun_path}/AIDs/{local_aid}/groups/{group_aid}/index.jsonl` 和同目录 `group-index-cache.json`;浏览器 JavaScript 写入 IndexedDB `group_index_cache`,字段包含 `index_jsonl`、`local_etag`、`remote_meta`、`settings`、`entry_etags`。
|
|
207
|
+
- 本地持久化目录按 `local_aid + group_aid` 隔离,不是 `{aun_path}/AIDs/{group_aid}/`;`index.jsonl` 保存签名 `group.index.body` 原文,SDK cache envelope 不再使用旧单文件 `group-index.json`。
|
|
208
|
+
|
|
209
|
+
这个模型与 `agent.md` 的版本观察机制对齐:先观察变化,再由应用层决定以本地还是远端为准,显式 pull、merge 或 push。
|
|
210
210
|
|
|
211
211
|
|
|
@@ -62,7 +62,7 @@ store = AIDStore(
|
|
|
62
62
|
| `register(aid)` | 注册新 AID,返回 Result |
|
|
63
63
|
| `list()` | 列出本地身份 |
|
|
64
64
|
| `exists(aid)` | HEAD PKI 端点,判断 AID 是否已注册 |
|
|
65
|
-
| `resolve(aid, opts=None)` |
|
|
65
|
+
| `resolve(aid, opts=None)` | 解析身份与 agent.md,返回结构化 frontmatter;自有私钥身份优先本地草稿/缓存,可传 `{"skip_agent_md": True}` 跳过名片,或 `{"ownership": client}` 按需查询并缓存共识和 owner.md |
|
|
66
66
|
| `download_agent_md(aid)` | 下载并验签对端 agent.md |
|
|
67
67
|
| `check_agent_md(aid, ttl_days=1)` | 检查本地缓存与远端 agent.md 状态 |
|
|
68
68
|
| `diagnose(aid)` | 本地和远端一致性诊断 |
|
|
@@ -235,9 +235,12 @@ uploaded = await store.upload_agent_md("alice.agentid.pub", content)
|
|
|
235
235
|
```python
|
|
236
236
|
resolved = await store.resolve("bob.agentid.pub")
|
|
237
237
|
if resolved["ok"]:
|
|
238
|
+
print(resolved["data"]["agent_md"]["frontmatter"].get("name"))
|
|
238
239
|
print(resolved["data"]["agent_md"]["verification"]["status"])
|
|
239
240
|
```
|
|
240
241
|
|
|
242
|
+
frontmatter 保留原文的标准字段、扩展字段及其类型,历史名片缺字段不补默认值。需要 ownership 时传入已有 client:`await store.resolve("bob.agentid.pub", {"ownership": client})`,读取 `data.ownership.status`;复用既有双文件共识校验,默认缓存 60 秒,包含读取到的 owner.md 原文。`ownership_ttl_ms=0` 禁止复用,`force_refresh=True` 同时绕过共识缓存;缓存不用于授权。完整 opts、字段、状态及四语言差异见 [resolve 输入与输出](06-API手册.md#resolve-输入与输出)。
|
|
243
|
+
|
|
241
244
|
只下载对端 agent.md:
|
|
242
245
|
|
|
243
246
|
```python
|
|
@@ -41,20 +41,22 @@ await client.call("message.send", {
|
|
|
41
41
|
})
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
当前 SDK 使用 E2EE V2 多设备 wrap 主路径:
|
|
45
|
-
|
|
46
|
-
1. 发送前通过 `message.v2.bootstrap` 或 `group.v2.bootstrap` 获取接收方当前活跃设备、设备 prekey、self-sync 设备和 audit recipients。
|
|
47
|
-
2. SDK 为每条消息生成独立 `master_key`、消息 nonce 和发送方临时 session key,只加密一次正文。
|
|
48
|
-
3. SDK 为每个接收设备生成一条 recipient wrap。设备有可用 SPK 时使用 `3DH` wrap;缺少 SPK 的兼容场景才使用 `1DH` wrap。
|
|
49
|
-
4. E2EE 信封包含 `sender_signature`、AAD、`recipients_digest` / Merkle proof,接收端必须验签、验 AAD、验 recipient proof 后再解密。
|
|
50
|
-
|
|
51
|
-
每条消息独立密钥;V2 当前主路径不再使用旧 `prekey_ecdh_v2` / `long_term_key` 信封作为默认发送格式。旧术语只用于历史兼容文档或迁移排查。
|
|
52
|
-
|
|
53
|
-
接收端校验签名需要发送方 IK。若本地 session 和 PKI 证书都没有目标 sender/device 的 IK,Python、TypeScript、Go、JavaScript SDK 会先给当前消息一次有界同步补救机会:同一发送端的并发请求通过 single-flight 合并,最多阻塞 3 秒调用 `message.v2.bootstrap`;群消息仍未命中时再调用 `group.v2.bootstrap`。获取成功后立即重试当前消息解密;获取失败或超时才进入原有 pending 后台重试,History 则返回结构化解密失败项而不进入实时 pending。该补救会增加少量首条消息延迟,但避免冷启动时“消息已 ACK、IK 稍后才到、当前消息无回调”。
|
|
54
|
-
|
|
55
|
-
同步获取失败意味着当前时点仍不具备验签和解密条件,SDK
|
|
56
|
-
|
|
57
|
-
## ProtectedHeaders 与可验证上下文
|
|
44
|
+
当前 SDK 使用 E2EE V2 多设备 wrap 主路径:
|
|
45
|
+
|
|
46
|
+
1. 发送前通过 `message.v2.bootstrap` 或 `group.v2.bootstrap` 获取接收方当前活跃设备、设备 prekey、self-sync 设备和 audit recipients。
|
|
47
|
+
2. SDK 为每条消息生成独立 `master_key`、消息 nonce 和发送方临时 session key,只加密一次正文。
|
|
48
|
+
3. SDK 为每个接收设备生成一条 recipient wrap。设备有可用 SPK 时使用 `3DH` wrap;缺少 SPK 的兼容场景才使用 `1DH` wrap。
|
|
49
|
+
4. E2EE 信封包含 `sender_signature`、AAD、`recipients_digest` / Merkle proof,接收端必须验签、验 AAD、验 recipient proof 后再解密。
|
|
50
|
+
|
|
51
|
+
每条消息独立密钥;V2 当前主路径不再使用旧 `prekey_ecdh_v2` / `long_term_key` 信封作为默认发送格式。旧术语只用于历史兼容文档或迁移排查。
|
|
52
|
+
|
|
53
|
+
接收端校验签名需要发送方 IK。若本地 session 和 PKI 证书都没有目标 sender/device 的 IK,Python、TypeScript、Go、JavaScript SDK 会先给当前消息一次有界同步补救机会:同一发送端的并发请求通过 single-flight 合并,最多阻塞 3 秒调用 `message.v2.bootstrap`;群消息仍未命中时再调用 `group.v2.bootstrap`。获取成功后立即重试当前消息解密;获取失败或超时才进入原有 pending 后台重试,History 则返回结构化解密失败项而不进入实时 pending。该补救会增加少量首条消息延迟,但避免冷启动时“消息已 ACK、IK 稍后才到、当前消息无回调”。
|
|
54
|
+
|
|
55
|
+
同步获取失败意味着当前时点仍不具备验签和解密条件,SDK 不伪造明文。P2P peer 路径不能仅凭扫描证明越过未成功解密或交付的消息,永久坏密文同样不推进该处 A/ACK;Group 保持原有终态规则。实时消息通过 `message.undecryptable` / `group.message_undecryptable` 暴露诊断;应用仍需按消息业务身份幂等处理后续 pending 重试可能产生的补交付。
|
|
56
|
+
|
|
57
|
+
## ProtectedHeaders 与可验证上下文
|
|
58
|
+
|
|
59
|
+
`message.send` / `group.send` 的可选 `need_render` 默认 `true`,由 SDK 提取到外层信封;缺字段及历史 NULL 也按 `true`。它是展示/统计提示,不进入 AAD、`protected_headers`、消息保护签名或元数据 HMAC。RPC `client_signature` 仍按既有完整业务参数规则覆盖它,不能因为不进 AAD 就从 RPC canonical 中排除。
|
|
58
60
|
|
|
59
61
|
`protected_headers` 是 E2EE 信封里的可选元数据字典,语义接近 HTTP headers:适合放 `device_id`、`slot_id`、`device_name`、`os`、`sdk_version`、`app_name` 等需要被接收端识别、且需要防篡改的非业务内容。`payload` 仍然类似 HTTP body,业务内容应放在 `payload` 内。
|
|
60
62
|
|
|
@@ -69,11 +71,11 @@ await client.call("message.send", {
|
|
|
69
71
|
|
|
70
72
|
`payload_type` 不需要应用层传入。SDK 会读取加密前 `payload.type`,自动写入 `protected_headers.payload_type`,接收端解密后会校验它与明文 `payload.type` 一致。
|
|
71
73
|
|
|
72
|
-
`protected_headers` / `headers` 是 send/thought 参数的顶层字段,不放入单独的 `envelope` 入参对象,也不属于业务 `payload`。裸 WebSocket 客户端若自行发送已加密信封,需要把 protected headers 放在自构造的 E2EE 信封内并自行完成 `_auth`,服务端不会替裸 RPC 调用生成或校验明文侧的 protected headers。
|
|
73
|
-
|
|
74
|
-
`agent_md` 是独立于 E2EE 的版本提示元数据,不属于 `protected_headers`,也不参与 AAD 或业务鉴权。当前 Message Service V2 P2P 信封可携带 `agent_md.sender`;SDK 也识别 `agent_md.group`。Gateway 在 RPC response / event push 的 `_meta.agent_md_etags` 中注入 `requester`、`peer`、`group`,四端 SDK 会自动写入对应 AID 的 `remote_etag` / `last_modified`,后续仍以下载后的 agent.md 签名验证作为可信依据。
|
|
75
|
-
|
|
76
|
-
示例:
|
|
74
|
+
`protected_headers` / `headers` 是 send/thought 参数的顶层字段,不放入单独的 `envelope` 入参对象,也不属于业务 `payload`。裸 WebSocket 客户端若自行发送已加密信封,需要把 protected headers 放在自构造的 E2EE 信封内并自行完成 `_auth`,服务端不会替裸 RPC 调用生成或校验明文侧的 protected headers。
|
|
75
|
+
|
|
76
|
+
`agent_md` 是独立于 E2EE 的版本提示元数据,不属于 `protected_headers`,也不参与 AAD 或业务鉴权。当前 Message Service V2 P2P 信封可携带 `agent_md.sender`;SDK 也识别 `agent_md.group`。Gateway 在 RPC response / event push 的 `_meta.agent_md_etags` 中注入 `requester`、`peer`、`group`,四端 SDK 会自动写入对应 AID 的 `remote_etag` / `last_modified`,后续仍以下载后的 agent.md 签名验证作为可信依据。
|
|
77
|
+
|
|
78
|
+
示例:
|
|
77
79
|
|
|
78
80
|
```python
|
|
79
81
|
from aun_core import ProtectedHeaders
|
|
@@ -109,8 +111,8 @@ await client.call("group.send", {
|
|
|
109
111
|
|
|
110
112
|
```json
|
|
111
113
|
{
|
|
112
|
-
"type": "e2ee.p2p_encrypted",
|
|
113
|
-
"version": "v2",
|
|
114
|
+
"type": "e2ee.p2p_encrypted",
|
|
115
|
+
"version": "v2",
|
|
114
116
|
"ciphertext": "...",
|
|
115
117
|
"protected_headers": {
|
|
116
118
|
"device_id": "dev-123",
|
|
@@ -134,8 +136,8 @@ await client.call("group.send", {
|
|
|
134
136
|
|
|
135
137
|
计算规则:
|
|
136
138
|
|
|
137
|
-
1. 解密流程会得到本条消息的 `master_key`。
|
|
138
|
-
2. `metadata_key = HMAC-SHA256(master_key, "aun-envelope-metadata-key-v1")`。
|
|
139
|
+
1. 解密流程会得到本条消息的 `master_key`。
|
|
140
|
+
2. `metadata_key = HMAC-SHA256(master_key, "aun-envelope-metadata-key-v1")`。
|
|
139
141
|
3. 对字典去掉 `_auth` 后做 canonical JSON:UTF-8、key 排序、紧凑分隔符。
|
|
140
142
|
4. `tag = HMAC-SHA256(metadata_key, domain + "\0" + canonical_json(body))`。
|
|
141
143
|
5. `domain` 对 `protected_headers` 为 `aun-protected-headers-v1`,对 `context` 为 `aun-protected-context-v1`。
|
|
@@ -292,7 +294,7 @@ client.on("message.received", handler)
|
|
|
292
294
|
`message.pull` 返回的消息已自动解密:
|
|
293
295
|
|
|
294
296
|
```python
|
|
295
|
-
result = await client.call("message.pull", {"
|
|
297
|
+
result = await client.call("message.pull", {"sender_aid": "alice.agentid.pub", "limit": 200})
|
|
296
298
|
for msg in result["messages"]:
|
|
297
299
|
if msg.get("encrypted"):
|
|
298
300
|
print(f"加密消息: {msg['payload']}")
|
|
@@ -301,20 +303,21 @@ for msg in result["messages"]:
|
|
|
301
303
|
|
|
302
304
|
---
|
|
303
305
|
|
|
304
|
-
## V2 设备公钥与 SPK 管理
|
|
305
|
-
|
|
306
|
-
连接成功后,SDK 会初始化本设备 V2 session,生成或加载 IK / SPK,并通过 `message.v2.put_peer_pk` 幂等注册当前 P2P 设备 SPK。群组路径会按群生成独立 group SPK,并通过 `group.v2.put_group_pk` 注册。应用层一般无需手动管理这些密钥。
|
|
307
|
-
|
|
308
|
-
当前主路径的要点:
|
|
309
|
-
|
|
310
|
-
- P2P 设备 SPK 的 `key_source` 为 `peer_device_prekey`,由 AID 私钥签名背书。
|
|
311
|
-
- 群内独立 group SPK 的 `key_source` 为 `group_device_prekey`,按规范化后的 `group_aid` 隔离。
|
|
306
|
+
## V2 设备公钥与 SPK 管理
|
|
307
|
+
|
|
308
|
+
连接成功后,SDK 会初始化本设备 V2 session,生成或加载 IK / SPK,并通过 `message.v2.put_peer_pk` 幂等注册当前 P2P 设备 SPK。群组路径会按群生成独立 group SPK,并通过 `group.v2.put_group_pk` 注册。应用层一般无需手动管理这些密钥。
|
|
309
|
+
|
|
310
|
+
当前主路径的要点:
|
|
311
|
+
|
|
312
|
+
- P2P 设备 SPK 的 `key_source` 为 `peer_device_prekey`,由 AID 私钥签名背书。
|
|
313
|
+
- 群内独立 group SPK 的 `key_source` 为 `group_device_prekey`,按规范化后的 `group_aid` 隔离。
|
|
312
314
|
- SDK 发送前通过 `message.v2.bootstrap` / `group.v2.bootstrap` 获取目标设备集合和当前 SPK;P2P 定向设备未命中缓存时的单次新鲜重试与失败边界见 [message.send](09-message-rpc-manual.md#messagesend)。
|
|
313
|
-
- 旧 SPK 会在本地保留一段安全窗口,用于解密引用旧 SPK 的历史消息;满足已消费和保留窗口条件后才销毁。
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
315
|
+
- 旧 SPK 会在本地保留一段安全窗口,用于解密引用旧 SPK 的历史消息;满足已消费和保留窗口条件后才销毁。
|
|
316
|
+
- peer 拉取保留原 seq 与历史密文,不更改 AAD。peer ACK 和 slot 级 `legacy_safe_ack_seq` 不是设备全部消费 slot 的安全证明;当前实现可省略 `device_safe_ack_seq`,SDK 不据前两者销毁旧 SPK。
|
|
317
|
+
|
|
318
|
+
裸 WebSocket 客户端如果绕过 SDK,需要自行完成同等的 SPK 生成、AID 私钥签名、注册和 bootstrap 逻辑。旧 `message.e2ee.put_prekey/get_prekey` 只用于 legacy `prekey_ecdh_v2` 信封兼容,不是当前 SDK 的默认路径。
|
|
319
|
+
|
|
320
|
+
SPK 签名里的 `spk_timestamp` 使用 Unix 秒;消息、群事件和服务端 `timestamp` / `created_at` 等主路径时间字段仍使用 Unix 毫秒。
|
|
318
321
|
|
|
319
322
|
---
|
|
320
323
|
|
|
@@ -364,7 +367,7 @@ client = AUNClient(aid)
|
|
|
364
367
|
| P2P 消息默认要求发送方签名 | 无 `sender_signature` 的消息被拒绝 |
|
|
365
368
|
| 群组消息默认要求发送方签名 | `require_signature=True`,无签名或无发送方证书的消息被拒绝 |
|
|
366
369
|
| 群组 E2EE 为固定启用能力 | `group_e2ee=true`,不可关闭 |
|
|
367
|
-
| 默认要求前向保密 | V2 优先使用设备 SPK 的 `3DH` wrap;缺少 SPK 的 `1DH` 路径仅作为兼容降级 |
|
|
368
|
-
| 客户端操作签名 | SDK 会为关键操作附加 `client_signature`;Gateway 对 `send/pull/ack` 等常规 RPC 优先使用连接级身份认证,只有身份声明与连接不一致、敏感操作、能力身份或主动携签场景才执行 ECDSA 验签 |
|
|
370
|
+
| 默认要求前向保密 | V2 优先使用设备 SPK 的 `3DH` wrap;缺少 SPK 的 `1DH` 路径仅作为兼容降级 |
|
|
371
|
+
| 客户端操作签名 | SDK 会为关键操作附加 `client_signature`;Gateway 对 `send/pull/ack` 等常规 RPC 优先使用连接级身份认证,只有身份声明与连接不一致、敏感操作、能力身份或主动携签场景才执行 ECDSA 验签 |
|
|
369
372
|
|
|
370
373
|
|