@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,233 @@
1
+ # Ownership 协议定义 3.0
2
+
3
+ 本文综合[原 Ownership 协议](Ownership协议定义.md)、[Ownership 2.0](Ownership协议定义2.0.md)与 Git 提交 `1f03bf2d` 中的 0.5.18 owner relation 实现,定义新的设计契约,与历史协议并存。SDK 场景见[Ownership SDK编排指南](../sdk/Ownership-SDK编排指南.md)。本文不表示现有服务或 SDK 已实现 3.0,也不直接修改现有 [agent.md Schema](../agent.md/SCHEMA.md)。
4
+
5
+ 关系权威决定谁是当前 owner;双方签名文件提供公开声明和审计证据。绑定、解绑、转移均通过可恢复 operation 完成,文档发布与可选托管分别推进。
6
+
7
+ ## 1. 核心概念与安全边界
8
+
9
+ | 概念 | 定义 |
10
+ | --- | --- |
11
+ | `sub_aid` | 被管理的完整小写 AID。 |
12
+ | `owner_aid` | 当前具有该关系管理授权的完整小写 AID;一条 active 关系只有一个当前 owner。 |
13
+ | 关系权威 | `sub_aid` 所属 issuer 的 Auth/Relation 服务。跨 issuer 的 owner 也在这里提交授权,不产生第二份关系权威。 |
14
+ | `relation_id` | sub_aid 关系记录的稳定标识,小写 UUID v4;解绑、重绑和转移均不更换。 |
15
+ | `relation_version` | 关系记录的单调递增整数;初始为 `0`,每次有效提交加一,解绑后仍保留,不重置。 |
16
+ | `operation_id` | 一次操作的唯一标识,用于授权、幂等、查询和崩溃恢复。 |
17
+ | 声明文档 | sub_aid 的 `agent.md` 与 owner_aid 的 `owner.md`,由各自独立签名、上传。 |
18
+
19
+ 必须遵守:
20
+
21
+ 1. 文件更新、删除、证书更新或 SDK 本地状态都不能单独改变关系权威中的 owner。
22
+ 2. 解绑必须由当前 owner 授权;转移必须由当前 owner、sub_aid、新 owner 共同授权。sub_aid 不能通过删文件、先解绑、重注册或改类型绕过当前 owner。
23
+ 3. 每次提交必须比较当前状态、当前 owner 和 `expected_relation_version`;重试同一 operation 不重复递增版本。
24
+ 4. 所有身份以服务端确认的认证上下文和对应 AID 的证书为准,拒绝客户端伪造 `_auth` 等内部身份字段。
25
+ 5. 不代签、不代上传:双方只能签署、公开写入自己的文件。托管不能自动授予代签、代上传能力。
26
+ 6. 降级只能减少功能,不能减少授权或版本检查。权威不可用时,不能以文件共识或缓存推断当前 owner。
27
+
28
+ 本协议约束 AID 之间的授权关系,不能限制持有同一私钥的程序冒用该 AID。应用把可解密私钥交给 owner 时,已授予其该身份的实际控制能力;移除 SDK 代签接口不能消除这一事实。
29
+
30
+ ## 2. 权威关系与授权条件
31
+
32
+ 关系记录至少包含 `relation_id`、`sub_aid`、`owner_aid`、`relation_version`、`state`、`last_operation_id` 和最近提交回执。`owner_aid` 在无 owner 时为 `null`。
33
+
34
+ | 权威状态 | 含义 |
35
+ | --- | --- |
36
+ | `unbound` | 没有当前 owner,可以建立绑定;仍保留关系标识和历史版本。 |
37
+ | `active` | 存在一个已授权的当前 owner。 |
38
+
39
+ 等待、失败、过期、不可用是操作或查询状态,不覆盖当前权威关系。准备转移时旧关系仍为 active;准备解绑时也不提前清空 owner。
40
+
41
+ 关系记录及解绑后的历史版本必须持久保留。存储读取、重启恢复或一致性校验失败时返回 `unavailable`,不能用空状态重新初始化为 unbound;只有权威确认的新身份或有效 release 才能处于 unbound。
42
+
43
+ | 操作 | 起始条件 | 必须授权 | 提交结果 |
44
+ | --- | --- | --- | --- |
45
+ | `bind` | `unbound`,匹配当前版本 | sub_aid + 拟绑定 owner | `active`,版本加一 |
46
+ | `transfer` | `active`,匹配当前 owner 与版本 | 当前 owner + sub_aid + 新 owner | `active`,直接替换 owner,版本加一 |
47
+ | `release` | `active`,匹配当前 owner 与版本 | 当前 owner | `unbound`,清空 owner,版本加一 |
48
+
49
+ sub_aid 可以发起解绑意向,但不能自行提交解绑。当前 owner 可以释放关系,不要求 sub_aid 在线;如果必须先隔离已外泄的身份凭据,则等待该安全步骤完成。转移直接从旧 owner 切换到新 owner,不经过可被抢绑的 unbound 窗口。
50
+
51
+ 保留现有类型约束:human 不作为 sub_aid;只有 `type: agent` 支持 self-owner。self-owner 仍走同一权威记录和版本检查;外部 owner 不能被普通更新改成 self-owner。类型以可信身份记录为准,不能只信任上传的 frontmatter。管理授权只针对直接关系,不自动沿 owner 链继承。
52
+
53
+ ## 3. 双方声明文档
54
+
55
+ ### 3.1 声明 schema
56
+
57
+ 两份文件采用相同的 `ownership.statement`:
58
+
59
+ | 字段 | 约束 |
60
+ | --- | --- |
61
+ | `version` | 固定为 `3`。 |
62
+ | `relation_id` | 关系权威分配的稳定标识。 |
63
+ | `relation_version` | 本份声明对应的已提交版本;准备文件使用 operation 的目标版本。 |
64
+ | `operation_id` | 建立该版本的 operation;普通正文更新不更换。 |
65
+ | `owner_aid`、`sub_aid` | 与该版本的权威关系一致。 |
66
+ | `declaration_id` | 本份声明的小写 UUID v4,双方可以不同。 |
67
+ | `declared_at` | Unix 秒正整数,处于被签名的正文中。 |
68
+
69
+ `agent.md` 保留已有身份字段与业务正文;出现 owner 时须同时包含 `owner` 和 `ownership.statement`,`owner` 等于声明中的 `owner_aid`,`aid` 等于 `sub_aid`。解绑提交后,sub_aid 自行移除这两个字段并重新签名上传。
70
+
71
+ owner 的声明存放于:
72
+
73
+ ```text
74
+ owner_aid:/sub_aids/{sub_aid}/owner.md
75
+ ```
76
+
77
+ `owner.md` 是 YAML frontmatter、可选正文及尾部文件签名块,frontmatter 包含 `owner_aid`、`sub_aid` 和上述 `ownership.statement`。顶层 AID 必须与 statement 及路径一致。公开可读、可下载;只有 owner_aid 自己可以认证写入或删除,不开放公共写权限。
78
+
79
+ ### 3.2 文件签名
80
+
81
+ ownership 字段不再独立签名,由整个文件的尾部签名保护;不增加 `signer_aid`。文件签署者由位置和证书所属 AID 确定:`agent.md` 是 sub_aid,`owner.md` 是 owner_aid。
82
+
83
+ ```text
84
+ <!-- AUN-SIGNATURE-OWNERSHIP-V2
85
+ cert_fingerprint: sha256:<64位hex>
86
+ public_key_fingerprint: sha256:<64位hex>
87
+ timestamp: <unix-seconds>
88
+ signature: <base64-der-signature>
89
+ -->
90
+ ```
91
+
92
+ 沿用 [agent.md 的文件签名格式](../agent.md/SCHEMA.md):ECDSA P-256/SHA-256,签名输入为尾部块之前的全部原始 UTF-8 字节,不规范化空白和换行。agent.md 兼容 `AUN-SIGNATURE`,也可使用 `AUN-SIGNATURE-AGENTMD-V2`;owner.md 使用 `AUN-SIGNATURE-OWNERSHIP-V2`。TAG 是文件载体标识,Ownership 版本由已签名的 `statement.version` 决定。
93
+
94
+ 必须检查证书链、AID、精确指纹、算法及有效性,拒绝重复或歧义字段。尾部 `timestamp` 不在文件签名输入内,不能单独作为可信历史时间;审计以权威验收时间、文档摘要及提交回执共同取证。
95
+
96
+ ### 3.3 准备证据与公开投影
97
+
98
+ - 提交前,各方生成目标版本的签名文件,分别提交给关系权威,作为 operation 的不可变证据快照;不覆盖当前公开文件。这是证据提交,不是 Name Service 或 Storage 的公开发布。
99
+ - bind 和 transfer 需要 sub_aid 的新 agent.md 及新 owner 的 owner.md;release 使用当前关系已保存的声明快照,不等待 sub_aid 生成无 owner 文档。
100
+ - 权威提交后,各方使用自己的身份,将对应文件发布到自己的服务。服务端核对当前关系及版本;普通 PUT 只能修改业务内容,不能新增、删除、替换 active 关系字段。
101
+ - 解绑后的字段清除使用 release 回执;转移后的新文件使用 transfer 回执。回执不是代上传凭证,上传者仍必须是文件所属 AID。
102
+ - 发布使用最新业务正文及 ETag 条件,可由文件所属 AID 保留已提交关系字段重新签名;新的公开文件不替换 operation 中已冻结的原始证据。
103
+ - 文件发布失败时,已提交关系保持稳定;缺失或旧版本文件只产生 `pending`、`missing` 或 `stale`,不得授予新关系的业务权限。迟到的旧版本上传、删除必须被拒绝或按版本条件忽略。
104
+
105
+ ## 4. Operation 与签名授权
106
+
107
+ ### 4.1 操作上下文
108
+
109
+ prepare 固定 `version=3`、`authority_issuer`、`action`、`operation_id`、`relation_id`、`sub_aid`、`old_owner_aid`、`new_owner_aid`、`expected_relation_version`、`target_relation_version`、`expires_at` 和 `idempotency_key`。bind 的 old owner 为 null;release 的 new owner 为 null。目标版本固定为当前版本加一。
110
+
111
+ 准备完成后,上下文还固定 `document_hashes` 与 `security_generation`:前者包含 `sub_document_sha256`、`owner_document_sha256`,均为完整签名文件字节的 SHA-256,以64位小写hex表示;bind/transfer 使用目标声明,release 使用已保存的当前声明。后者为 `{credential_generation, custody_generation}`,均是权威确认的非负整数。没有托管时也不能由客户端伪造或跳过已存在的私钥外发记录。
112
+
113
+ 操作有效期默认 7 天。幂等键按发起者、sub_aid 和 action 隔离;同键同参数返回同一 operation,同键不同参数返回 `idempotency_key_reuse`。prepare 仅接受 sub_aid 或当前 owner;新 owner 只能加入已创建的 operation,不能抢占别人的关系。
114
+
115
+ ### 4.2 授权签名
116
+
117
+ operation 授权是控制面的签名同意,不是 ownership 字段的独立签名。每方确认 action、双方 AID、版本、证据摘要和有效期后签署权威返回的完整固定上下文:
118
+
119
+ ```text
120
+ UTF-8("AUN-OWNERSHIP-OPERATION-V3;") || JCS(operation_context)
121
+ ```
122
+
123
+ JCS 使用 RFC 8785;本协议的版本、代际及时间为可精确表示的整数。签名算法与文件签名一致,封套包含 `cert_fingerprint` 和 `signature`;不设 `signer_aid`。
124
+
125
+ | 授权位置 | 预期签署者 |
126
+ | --- | --- |
127
+ | `sub_approval` | sub_aid |
128
+ | `owner_approval` | bind 的 new_owner_aid |
129
+ | `old_owner_approval` | transfer 或 release 的当前 old_owner_aid |
130
+ | `new_owner_approval` | transfer 的 new_owner_aid |
131
+
132
+ 每方只能通过自己的认证上下文提交本端证据和 approval。新的 approval 与 bind/transfer 的目标签名证据,在验收和提交时检查 active signing 与撤销规则;release/transfer 引用的旧声明档案按原提交回执、精确文档摘要及原验收时间验证,不要求历史证书当前仍可签名,也不接受新造的回溯文件冒充档案。
133
+
134
+ 同一份授权签名不能用于另一 operation、另一 issuer 或另一版本。上下文冻结后不得替换证据或授权;需要改内容时取消旧操作并重新准备。self-owner 中同一 AID 的有效同意可同时满足其重合角色。
135
+
136
+ ### 4.3 稳定阶段
137
+
138
+ | 阶段 | 已持久化的事实 | 当前关系与恢复方式 |
139
+ | --- | --- | --- |
140
+ | `prepared` | 操作参数、版本、期限;各准备任务的进度 | 原关系不变;继续本端证据准备和必要的密钥隔离。 |
141
+ | `awaiting_approvals` | 完整签名证据与冻结上下文;已收到的各方授权 | 原关系不变;只补缺失授权,不重写已验收内容。 |
142
+ | `ready` | 必要授权齐全,安全前置条件已确认 | 原关系不变;再次检查期限、当前 owner、版本和凭据代际后提交。 |
143
+ | `committed` | 新关系、提交回执、证据与审计记录、待发布/清理任务 | 新关系生效;各方只续跑自己的发布、删除和清理。 |
144
+ | `completed` | 必需文档投影和旧关系清理已完成,或被后续版本取代 | 返回原提交结果;可选托管仍按独立任务处理。 |
145
+
146
+ 取消、失败、过期只发生在 committed 之前,该操作不改变权威关系;继续采用权威当前状态,不能因版本冲突恢复准备时的旧 owner。已完成的 rekey、凭据撤销不逆转。committed 后不能因文档、网络、托管失败回滚关系,也不因 operation 期限到达重新判为过期。
147
+
148
+ 原提交回执永久对应原版本,查询时另行返回当前关系。后续版本已提交时,旧 operation 的发布/清理任务按目标版本标记为 `superseded`,不得重写或删除新关系的文档。
149
+
150
+ **唯一原子边界**是关系权威内部的小事务:CAS 更新关系,同时记录 operation 的 committed 状态、不可变提交回执、审计证据及后续任务。任一持久化失败均不返回提交成功。它不包含 Name Service、Storage、CA 或 SDK 网络调用;多实例必须共享一致存储,禁止各自维护关系副本。
151
+
152
+ ## 5. 权限失效与可选托管
153
+
154
+ ### 5.1 旧权限失效
155
+
156
+ 关系授予的业务权限、Storage ACL 和下载票据必须绑定 `relation_id + relation_version`。服务端在实际使用 owner 权限时核对认证调用者为当前 owner、目标为对应 sub_aid,且关系为当前 active 版本;SDK 的先验检查或一份合法关系证据不能替代调用者授权。
157
+
158
+ transfer/release 提交后,旧版本权限立即不再有效,因此 ACL 元数据和旧 owner.md 可以异步清理。如果某服务不能在权限使用时检查版本,则相关 ACL、票据或长期凭据必须在提交前完成撤销/失效,并持久化阻断旧版本继续签发授权的屏障;未确认时停留在准备阶段,不能把安全撤权放进无约束的后台任务。屏障在提交或取消被权威确认前不得自行解除。
159
+
160
+ ### 5.2 托管方式
161
+
162
+ 托管默认关闭,显式启用后独立于关系操作推进。默认仅托管加密身份/恢复材料,custodian 保存或下载密文,解密密钥由应用管理;不因关系建立自动把 sub_aid 私钥、解密口令或已导入身份交给 owner。
163
+
164
+ 封套固定 `version=3`、`algorithm=AES-256-GCM`、`kdf=PBKDF2-SHA256`、`iterations=200000`,包含 `sub_aid`、`relation_id`、`custody_generation`、`credential_generation`、`salt`、`nonce` 和包含16字节认证 tag 的 `ciphertext`。复用原实现的派生参数:16字节 salt、12字节 nonce;二进制字段使用严格 Base64,加密秘密由应用提供至少256 bit高熵随机值。每次新加密使用密码学安全的随机 salt/nonce,不复用 nonce;任务重试则重发已保存的原始封套。元数据使用以下 AAD 绑定,下载和恢复必须检查 AID、代际、证书与私钥是否匹配:
165
+
166
+ ```text
167
+ UTF-8("AUN-OWNERSHIP-CUSTODY-V3;") || JCS({sub_aid, relation_id, custody_generation, credential_generation})
168
+ ```
169
+
170
+ 解密秘密、明文私钥和身份包不进入公开文档或审计记录。该封套是 3.0 的独立格式,不能直接按旧 escrow 的固定 AAD 解密。恢复只在应用显式授权的 sub_aid 设备上导入;owner SDK 不自动装载 sub_aid 身份或代其执行操作。
171
+
172
+ ### 5.3 已外发私钥的隔离
173
+
174
+ 关系权威/托管服务记录身份材料代际及是否向旧 owner 外发过可解密材料;不能通过本次 `custody=false` 或删除本地配置消除记录。旧协议迁移且无法证明未外发时,按已外发处理。
175
+
176
+ 如果旧 owner 曾取得可解密私钥,transfer/release 必须在提交前依次:停止向旧 owner 提供新的身份材料;sub_aid 在自己的设备持久化新密钥并完成 rekey;使旧证书、旧访问/刷新 token、设备会话及可恢复旧身份的凭据失效;由身份权威确认新的 credential generation。之后才生成目标签名文档和 operation approvals。
177
+
178
+ 每一步都有持久化回执,可以独立重试;原关系在此期间保持不变,可选托管暂时不可用。无法确认凭据隔离时返回 `custody_rekey_required` 或 `credential_isolation_pending`,不得继续提交。新 owner 的托管授权在关系提交后另行启用。没有私钥外发时,不强制无意义的 rekey。
179
+
180
+ 隔离回执绑定 operation、sub_aid、关系版本及凭据代际,必须由相应服务确认并可查询。关系权威核验服务端状态,不能接受客户端提供的“已撤销”布尔值作为凭据。
181
+
182
+ ## 6. 共识检查、功能降级与审计
183
+
184
+ ### 6.1 检查结果
185
+
186
+ `checkOwnershipConsensus(sub_aid)` 返回 `relation_state`、`document_state`、`authorized`、双方 AID、`relation_id`、`relation_version`、`operation_id` 及证据快照。
187
+
188
+ 只有关系权威当前为 active、两份公开文件的签名有效,且关系标识、版本、operation 和双方 AID 与权威一致时,才返回 `authorized=true`。读取顺序为权威快照、两份文件、再次核对权威版本;变化则重试或返回 `relation_version_conflict`。真正执行受管理操作的服务仍须检查该版本,防止检查后转移或解绑。
189
+
190
+ | 文档情况 | `document_state` | 授权结果 |
191
+ | --- | --- | --- |
192
+ | 双方文件匹配当前 active 关系 | `consensus` | 可为 true |
193
+ | 已提交但尚未发布完整 | `pending` / `missing` | false,等待补发 |
194
+ | 只有旧版本或旧 owner 文件 | `stale` | false |
195
+ | 权威 unavailable,只有文件互相匹配 | `document_consensus` | false,只能展示历史声明 |
196
+ | 路径、字段或签名不合法 | `invalid` | false |
197
+
198
+ ### 6.2 降级规则
199
+
200
+ 能力发现分别报告协议版本、权威操作、双方文件服务、托管、rekey 与权限版本检查能力;未知能力不视为支持。
201
+
202
+ | 缺失能力/条件 | 允许的降级 | 不允许的行为 |
203
+ | --- | --- | --- |
204
+ | 未启用托管,或托管服务不可用 | 无托管的绑定、转移、解绑;保留待恢复托管任务 | 已外发私钥时跳过隔离 |
205
+ | 某一方离线或未授权 | 保存 operation,返回等待参与方 | 替对方签名或越过审批 |
206
+ | 提交后文件服务不可用 | 关系已提交、投影待发布;基本身份与消息继续使用 | 返回完整共识或回滚关系 |
207
+ | 关系权威不可用 | 本地保存 intent;只读验证历史文件 | 修改当前关系、使用缓存授予 owner 权限 |
208
+ | 参与方/服务只支持旧协议 | `legacy_read`,保留原关系,等待升级或显式迁移 | 自动退回 2.0 文件绑定、双协议并行写关系 |
209
+ | 凭据隔离或权限撤销能力不足 | 基本通信与只读检查,操作停留在准备阶段 | 宣称安全转移或解绑完成 |
210
+
211
+ 没有 owner 或正在准备 operation 不影响 AID 的身份认证、普通通信和与 owner 无关的业务。需要 owner 共识的功能只在检查通过后开放。旧关系迁移必须携带既有当前 owner 授权、版本和凭据外发历史,不能按新 unbound 身份导入。
212
+
213
+ ### 6.3 审计证据
214
+
215
+ 关系提交审计保存固定 operation 上下文、各方 approval、提交前后状态/版本、双方完整签名文件快照、文档摘要、凭据隔离回执及提交回执。release 保留原 active 关系声明,transfer 同时保留旧关系与新关系声明。
216
+
217
+ 需要 owner 关系的业务操作在执行前,持久化审计记录:业务操作标识、actor_aid、双方 AID、检查时间、权威版本/回执、双方声明原文与完整签名文件、实际服务端授权结果。只记哈希或可被删除的 URL 不足以证明历史声明。审计写入失败时不得继续受管理操作;关系后续撤销或文档删除不影响已保存证据。
218
+
219
+ ## 7. 接口边界
220
+
221
+ | 能力 | 最小协议要求 | 0.5.18 参考入口 |
222
+ | --- | --- | --- |
223
+ | prepare | 固定操作参数、当前版本与期限,幂等生成 operation | `auth.aid_bind_prepare`、`auth.owner_change_prepare` |
224
+ | 提交本端证据/授权 | 验证调用者、文件签名和 operation approval,持久化进度 | `auth.aid_bind_submit`、`auth.owner_change_submit` |
225
+ | commit | 授权齐全、安全前置条件满足、CAS;重试返回原回执 | `auth.aid_bind_activate`、`auth.owner_change_activate`、`auth.owner_relation_unbind` |
226
+ | 查询/恢复 | 返回 operation 稳定阶段、缺失任务及当前关系 | `auth.aid_operation_get`、`auth.owner_relation_status` |
227
+ | 取消准备操作 | 仅发起者或该操作必须授权的参与方在提交前取消;保留证据 | 由上述 operation 控制面定义 |
228
+ | 文件发布 | sub_aid 自签自传 agent.md;owner_aid 自签自写 owner.md;带当前版本条件 | Name Service、Storage 现有数据面 |
229
+ | 托管/权限 | 独立密文对象、ACL、凭据代际及可查询回执 | Storage、Auth/CA 既有能力 |
230
+
231
+ 以上是所需语义及复用方向,不表示原 RPC 原样具备 3.0 约束;特别是 unbind 必须纳入同一 operation、授权和审计边界。Message/Gateway 不新增 ownership 专用 P2P 消息,应用可通过已有消息或自己的流程通知参与方 operation_id;通知本身不是授权。
232
+
233
+ 常见结果/错误:`waiting_participant`、`owner_approval_required`、`relation_version_conflict`、`idempotency_key_reuse`、`operation_expired`、`document_stale`、`authority_unavailable`、`custody_rekey_required`、`credential_isolation_pending`、`unsupported_capability`。返回值必须区分“意向已保存”“权威已提交”“文档已收敛”“可选托管已完成”。
@@ -4,7 +4,7 @@
4
4
 
5
5
  本文是 legacy Agent.md 1.0 历史副本,不定义当前尾部文件签名或 Ownership 2.1。当前文件容器、可扩展 type、AID 和 ownership 字段以 [Ownership 协议定义 2.1](../Ownership协议定义2.1.md) 为准;[Ownership 2.0 Schema](../../agent.md/SCHEMA.md) 也属于历史版本。
6
6
 
7
- 当前 Group/Org 仅由专属生命周期的 owner 本人代签代上传,owner 必填而 ownership 可缺省,签名块必含 signer_aid;完整规则见 [当前协议 §2.5](../Ownership协议定义2.1.md#25-grouporg-名片的-owner-代签与上传),不采用本历史副本的签名方式。
7
+ 当前 Group/Org 使用实体主签、owner token 上传与可选 owner 附签,owner 必填且禁止通用 ownership/proof/release;Group 旧实体上传持续兼容,Org 不开放。通用多签精确字节及授权见 [当前协议 §2.4/§2.5](../Ownership协议定义2.1.md#24-文件签名),不采用本历史副本的签名方式。
8
8
 
9
9
  ## 限制
10
10
 
@@ -73,7 +73,7 @@ Tail 和 Forward 的水位由通过校验的服务端原始页决定。单条解
73
73
 
74
74
  冷启动缺少发送方 IK 时,SDK 会将同发送端请求 single-flight 合并,最多同步等待 3 秒执行 bootstrap;成功后立即重试当前消息解密,失败或超时才转入 pending 后台重试。这个有界阻塞只影响当前加密消息的首次处理,不改变“永久坏密文不阻塞 A/T/H”的规则。
75
75
 
76
- 每个 `AUNClient` 只有一个客户端级 Pull Gate。P2P Message、Group Message、Group Event 的 Pull 都进入该 Gate;Gate 始终 single-inflight,相同请求 key 折叠并共享同一结果,不同 key 排队,前台 FIFO 优先于后台 FIFO。Message Tail/History 属于前台,Message Forward/Gap Fill 与 Group Event Pull 属于后台。Group Event 仍使用独立的 Forward Cursor,但不使用独立 Gate;不同 `AUNClient` 实例之间不共享 Gate。应用层直接正常 `await client.call(...)` 即可,无需自行实现重复并发控制。
76
+ 每个 `AUNClient` 只有一个客户端级 Pull Gate。Python、TypeScript、JavaScript、Go 的 P2P Message、Group Message、Group Event Pull 都进入该 Gate;同 namespace single-inflight,不同 namespace 最多 4 个并行。namespace 分别为 `p2p:{当前 AID}`、`group:{群 ID}`、`group_event:{群 ID}`;同一消息 namespace 的 Tail、History、Forward 相互串行,群消息与群事件属于不同 namespace。相同请求 key 折叠并共享同一结果,其余可运行请求按前台 FIFO 优先于后台 FIFO 调度。Message Tail/History 属于前台,Message Forward/Gap Fill 与 Group Event Pull 属于后台。Group Event 仍使用独立的 Forward Cursor,但不使用独立 Gate;不同 `AUNClient` 实例之间不共享 Gate。应用层直接正常 `await client.call(...)` 即可,无需自行实现重复并发控制。
77
77
 
78
78
  A/T/H 是服务端消息空间的扫描水位,不是应用层发布账本。SDK 只保证单个通过校验的 Tail/Forward 响应页内按 seq 升序、同 seq 最多发布一次;History 只解密返回,不进入实时发布流。Tail 与 Forward 的窗口可能重叠,因此跨 Push/Tail/Forward 页不保证唯一或有序。全局去重与排序属于应用业务仓库职责,不是 SDK 交付契约;应用必须在 namespace 内按 `message_id` 幂等,并按 `seq` 排序存储或展示。SDK 的进程内去重只能作为 best-effort 重叠折叠,不能替代应用持久化幂等。
79
79
 
@@ -23,7 +23,7 @@ await client.connect({"slot_id": "main", "auto_reconnect": True})
23
23
 
24
24
  关键约束:
25
25
 
26
- - `AIDStore.register(aid)` 只负责生成本地密钥、申请证书并落盘。
26
+ - `AIDStore.register(aid)` 只负责生成本地密钥、申请证书并落盘。
27
27
  - `AIDStore.load(aid)` 返回 AID 值对象;AID 对象加载后不可变。
28
28
  - `AUNClient` 只接收 AID 对象,不接收字符串 AID 或配置字典里的 `aid` 字段。
29
29
  - `connect(options)` 内部会按需认证、建立 WebSocket、初始化 E2EE 和后台任务。
@@ -62,12 +62,14 @@ store = AIDStore(
62
62
  | `register(aid)` | 注册新 AID,返回 Result |
63
63
  | `list()` | 列出本地身份 |
64
64
  | `exists(aid)` | HEAD PKI 端点,判断 AID 是否已注册 |
65
- | `resolve(aid, opts=None)` | 拉取并缓存对端证书;默认同时下载并验签 agent.md,可传 `skip_agent_md=True` 跳过 |
65
+ | `resolve(aid, opts=None)` | 拉取并缓存对端证书及 agent.md;自有私钥身份优先本地草稿/缓存,可传 `skip_agent_md=True` 跳过名片 |
66
66
  | `download_agent_md(aid)` | 下载并验签对端 agent.md |
67
67
  | `check_agent_md(aid, ttl_days=1)` | 检查本地缓存与远端 agent.md 状态 |
68
- | `diagnose(aid)` | 本地和远端一致性诊断 |
69
- | `renew_cert(aid)` / `rekey(aid)` | 证书续签和换钥,成功后重新 `load()` |
70
-
68
+ | `diagnose(aid)` | 本地和远端一致性诊断 |
69
+ | `renew_cert(aid)` / `rekey(aid)` | 证书续签和换钥,成功后重新 `load()` |
70
+
71
+ 自有私钥身份的 `resolve` 默认优先返回本地草稿或缓存,标记 `verification.status=unverified`、`source.agent_md_fetched=false`;草稿的 `in_sync=false`。需要读取远端权威内容时,显式调用 `download_agent_md` 或 `resolve(..., {"force_refresh": True})`,本地草稿仍保留;失败稿由应用显式重试上传,重连不自动重传。
72
+
71
73
  Result 使用方式:
72
74
 
73
75
  ```python
@@ -89,65 +91,65 @@ client.load_identity(me) # standby
89
91
  client = AUNClient(me) # standby
90
92
  ```
91
93
 
92
- `load_identity()` 只允许在 `no_identity` 或 `closed` 状态调用;传入对象必须是私钥有效的 AID。
93
-
94
+ `load_identity()` 只允许在 `no_identity` 或 `closed` 状态调用;传入对象必须是私钥有效的 AID。
95
+
94
96
  ### 显式认证
95
97
 
96
- 通常不需要单独调用认证,`connect()` 会自动完成。需要提前完成认证或检查认证链路时可调用:
98
+ 通常不需要单独调用认证,`connect()` 会自动完成。需要提前完成认证或检查认证链路时可调用:
97
99
 
98
100
  ```python
99
101
  auth = await client.authenticate()
100
102
  print(auth["access_token"], auth["gateway"])
101
103
  ```
102
104
 
103
- 认证成功后状态进入 `authenticated`,随后调用 `connect()` 建立会话。
104
-
105
- `authenticate()` 返回的 `access_token` 只是认证完成时的快照。后台刷新会更新 SDK 内部身份状态,但无法更新应用已经保存的字符串,因此不得把首次返回值长期用于 HTTP 请求。
106
-
107
- ### 读取当前 access token
108
-
109
- 应用绕过 SDK 门面、自行访问要求 AID Bearer 的 HTTP 数据面时,应在每次请求前同步读取当前缓存:
110
-
111
- ```python
112
- token = client.get_access_token() # str | None
113
- ```
114
-
115
- ```typescript
116
- const token = client.getAccessToken(); // string | null
117
- ```
118
-
119
- ```go
120
- token := client.GetAccessToken() // 缺失时为空字符串
121
- ```
122
-
123
- 这些 getter 只读取后台刷新任务维护的最新缓存,不发起认证、刷新或网络请求,也不独立检查 token 是否临近过期;其有效性依赖客户端连接生命周期和后台刷新任务正常运行。返回空值时应先完成身份加载和认证/连接,不得发送空 Bearer。应用不得跨请求长期保存 getter 的返回值;具体的数据面票据、跨域凭据和重试规则见 [Storage RPC 手册:数据面 HTTP 下载流程](09-storage-rpc-manual.md#数据面-http-下载流程)。
105
+ 认证成功后状态进入 `authenticated`,随后调用 `connect()` 建立会话。
106
+
107
+ `authenticate()` 返回的 `access_token` 只是认证完成时的快照。后台刷新会更新 SDK 内部身份状态,但无法更新应用已经保存的字符串,因此不得把首次返回值长期用于 HTTP 请求。
108
+
109
+ ### 读取当前 access token
110
+
111
+ 应用绕过 SDK 门面、自行访问要求 AID Bearer 的 HTTP 数据面时,应在每次请求前同步读取当前缓存:
112
+
113
+ ```python
114
+ token = client.get_access_token() # str | None
115
+ ```
116
+
117
+ ```typescript
118
+ const token = client.getAccessToken(); // string | null
119
+ ```
120
+
121
+ ```go
122
+ token := client.GetAccessToken() // 缺失时为空字符串
123
+ ```
124
+
125
+ 这些 getter 只读取后台刷新任务维护的最新缓存,不发起认证、刷新或网络请求,也不独立检查 token 是否临近过期;其有效性依赖客户端连接生命周期和后台刷新任务正常运行。返回空值时应先完成身份加载和认证/连接,不得发送空 Bearer。应用不得跨请求长期保存 getter 的返回值;具体的数据面票据、跨域凭据和重试规则见 [Storage RPC 手册:数据面 HTTP 下载流程](09-storage-rpc-manual.md#数据面-http-下载流程)。
124
126
 
125
127
  ### 连接
126
128
 
127
129
  ```python
128
- await client.connect({
129
- "connection_kind": "long",
130
- "auto_reconnect": True,
131
- "push_notify_aid": "push.example.com",
132
- "push_token": "opaque-token",
133
- })
130
+ await client.connect({
131
+ "connection_kind": "long",
132
+ "auto_reconnect": True,
133
+ "push_notify_aid": "push.example.com",
134
+ "push_token": "opaque-token",
135
+ })
134
136
  ```
135
137
 
136
138
  | 选项 | 说明 |
137
139
  |------|------|
138
- | `connection_kind` | `"long"` 或 `"short"` |
139
- | `short_ttl_ms` | 短连接服务端兜底超时 |
140
- | `delivery_mode` | 连接级投递语义 |
141
- | `extra_info` | 应用层自定义连接信息;下划线开头的键不会透传给 Gateway |
142
- | `background_sync` | 连接成功后是否执行消息后台补洞 / 未读同步,默认开启;不关闭已读状态查询补偿 |
143
- | `auto_reconnect` | 断线后是否自动重连 |
144
- | `heartbeat_interval` | 心跳间隔,秒 |
145
- | `retry.initial_delay` / `retry.max_delay` | 退避重连参数 |
146
- | `connect_timeout` / `call_timeout` | 连接和 RPC 超时,秒 |
147
- | `push_notify_aid` / `push_token` | Python SDK 推荐的离线推送登记入口;必须同时提供 |
148
- | `push_provider` / `push_app_id` | Python SDK 可选诊断字段 |
149
-
150
- `slot_id` 来自已加载的 AID,不是 `connect()` 选项。Python SDK 在 AUN 会话建立后把 `push_notify_aid` 映射为冻结 RPC 字段 `push_server_aid`,调用 `push.register` 登记当前 `(aid, device_id, slot_id)`;这些字段不会进入 `auth.connect`。登记失败时本次连接回滚并向调用方抛错。Go、TypeScript、JavaScript SDK 本轮未增加该便利入口。
140
+ | `connection_kind` | `"long"` 或 `"short"` |
141
+ | `short_ttl_ms` | 短连接服务端兜底超时 |
142
+ | `delivery_mode` | 连接级投递语义 |
143
+ | `extra_info` | 应用层自定义连接信息;下划线开头的键不会透传给 Gateway |
144
+ | `background_sync` | 连接成功后是否执行消息后台补洞 / 未读同步,默认开启;不关闭已读状态查询补偿 |
145
+ | `auto_reconnect` | 断线后是否自动重连 |
146
+ | `heartbeat_interval` | 心跳间隔,秒 |
147
+ | `retry.initial_delay` / `retry.max_delay` | 退避重连参数 |
148
+ | `connect_timeout` / `call_timeout` | 连接和 RPC 超时,秒 |
149
+ | `push_notify_aid` / `push_token` | Python SDK 推荐的离线推送登记入口;必须同时提供 |
150
+ | `push_provider` / `push_app_id` | Python SDK 可选诊断字段 |
151
+
152
+ `slot_id` 来自已加载的 AID,不是 `connect()` 选项。Python SDK 在 AUN 会话建立后把 `push_notify_aid` 映射为冻结 RPC 字段 `push_server_aid`,调用 `push.register` 登记当前 `(aid, device_id, slot_id)`;这些字段不会进入 `auth.connect`。登记失败时本次连接回滚并向调用方抛错。Go、TypeScript、JavaScript SDK 本轮未增加该便利入口。
151
153
 
152
154
  当前稳定实现为 Gateway 模式;Peer / Relay 仍是协议能力定义,SDK 连接层会明确返回未实现。
153
155
 
@@ -160,16 +162,16 @@ await client.connect({
160
162
  长连接:
161
163
 
162
164
  ```python
163
- daemon = AUNClient(me)
164
- await daemon.connect({"connection_kind": "long"})
165
+ daemon = AUNClient(me)
166
+ await daemon.connect({"connection_kind": "long"})
165
167
  daemon.on("message.received", lambda e: print(e["payload"]))
166
168
  ```
167
169
 
168
170
  短连接:
169
171
 
170
172
  ```python
171
- cli = AUNClient(me)
172
- await cli.connect({"connection_kind": "short", "short_ttl_ms": 30000})
173
+ cli = AUNClient(me)
174
+ await cli.connect({"connection_kind": "short", "short_ttl_ms": 30000})
173
175
  await cli.call("message.send", {"to": peer, "payload": {"type": "text", "text": "hello"}})
174
176
  await cli.close()
175
177
  ```
@@ -206,19 +208,19 @@ print(client.can_connect, client.can_send, client.is_online)
206
208
  | `connection.error` | 连接、认证、重连错误 |
207
209
  | `token.refreshed` | token 自动刷新完成 |
208
210
  | `token.refresh_exhausted` | refresh_token 缺失、过期或刷新链耗尽,SDK 已清理本地 token,下一次重连会重新走完整登录 |
209
- | `message.received` | 收到消息 |
210
- | `group.changed` | 群组事件 |
211
- | `message.undecryptable` / `group.message_undecryptable` | E2EE 解密失败 |
212
- | `delivery.changed` | SDK 本地批量交付完成通知,用于合并 UI 刷新(完整契约见 [06-API手册](06-API手册.md#deliverychanged)) |
213
- | `read_state_sync` | SDK 本地已读状态补偿完成,含本轮 `owner_aid`、`states` 和逐项 `errors`;连接返回不代表补偿完成 |
214
-
215
- 建议在 `connect()` 前注册事件处理器。`read_state_sync` 只交付本轮成功查询涉及的去重状态及逐项错误,空查询不夹带旧缓存。AID/device 切换按身份代际隔离缓存、登记及补偿完成事件,A→B→A 也不能接受旧代际结果;原在途 RPC 仍可返回。
211
+ | `message.received` | 收到消息 |
212
+ | `group.changed` | 群组事件 |
213
+ | `message.undecryptable` / `group.message_undecryptable` | E2EE 解密失败 |
214
+ | `delivery.changed` | SDK 本地批量交付完成通知,用于合并 UI 刷新(完整契约见 [06-API手册](06-API手册.md#deliverychanged)) |
215
+ | `read_state_sync` | SDK 本地已读状态补偿完成,含本轮 `owner_aid`、`states` 和逐项 `errors`;连接返回不代表补偿完成 |
216
+
217
+ 建议在 `connect()` 前注册事件处理器。`read_state_sync` 只交付本轮成功查询涉及的去重状态及逐项错误,空查询不夹带旧缓存。AID/device 切换按身份代际隔离缓存、登记及补偿完成事件,A→B→A 也不能接受旧代际结果;原在途 RPC 仍可返回。
216
218
 
217
219
  ---
218
220
 
219
221
  ## Agent Web / agent.md
220
222
 
221
- 以下普通主体示例以本 AID 签名和 token 上传。Group/Org 使用专属 owner 生命周期:owner 与 entity_cert_fingerprint 必填、可无 ownership;仅当前 owner 用本人证书和私钥尾签(signer_aid=owner)并携带 V2 owner 授权,实体自身 token 不授予名片上传权。上传与恢复新鲜核对 owner/状态及实体证书代际,格式见 [Ownership 协议 §2.5](../protocol/Ownership协议定义2.1.md#25-grouporg-名片的-owner-代签与上传)。
223
+ 以下普通主体示例以本 AID 主签和 token 上传。Group/Org 名片由实体私钥生成主签,当前 owner 以本人 token 上传,无需实体登录;可选 owner 附签,存在就必须验全部块。Group 旧实体单签及实体身份上传始终兼容,Org 不开放该旧入口。取消实体额外 V2 上传授权,保留当前 owner、生命周期、实体证书代际与 CAS 检查,见 [协议 §2.5](../protocol/Ownership协议定义2.1.md#25-grouporg-名片的实体主签与-owner-上传)。
222
224
 
223
225
  发布自己的 agent.md:
224
226
 
@@ -226,8 +228,8 @@ print(client.can_connect, client.can_send, client.is_online)
226
228
  uploaded = await store.upload_agent_md("alice.agentid.pub", content)
227
229
  ```
228
230
 
229
- `aid` 必须是本地 AID。上传由 `AIDStore` 读取本地私钥签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
230
-
231
+ `aid` 必须是本地 AID。上传由 `AIDStore` 读取本地私钥签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
232
+
231
233
  解析对端并验签 agent.md:
232
234
 
233
235
  ```python
@@ -248,11 +250,13 @@ downloaded = await store.download_agent_md("bob.agentid.pub")
248
250
  state = await store.check_agent_md("bob.agentid.pub", ttl_days=1)
249
251
  ```
250
252
 
253
+ 新 SDK 全部 NS GET/HEAD/PUT(含 fresh)携带 `X-AUN-Signature-Format: multi-v1` 获取完整多签;实体 fresh GET 必须同时携带 `X-AUN-Ownership-Check: 1`、`Cache-Control: no-cache`、`X-AUN-Signature-Format: multi-v1`;NS 仅凭 Check 头绕过 LRU 并新鲜验证全部签块,仍返回 not_applicable,不进入通用 Ownership 账本。无头客户端读取首签投影,表示 ETag 与完整存储 CAS 的映射见 [协议 §3.3](../protocol/Ownership协议定义2.1.md#33-读取并发与接口)。
254
+
251
255
  当前公开入口分工固定:
252
256
 
253
257
  | 主体 | 公开能力 |
254
258
  |------|----------|
255
- | `AIDStore` | `upload_agent_md(aid, content=None)`、`download_agent_md(aid)`、`check_agent_md(aid, ttl_days=1)`、`resolve(aid, opts)` |
259
+ | `AIDStore` | `sign/verify` 通用 raw/document 签验、`upload_agent_md(aid, content=None)`、`download_agent_md(aid)`、`check_agent_md(aid, ttl_days=1)`、`resolve(aid, opts)` |
256
260
  | `AID` | `sign_agent_md(content)`、`verify_agent_md(content)` 低层签验能力 |
257
261
  | `AUNClient` | 连接、事件和 RPC 调用;不再暴露 agent.md 上传入口 |
258
262
 
@@ -309,7 +309,7 @@ for msg in result["messages"]:
309
309
 
310
310
  - P2P 设备 SPK 的 `key_source` 为 `peer_device_prekey`,由 AID 私钥签名背书。
311
311
  - 群内独立 group SPK 的 `key_source` 为 `group_device_prekey`,按规范化后的 `group_aid` 隔离。
312
- - SDK 发送前通过 `message.v2.bootstrap` / `group.v2.bootstrap` 获取目标设备集合和当前 SPK。
312
+ - SDK 发送前通过 `message.v2.bootstrap` / `group.v2.bootstrap` 获取目标设备集合和当前 SPK;P2P 定向设备未命中缓存时的单次新鲜重试与失败边界见 [message.send](09-message-rpc-manual.md#messagesend)。
313
313
  - 旧 SPK 会在本地保留一段安全窗口,用于解密引用旧 SPK 的历史消息;满足已消费和保留窗口条件后才销毁。
314
314
 
315
315
  裸 WebSocket 客户端如果绕过 SDK,需要自行完成同等的 SPK 生成、AID 私钥签名、注册和 bootstrap 逻辑。旧 `message.e2ee.put_prekey/get_prekey` 只用于 legacy `prekey_ecdh_v2` 信封兼容,不是当前 SDK 的默认路径。
@@ -7,6 +7,7 @@
7
7
  - [AIDStore](#aidstore)
8
8
  - [AID](#aid)
9
9
  - [AUNClient](#aunclient)
10
+ - [业务门面](#业务门面)
10
11
  - [事件](#事件)
11
12
  - [ServiceProxyClient](#serviceproxyclient)
12
13
  - [E2EE 高级 API](#e2ee-高级-api)
@@ -63,18 +64,32 @@ store := aun.NewAIDStore(aunPath, encryptionSeed, aun.AIDStoreOptions{
63
64
  | Python | TS/JS | Go | 说明 |
64
65
  |--------|-------|----|------|
65
66
  | `load(aid)` | `load(aid)` | `Load(aid)` | 从本地加载 AID |
66
- | `register(aid)` | `register(aid)` | `Register(ctx, aid)` | 注册并落盘证书和私钥 |
67
+ | `register(aid)` | `register(aid)` | `Register(ctx, aid[, opts])` | 注册并落盘证书和私钥;Go 两参数旧调用仍可用,显式选项使用 `AIDStoreRegisterOptions` |
67
68
  | `list()` | `list()` | `List()` | 列出本地 AID |
68
69
  | `exists(aid)` | `exists(aid)` | `Exists(ctx, aid)` | 远端存在性检查 |
69
- | `resolve(aid, opts=None)` | `resolve(aid, opts?)` | `Resolve(ctx, aid, opts...)` | 拉证书并缓存;默认下载 agent.md,可用 `skip_agent_md` / `skipAgentMd` 跳过 |
70
- | `upload_agent_md(aid, content=None)` | `uploadAgentMd(aid, content?)` | `UploadAgentMD(ctx, aid, content...)` | 普通主体以本身份签名/token 发布;Group/Org 仅当前 owner 本人尾签并携带 V2 owner 授权上传,见协议 §2.5 |
70
+ | `resolve(aid, opts=None)` | `resolve(aid, opts?)` | `Resolve(ctx, aid, opts...)` | 拉证书并缓存;自有私钥身份优先本地名片草稿/缓存,可用 `skip_agent_md` / `skipAgentMd` / `SkipAgentMD` 跳过名片 |
71
+ | `upload_agent_md(aid, content=None, *, uploader_aid=None, co_signer_aid=None)` | `uploadAgentMd(aid, content?, options?)` | `UploadAgentMD(ctx, aid, content...)` / `UploadAgentMDWithOptions(ctx, aid, opts, content...)` | 普通主体自签自传;Group/Org 实体主签、owner token 上传及可选 owner 附签,见协议 §2.5 |
71
72
  | `download_agent_md(aid, timeout_s=None)` | `downloadAgentMd(aid, timeoutMs=30000)` | `DownloadAgentMD(ctx, aid)` | 下载 agent.md,返回 `DownloadAgentMdResult` / `AgentMDInfo` |
72
73
  | `check_agent_md(aid, ttl_days=1)` | `checkAgentMd(aid, ttlDays=1)` | `CheckAgentMD(ctx, aid, maxUnsyncedDays...)` | 通过 HEAD 和本地记录检查一致性 |
74
+ | `sign(aid, input, *, input_type="text", format="raw", tag="", append=False, in_place=False, ...)` | `sign(aid, input, {inputType, format, tag, append, inPlace, ...})` | `Sign(ctx, aid, input, opts)` | 通用裸签或文档多签 |
75
+ | `verify(aid, input, signature=None, *, input_type="text", format="raw", ...)` | `verify(aid, input, signature?, {inputType, format, ...})` | `Verify(ctx, aid, input, opts)` | 裸签或逐块文档验签;Go detached 签名传 opts.Signature |
73
76
  | `diagnose(aid)` | `diagnose(aid)` | `Diagnose(ctx, aid)` | 本地 + 远端诊断 |
74
77
  | `renew_cert(aid)` / `rekey(aid)` | `renewCert(aid)` / `rekey(aid)` | `RenewCert(ctx, aid)` / `Rekey(ctx, aid)` | 证书运维 |
75
78
  | `change_seed(old, new)` | `changeSeed(old, new)` | `ChangeSeed(old, new)` | 本地密钥保护种子迁移 |
76
79
 
77
- Python、TS/Node 与 Go 的 `AIDStore` 本地方法返回 Result 包装;浏览器 JS 因 IndexedDB / WebCrypto 约束返回 `Promise<Result>`。联网方法在 TS/JS 中均返回 `Promise<Result>`;Go 形态为 `Result[T]`,字段为 `Ok` / `Data` / `Error`。
80
+ Go 的 `Register(ctx, aid)` 保留原有简单身份注册行为;`Register(ctx, aid, AIDStoreRegisterOptions{...})` 是显式类型/owner 登记入口。历史 owner-aware `RegisterWithOptions(ctx, AIDRegisterParams{...})` 单独保留旧注册编排,不等同于 Ownership 2.1 的双文件关系确认。
81
+
82
+ Python、TS/Node 与 Go 的 `AIDStore` 本地方法返回 Result 包装;浏览器 JS 因 IndexedDB / WebCrypto 约束返回 `Promise<Result>`。TS/Node 的 Store.sign/verify 也是异步 `Promise<Result>`,属于上述本地方法的例外;联网方法在 TS/JS 中均返回 `Promise<Result>`;Go 形态为 `Result[T]`,字段为 `Ok` / `Data` / `Error`。
83
+
84
+ 自有私钥身份的 `resolve` 默认优先返回本地草稿/缓存,标记 `verification.status=unverified`、`source.agent_md_fetched=false`;草稿的 `in_sync=false`。需要读取远端内容时,显式下载或强制刷新,本地草稿仍保留;刷新选项分别为 Python `force_refresh`、TS/JS `forceRefresh`、Go `ForceRefresh`。失败稿由应用显式重试上传,重连不自动重传。
85
+
86
+ ### 通用 sign / verify
87
+
88
+ `format="raw"` 为默认,保留 detached Base64 签名/验签;`format="document"` 处理任意 UTF-8 文本文档的连续尾签,不要求 YAML 或 agent.md 业务字段。sign 返回 `signed` 全文、本次 `signature` 和实际 `signatures` 列表;verify 返回 `valid`、`status`、首块前 `payload` 及逐块 `signatures`。普通 agent.md 与 owner.md 主签不输出且业务不接受 signer_aid;实体主块显式 signer_aid=target,主签身份、Schema、资源及授权由调用者另验,SDK 文件流程和服务端均复用各自唯一同构签验 core。
89
+
90
+ `input_type="text"` 默认把输入视作内容;显式 `input_type="file"` 才读取文件;raw 按原始字节处理,包括非 UTF-8 文件,document 必须严格 UTF-8,raw/document 字符串均严格校验 Unicode 并编码为 UTF-8,无效 Unicode 拒绝。不识别 `file://` 或自动猜路径,缺失文件报错。`in_place=True` 仅适用于 file + document,签名成功后才写回;TS/JS 使用 `inputType/inPlace`,Go 使用 `InputType/Format/Tag/Append/InPlace`。浏览器 File/Blob 只读,FileSystemFileHandle 可原地写,不接受裸本地路径。
91
+
92
+ `append=True` 仅在已有签名链末尾追加时,末字节非 LF/CR 才补 LF,其余字节不变;无旧块时仍按首签要求正文以 CR/LF 结束,否则拒绝;新块签完整 prefix,含该分隔及所有前序完整块。主块保持旧正文裸签;附签必填 signer_aid。tag 是 `[A-Z0-9-]+` 字面后缀:空串无 TAG,`AGENTMD-V2` 生成 `AUN-SIGNATURE-AGENTMD-V2`,`AUN-SIGNATURE` 不作特殊选项;内部 wrapper 传短 tag,标签不授予角色。verify 必须全块验签并报告实际顺序,不能保证检测末端删签;签名区总计仍限 2048 字节,不加证书链字段,证书沿用可信指纹 resolver。verify 默认离线 load 本地公钥/证书,不要求私钥;历史/远端材料由业务显式注入可信 certificateResolver,并沿用既有证书链及生命周期检查,既有 agent.md 远程下载流程不变。数学验签失败仍返回全部逐签 records;resolver 操作异常保留外层 error,不能误标坏签名。精确字节及主签规则见 [协议 §2.4](../protocol/Ownership协议定义2.1.md#24-文件签名)。
78
93
 
79
94
  ### Result 类型
80
95
 
@@ -312,15 +327,16 @@ headers = client.get_protected_headers()
312
327
 
313
328
  | Python | TS/JS | Go | 说明 |
314
329
  |--------|-------|----|------|
315
- | `AIDStore.upload_agent_md(aid, content=None)` | `store.uploadAgentMd(aid, content?)` | `store.UploadAgentMD(ctx, aid, content...)` | 发布指定本地 AID 的 agent.md |
330
+ | `AIDStore.upload_agent_md(aid, content=None, *, uploader_aid=None, co_signer_aid=None)` | `store.uploadAgentMd(aid, content?, options?)` | `store.UploadAgentMD(ctx, aid, content...)` / `store.UploadAgentMDWithOptions(ctx, aid, opts, content...)` | 发布指定本地 AID 的 agent.md |
316
331
  | `AIDStore.download_agent_md(aid)` | `store.downloadAgentMd(aid)` | `store.DownloadAgentMD(ctx, aid)` | 下载并验签 |
317
332
  | `AIDStore.check_agent_md(aid)` | `store.checkAgentMd(aid)` | `store.CheckAgentMD(ctx, aid, maxUnsyncedDays...)` | 检查一致性 |
318
333
 
319
334
  说明:
320
335
 
321
336
  - agent.md 上传、下载和检查入口都在 `AIDStore`;`AUNClient` 不再暴露上传入口。
322
- - 上传要求目标 AID 已在本地加载且私钥有效;SDK 会对正文签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
323
- - SDK 发起 GET 时只发送 `Accept: text/markdown`,不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
337
+ - 普通主体目标 AID 须有本地签名材料并使用自身 token;Group/Org 用实体主签、当前 owner token 上传,可选 owner 附签,不能以附签替换资源授权。agent.md 与 owner.md 统一调用 Store document 签验 core,再验证业务 Schema。
338
+ - 实体上传默认使用名片当前 owner;显式指定上传者分别使用 Python `uploader_aid`、TS/JS `options.uploaderAid`、Go `AIDStoreUploadAgentMDOptions.UploaderAID`。追加 owner 附签分别使用 `co_signer_aid`、`options.coSignerAid`、`AIDStoreUploadAgentMDOptions.CoSignerAID`,值必须为当前 owner;Go 通过 `UploadAgentMDWithOptions` 传入选项。
339
+ - SDK 全部 NS GET/HEAD/PUT 携带 `X-AUN-Signature-Format: multi-v1`;GET 同时发送 `Accept: text/markdown`;实体 fresh GET 另带 `X-AUN-Ownership-Check: 1` 与 `Cache-Control: no-cache` 以绕 LRU 全块验证,仍为 not_applicable、不进入通用账本;不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
324
340
  - SDK 会自动从 Gateway `_meta.agent_md_etags` 和信封 `agent_md` 观察远端版本;`requester`、`peer`、`group` 是标准角色键,`receiver`、`target`、`to`、`sender`、`from` 是兼容别名。
325
341
  - `Accept: text/markdown` 与 agent.md 的 YAML frontmatter + Markdown 格式兼容;agent.md 仍是 Markdown 媒体类型上的结构化约定。
326
342
 
@@ -330,6 +346,15 @@ headers = client.get_protected_headers()
330
346
 
331
347
  除 `client.call(method, params)` 外,四语言 SDK 提供高层门面(Facade),封装常用操作并简化参数。普通应用优先用门面,只有需要精确控制底层参数时再直调 RPC。
332
348
 
349
+ ### Ownership、凭证与组织专题
350
+
351
+ | 领域 | 当前入口 | 详细指南 |
352
+ |------|----------|----------|
353
+ | Ownership 2.1 | 七个 Facade:`bindOwneraid`、`bindSubaid`、`releaseSubaid`、`transferSubaid`、`acceptOwnershipRequest`、`rejectOwnershipRequest`、`checkOwnershipConsensus`(TS/JS 命名) | [应用集成](Ownership2.1-SDK应用集成指南.md#4-七个-facade-的参数角色与结果)、[开发与移植](Ownership2.1-SDK开发与移植指南.md) |
354
+ | Credential/Org | Python/TS/JS:`client.credential`、`client.org`;Go:`client.Credential()`、`client.Org()`;包含本地协议助手和业务 facade,服务端可用方法以 RPC 手册为准 | [应用集成](凭证与组织服务SDK应用集成指南.md)、[开发与移植](凭证与组织服务SDK开发与移植指南.md) |
355
+
356
+ Ownership 2.1 关系须检查当前双文件共识;Group/Org 使用专属生命周期,通用关系操作返回 `not_applicable`。Credential/Org 的本地验签不代替当前状态和请求授权检查。
357
+
333
358
  ### 文件与存储门面
334
359
 
335
360
  | 能力 | Python | TS/JS | Go | 说明 |
@@ -768,7 +793,7 @@ sub.unsubscribe()
768
793
  | `push.offline_message` | Push Server 收到离线唤醒批次;普通终端无需订阅 |
769
794
  | `storage.object_changed` | Storage 对象变更事件透传 |
770
795
 
771
- 服务端不产生 `message.ack` 通知;SDK 保留的透传订阅不能作为可依赖的 ACK 或对端已读事件。已读事件只向 owner AID 分发且不进入 Pull 队列;重连查询补偿不受消息 `background_sync=false` 影响。身份切换按代际隔离旧缓存与补偿完成事件,A→B→A 也不能接收旧代际结果;原在途 RPC 仍可返回。详见 [跨设备已读未读 SDK 集成指南](../跨设备已读未读SDK集成指南.md)。
796
+ 服务端不产生 `message.ack` 通知;SDK 保留的透传订阅不能作为可依赖的 ACK 或对端已读事件。已读事件只向 owner AID 分发且不进入 Pull 队列;重连查询补偿不受消息 `background_sync=false` 影响。身份切换按代际隔离旧缓存与补偿完成事件;已排队的 raw/public `read_changed` 与 `read_state_sync` 在出队和每个处理器执行前重新核对身份代际,处理器等待期间切换身份也会停止后续交付,A→B→A 同样隔离;原在途 RPC 仍可返回。详见 [跨设备已读未读 SDK 集成指南](../跨设备已读未读SDK集成指南.md)。
772
797
 
773
798
  `message.received` 的应用层消息保留 `sender_device_id`,表示发送方设备;需要只回复原设备时,将该值作为下一次 `message.send.device_id`。volatile 消息还保留 `storage="volatile"`、`volatile_cursor`、`created_at`、`expires_at`,不含持久 `seq`;内部 slot 不会暴露给应用。
774
799
 
@@ -888,6 +913,8 @@ JavaScript 浏览器 SDK 的 Worker 是网络隔离增强:`new Worker()` 同
888
913
  |------|------|----------|
889
914
  | 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | `message.send` / `message.pull` / `message.history` / `message.ack` / `message.read` / `message.read_state` / `message.thought.*` |
890
915
  | 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | `group.create` / `group.send` / `group.pull` / `group.history` / `group.read` / `group.read_state` / `group.v2.*` / `group.fs.*` |
916
+ | 凭证 | [凭证服务 RPC 手册](09-凭证服务RPC手册.md) | `credential.register` / `credential.revoke` / `credential.status` / `credential.revocationList` |
917
+ | 组织 | [组织服务 RPC 手册](09-组织服务RPC手册.md) | `org.lifecycle.*` / `org.member.*` / `org.credential.*` / `org.audit.query` / `org.authz.check`;内部及未实现方法见手册 |
891
918
  | 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | `storage.put_object` / `storage.fs.*` / `storage.volume.*` / ACL / token / share link |
892
919
  | 协作 | [09-collab-rpc-manual.md](09-collab-rpc-manual.md) | `collab.ls-files` / `collab.show` / `collab.commit` / `collab.merge` / `collab.log` / `collab.diff` / `collab.clone` / `collab.prune` / `collab.gc` / `collab.reflog` / `collab.revert` / `collab.tag.*` / `collab.ls-remote` / `collab.unregister` |
893
920
  | 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | `meta.ping` / `meta.status` / `meta.trust_roots` |