@agentunion/fastaun 0.5.25 → 0.5.27

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 (76) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/_packed_docs/CHANGELOG.md +41 -0
  3. package/_packed_docs/INDEX.md +22 -14
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +11 -6
  5. 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 +16 -1
  6. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +37 -9
  7. package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +20 -9
  8. package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +57 -12
  9. package/_packed_docs/cli//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241CLI/345/274/200/345/217/221/344/270/216/344/275/277/347/224/250/346/214/207/345/215/227.md +869 -0
  10. 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 +2 -2
  11. package/_packed_docs/sdk/0.5.27-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +408 -0
  12. package/_packed_docs/sdk/0.5.27-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +410 -0
  13. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +2 -2
  14. package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +187 -73
  15. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +1 -1
  16. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +13 -9
  17. package/_packed_docs/sdk/INDEX.md +21 -13
  18. package/_packed_docs/sdk/agent.md/344/270/216group.index/347/232/204ETag/350/247/202/345/257/237/344/270/216/345/220/214/346/255/245/346/234/272/345/210/266.md +240 -0
  19. 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 +973 -266
  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/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +950 -242
  21. package/dist/agent-md-schema.js +1 -1
  22. package/dist/agent-md-schema.js.map +1 -1
  23. package/dist/agent-md.js +5 -0
  24. package/dist/agent-md.js.map +1 -1
  25. package/dist/aid.js +3 -3
  26. package/dist/aid.js.map +1 -1
  27. package/dist/auth.js +21 -19
  28. package/dist/auth.js.map +1 -1
  29. package/dist/cert-utils.d.ts +1 -1
  30. package/dist/cert-utils.js +4 -4
  31. package/dist/cert-utils.js.map +1 -1
  32. package/dist/client/lifecycle.d.ts +1 -1
  33. package/dist/client/lifecycle.js.map +1 -1
  34. package/dist/client/rpc-pipeline.js +8 -1
  35. package/dist/client/rpc-pipeline.js.map +1 -1
  36. package/dist/client/v2-e2ee.js +7 -0
  37. package/dist/client/v2-e2ee.js.map +1 -1
  38. package/dist/client.d.ts +12 -0
  39. package/dist/client.js +305 -29
  40. package/dist/client.js.map +1 -1
  41. package/dist/facades.d.ts +38 -0
  42. package/dist/facades.js +49 -5
  43. package/dist/facades.js.map +1 -1
  44. package/dist/index.d.ts +4 -0
  45. package/dist/index.js +3 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/keystore/local-identity-store.js +16 -1
  48. package/dist/keystore/local-identity-store.js.map +1 -1
  49. package/dist/keystore/local-token-store.js +16 -1
  50. package/dist/keystore/local-token-store.js.map +1 -1
  51. package/dist/org-protocol.d.ts +1 -0
  52. package/dist/org-protocol.js +39 -36
  53. package/dist/org-protocol.js.map +1 -1
  54. package/dist/ownership-v21.js +7 -7
  55. package/dist/ownership-v21.js.map +1 -1
  56. package/dist/register-flow.js +2 -2
  57. package/dist/register-flow.js.map +1 -1
  58. package/dist/semantic-facade.d.ts +176 -0
  59. package/dist/semantic-facade.js +611 -0
  60. package/dist/semantic-facade.js.map +1 -0
  61. package/dist/semantic-protocol.d.ts +810 -0
  62. package/dist/semantic-protocol.js +894 -0
  63. package/dist/semantic-protocol.js.map +1 -0
  64. package/dist/tools/cross-sdk-agent.d.ts +11 -1
  65. package/dist/tools/cross-sdk-agent.js +388 -7
  66. package/dist/tools/cross-sdk-agent.js.map +1 -1
  67. package/dist/transport.d.ts +1 -0
  68. package/dist/transport.js +28 -2
  69. package/dist/transport.js.map +1 -1
  70. package/dist/v2/e2ee/decrypt.d.ts +5 -0
  71. package/dist/v2/e2ee/decrypt.js +22 -2
  72. package/dist/v2/e2ee/decrypt.js.map +1 -1
  73. package/dist/version.d.ts +1 -1
  74. package/dist/version.js +1 -1
  75. package/package.json +2 -1
  76. package/src/certs/root.crt +13 -0
@@ -1,242 +1,950 @@
1
- # 凭证与组织服务 SDK 开发与移植指南
2
-
3
- 面向 SDK 实现者,按“共同签验 → 请求绑定 → RPC → 持久状态 → 实体恢复”移植。协议为 Org `0.8`,SDK 为 `0.5.19` 基线及当前 `0.5.22` 工作区增量(未发布)。应用调用见 [集成指南](凭证与组织服务SDK应用集成指南.md),字段以 [详细设计](../../../extensions/services/docs/org/0.8/17-凭证与组织服务详细设计.md) 为准,不以任一语言实现代替协议。
4
-
5
- ## 1. 实现层次和边界
6
-
7
- | 层次 | 职责/TS 参考 |
8
- | --- | --- |
9
- | 连接/RPC | 认证、Gateway service-plane、设备/slot、重连、token、外层签名;[rpc-pipeline.ts](../../ts/src/client/rpc-pipeline.ts)。 |
10
- | 协议 | canonical JSON、CAP、binding、回执、代表证明和完整 Verify;[org-protocol.ts](../../ts/src/org-protocol.ts)。 |
11
- | Facade | 参数和 method 映射,不复制组织服务状态机;[facades.ts](../../ts/src/facades.ts)。 |
12
- | 身份编排 | 组织实体身份、名片、rekey、checkpoint/恢复;[client.ts](../../ts/src/client.ts)。 |
13
-
14
- Credential/Org 业务经 Gateway service-plane,不经 Kernel;health/诊断走控制面。服务独立部署、独立数据库,不共享私钥或通过代码 import 耦合。
15
-
16
- ## 2. 共同密码学和 JSON 规则
17
-
18
- ### 2.1 两类 canonical JSON
19
-
20
- | 输入 | 规范 |
21
- | --- | --- |
22
- | CAP/binding/receipt/representation/服务事件 | 键按 Unicode code point 排序,字符串 NFC,数字最短十进制;签名时去掉对象对应签名字段。 |
23
- | org 外层 client_signature 请求摘要 | 排序和数值规则相同,字符串保持原值,不 NFC。 |
24
-
25
- 两者都拒绝孤立 surrogate、非有限或不安全数值。签名固定 ECDSA P-256/SHA-256,严格标准 Base64 的 ASN.1 DER;不能把 WebCrypto P1363 原样写入。
26
-
27
- 内部签名片段,unsigned 已按对象规则去除签名字段:
28
-
29
- ```ts
30
- import { canonicalJson } from './org-protocol.js';
31
- const result = signer.sign(Buffer.from(canonicalJson(unsigned)));
32
- if (!result.ok) throw new Error(result.error.message);
33
- const envelope = { cert_fingerprint: signer.certFingerprint,
34
- timestamp: now, signature: result.data.signature };
35
- ```
36
-
37
- 封套仅允许 cert_fingerprint、timestamp、signature 及可选 public_key_fingerprint;拒绝 algorithm/kid/value。resolver 支持 PEM/DER bytes/标准 Base64 DER,核对指纹、证书 AID、P-256 和签名时间。
38
-
39
- ### 2.2 摘要和随机数
40
-
41
- 摘要为 `sha256:<64位小写hex>`;nonce 为安全随机字节的 base64url,默认 32 字节。时间使用 Unix 秒及 `iat ≤ now < exp`,整数限制为 ±9007199254740991。
42
-
43
- 幂等键为 method+request_id,同键换参数摘要报 CREDENTIAL_IDEMPOTENCY_KEY_REUSE/Org IDEMPOTENCY_KEY_REUSE;register 与补偿 revoke 按 method 独立去重。JSON null/字段缺省和语言异步差异不得改变 wire。
44
-
45
- ## 3. AUN-CAP/1 实现契约
46
-
47
- ### 3.1 结构验证
48
-
49
- CAP 必填 ver/cid/iat/iss/sub/aud/nbf/exp/cap/key_version/signature。拒绝未知字段、空身份/受众、非法时间、签名结构及超深父链。根深度缺省 0;子凭证显式为父深度+1,最大 8;security_level 为 low/medium/high,缺省 medium,不能降低业务最低策略。
50
-
51
- 成员 CAP 固定为:iss=org_id、sub=member_aid、aud 包含 org_id,唯一能力为 org/role.<role>/org_id;ext 绑定 membership_id、binding_version、terms_hash,bound_to_membership=true、delegate=false,不允许 parent delegation。createMemberCredential 必须拒绝冲突覆盖。
52
-
53
- ### 3.2 验证级别
54
-
55
- ```mermaid
56
- flowchart LR
57
- A[结构、链、各层签名、时间和收窄] --> B[当前 CAP 及父链状态]
58
- B --> C[实际能力和约束]
59
- C --> D[实际请求 binding]
60
- D --> E[成员双签和当前状态]
61
- E --> F[代表证明、Ownership 和 actor 状态]
62
- F --> G[重读时钟,复核全部有效窗口]
63
- G -->|全部通过| H[授权结果]
64
- ```
65
-
66
- ```ts
67
- import { verifyCredential } from './org-protocol.js';
68
- const checked = verifyCredential(credential, verificationContext);
69
- ```
70
-
71
- verifySignature 只做结构/证书/数学验签,可选时间和受众,不授权。verify 的 context 必须绑定实际 audience、subject、能力和 method/target/params/requestId/nonce/requestBinding。
72
-
73
- 有 parent 时,每个父文档必须取得强 resolveStatusProof;受信 resolver 先验服务签名、信任链/撤销及真实查询 request ID/nonce/target,SDK 再核对 aun-credential-status/1、CID/完整 CAP hash、active、responder 和证明窗口。缺失/畸形为 STATUS_UNKNOWN,明确 CID/hash 不符为 CHAIN_INVALID;不得降为字符串 active 或相信自报 verified。
74
-
75
- 成员必须有当前 binding 双签及 resolveMembershipStatus=true;代表请求另验当前 Ownership 和 actor 状态。未指定 now 时,所有 resolver/异步等待后重读时钟,复核 CAP、证书、状态 proof、request binding 和代表证明;显式有效 now 按固定时间测试。校验不修改输入。
76
-
77
- ## 4. Membership binding、回执与请求绑定
78
-
79
- ### 4.1 Binding
80
-
81
- 成员和组织签同一不可变条款,签名 payload 去掉 member_signature/org_signature;绑定 membership_id、binding_version、terms_hash、request ID/nonce 和时间窗。binding 不是能力凭证,当前成员状态由 Org 保存。
82
-
83
- ```ts
84
- import { createMembershipBinding, signMembershipBinding } from './org-protocol.js';
85
- const memberSigned = createMembershipBinding(bindingOptions, memberSigner);
86
- const bothSigned = signMembershipBinding(memberSigned, orgSigner, 'org');
87
- ```
88
-
89
- 申请先成员签,邀请先组织签;每个邀请目标独立 binding/nonce/CAP/登记授权,不能互换材料。
90
-
91
- ### 4.2 登记授权和回执
92
-
93
- ```mermaid
94
- sequenceDiagram
95
- participant I as issuer/Org SDK
96
- participant O as Org 服务
97
- participant C as Credential 服务
98
- I->>O: 完整双签 binding、CAP 和登记授权
99
- O->>C: register:原 CID、request ID、nonce 和请求绑定
100
- C-->>O: 服务签名 registrationReceipt
101
- O->>O: 核对服务、registrar=iss、CID/全文摘要、原请求和有效期
102
- O->>O: 最终化成员及版本
103
- O-->>I: 完整成员包
104
- ```
105
-
106
- registration_authorization 由 issuer 签,覆盖完整 CAP hash、CID、原 request ID/nonce、Credential target、relay 和期限;转交服务不是 registrar。回执只是登记见证,不代替授权;公开验回执和服务最终化均按当前窗口严格检查。
107
-
108
- 受限跨期恢复只接受原期限内已持久且已发送的同一已批准操作,固定 CID/request ID/nonce、双签、登记授权和成员 CAS。原回执仅作私有历史来源,仍须新鲜签名状态绑定同一 CAP/全文摘要,并确认当前成员版本已生效;只记录既成结果,不重登记/重签。无原回执的 generic 恢复还须 registrationConfirmedAt,仅保存 issuer checkpoint,不导入 holder/发送 issued;holder 交付仍需有效原回执。unknown、revoked、未提交或不匹配拒绝,详见 [审查 §7.14](../../../extensions/services/docs/org/0.8/凭证与组织服务及四SDK审查报告.md)。
109
-
110
- ### 4.3 Org request binding
111
-
112
- ```mermaid
113
- flowchart LR
114
- A[业务参数+AAD+授权包] --> B[去 request_binding、client_signature、下划线字段、representation_proof]
115
- B --> C[业务摘要:request binding/代表证明]
116
- C --> D[完整准备后的参数]
117
- D --> E[仅去 client_signature 和下划线字段,重算外层摘要]
118
- E --> F[RPC 管线生成 client_signature]
119
- ```
120
-
121
- ```ts
122
- const prepared = client.org.prepareRequest(
123
- 'org.member.get', params, orgServiceAid, signer,
124
- );
125
- const result = await client.org.member.get(prepared);
126
- ```
127
-
128
- 两层 params_hash 不可复用。所有 org.* 加 aad.org_id,不匹配即拒绝;只有 lifecycle.list/health/status 允许 null。外层签名覆盖完整 method、AID、timestamp 和外层摘要,服务端独立重算。
129
-
130
- 首次 join/inviteAccept 使用 membership_id 专用 admission binding,复用原 request ID/nonce,不含 credential_cid,不要求已有 active CAP。本人/代表都须目标成员签名;仅 for≠signer.aid 加 admission representation proof 和当前 Ownership/actor 检查。inviteAccept 保留原组织签名、CAP 模板及登记授权;已有绑定/签名冲突不能重写。
131
-
132
- ### 4.4 Credential request binding
133
-
134
- register/revoke/status/revocationList 均绑定真实 method、target、参数摘要、request ID/nonce、iat/exp 和 CID,满足 exp≤iat+300。仅 revocationList 的 CID 固定为空,摘要仍包含 issuer、cursor、limit、since_event_seq。
135
-
136
- 内部 helper 示例;public Credential facade 不自动生成该绑定,也没有 credential.prepare:
137
-
138
- ```ts
139
- import { buildRequestBinding } from './org-protocol.js';
140
- const requestBinding = buildRequestBinding(
141
- method, credentialServiceAid, businessParams, cid, nonce, requestId, now, now + 300,
142
- );
143
- ```
144
-
145
- ## 5. Facade 和 RPC 映射
146
-
147
- ### 5.1 四端公共能力
148
-
149
- | 能力 | Python | TS/JS | Go |
150
- | --- | --- | --- | --- |
151
- | facade | client.credential/org | client.credential/org | Credential()/Org() |
152
- | 请求准备 | org.prepare_request | org.prepareRequest | Org().PrepareRequest |
153
- | 完整验证 helper | verify_credential | verifyCredential | VerifyCredential |
154
-
155
- 协议 helper 和 facade 命名分开;创建高层入口分别为 org.lifecycle.create、client.createOrg、client.CreateOrg,不能将概念名当四端统一方法名。
156
-
157
- ### 5.2 当前方法集合
158
-
159
- Credential:register/revoke/status(getStatus 别名)/revocationList;本地 create/verifySignature/verify/verifyRegistrationReceipt/current。Org:lifecycle、member、credential、audit.query、authz.check;具体方法见 [RPC 手册](09-组织服务RPC手册.md)。
160
-
161
- 保留的 resource.create/task.create/host.add/workflow.create 返回 NOT_IMPLEMENTED,其他未注册同类方法返回 Method not found。membership.reserve/get/commit/release 为内部受限接口,member.add 仅服务内部最终化使用。
162
-
163
- ### 5.3 异步约定
164
-
165
- Python RPC async、本地协议 helper 同步;Node TS 协议 helper 同步,浏览器 WebCrypto helper 返回 Promise,Go 返回 value/error。公共文件 AIDStore.sign/verify 的 TS/JS 接口均为 Promise<Result>;这些差异不能进入 wire。
166
-
167
- ### 5.4 撤销同步与持久快照(0.5.22)
168
-
169
- ```mermaid
170
- flowchart LR
171
- A[推送/列表页] --> B[验证服务、目标、签名和时间窗]
172
- B --> C[复核身份、连接和 worker 代次]
173
- C --> D[读取当前 authority 快照,逐 CID 合并最大 event_seq]
174
- D --> E[仅连续覆盖的列表页可推进分页位置]
175
- E --> F[持久保存,再影响授权/通知应用]
176
- ```
177
-
178
- ```ts
179
- import { verifyServiceEvent } from './org-protocol.js';
180
- const verifiedEvent = await verifyServiceEvent(eventName, payload, eventContext);
181
- ```
182
-
183
- 四端使用既有 keystore/tokenstore 的 credential_revocations_v1 metadata(浏览器 IndexedDB),保存 version=1、authority 分区 revoked、since_event_seq、cursor、as_of、expires_at。单条推送不推进水位,旧页不覆盖新证据;本地未见 CID 不证明有效,完整 Verify 仍需新鲜状态 proof。
184
-
185
- 兜底列表:连接后一小时首次,此后每小时,重连重新计时。长期凭证扫描:READY 后及在线每 60 秒;断线/关闭/身份变化取消旧代任务。续期成功立即保存完整 CAP、binding、terms、回执、服务版本和信息,供 credential.current 读取;只写当前成员账户,短期凭证保持 long_term=false。自动续期标识 aun-credential-renewal/1 不是跨版本冻结契约。
186
-
187
- ## 6. 高层实体身份和转让编排
188
-
189
- ```mermaid
190
- flowchart LR
191
- A[校验 proposal、owner 和双方完整成员材料] --> B[生成新 P-256 私钥并持久保存 checkpoint]
192
- B --> C[持久保存 rekey 参数后调用]
193
- C --> D[导入匹配新公钥/指纹的证书]
194
- D --> E[新实体签双方 binding/CAP,保存独立 finalize 请求]
195
- E --> F[finalize:提交新成员包并撤旧 CAP]
196
- F --> G[新实体主签、owner token 上传名片]
197
- G --> H[保存 completed 结果,后续可精确重放]
198
- ```
199
-
200
- ```ts
201
- const result = await client.completeOrgTransfer(
202
- originalTransferParams, { aidStore },
203
- );
204
- ```
205
-
206
- checkpoint 固定输入摘要、transfer ID、owner、proposal、原新私钥、公钥、阶段 request ID/nonce/params。恢复不复制旧组织私钥、不生成替代 key;输入或公钥变化报 conflict,缺原候选私钥报 persistence_unavailable。rekey/finalize 使用不同持久 request ID。
207
-
208
- 0.5.22:Python 缺 key 保留 checkpoint 并拒绝;Go 导入前核对证书与 CA 结果指纹;TS/JS 保留 completed,精确同任务返回原结果,重连跳过完成态。不补造缺失的历史恢复材料。
209
-
210
- 组织名片实体主签、owner token 上传,可选当前 owner 附签且全验;现代指纹严格匹配,Org 不开放旧实体上传。完整多签原文、原 CAS、固定 owner/证书代际留待显式重试,重连不自动 PUT;转让/rekey/解散停止旧代待办。通用文件签验见 [Ownership 移植 §4](Ownership2.1-SDK开发与移植指南.md#4-原始文件严格解析与签名),不改变 CAP/binding 算法。
211
-
212
- 解散先以当前 binding/CAP 准备请求,服务确认 dissolved 后才标记实体证书失效,清创建/转让及名片待办和设备/slot 恢复状态;保留磁盘私钥,不追溯失效已加载 signer,后续 AIDStore.load 拒绝该证书。
213
-
214
- ## 7. 服务端兼容实现要点
215
-
216
- | 服务 | 必须保持 |
217
- | --- | --- |
218
- | Credential | register 只收完整 CAP 并签回执;status/列表返回签名证明;revoke 从持久去重恢复原请求摘要、CID 和 revoked,冲突或损坏不当成功。 |
219
- | Org 授权 | 独立验 client signature/AAD;受保护读写验 CAP、binding、request binding 和当前 actor/member。公开 lifecycle 读取按可见性处理。 |
220
- | 跨服务收尾 | 外部登记/吊销不在 Org SQL 事务内,失败保留续跑;创建补偿遇幂等冲突停止自动补偿,槽位提交丢响应先复读原操作。 |
221
- | 转让/通知 | 新换钥前核对代际,以规范 SPKI DER Base64 调 CA;成员替换及新旧 owner 完整包通知同次持久保存,失败按组织轮转重试。 |
222
- | 事件/跨域 | 先验签,再按 responder/event_id/current_aid 去重(最近4096项,不承诺重启 exactly-once);实体跨域重试用上传者域 token,向实体域提交原文件/CAS。 |
223
-
224
- 以上需要配套服务部署;不能从 SDK 版本推断服务已具备。
225
-
226
- ## 8. 错误模型与兼容性
227
-
228
- 保留结构化业务码及 retryable;不要解析错误文本。签名/链/摘要/binding/权限/成员状态/幂等键冲突不盲重试,服务/数据库/证书状态未知可等待但不授权。
229
-
230
- 跨平台必须固定 Unicode/JSON 数值、PEM/DER/Base64 边界、UTC Unix 时间和 exp 开区间;不把 None/undefined/nil 混成相同字段,也不将 Promise/exception/error 写入 wire。
231
-
232
- ## 9. 测试与验收
233
-
234
- | 顺序 | 验收范围 |
235
- | --- | --- |
236
- | 共同向量 | [org-0.8/protocol.json](../../tests/vectors/org-0.8/protocol.json):canonical bytes、DER、CID/hash、binding、receipt、representation 和错误码。 |
237
- | 本地 helper | 创建、签名预检、完整 Verify、请求准备;父链缺证明、错误 CID/版本、过期、代表目标及两层摘要冲突必须拒绝。 |
238
- | Credential RPC | register/status/revoke/列表,持久幂等、失败补偿、撤销推送/分页乱序、重启快照。 |
239
- | Org/恢复 | join/approve/invite/renew/offboard,rekey/finalize 断线、缺 key、完成重放、完整成员包、解散失效。 |
240
- | 网络 | 单域 → cross-SDK → 双域;真实数据库、多实例、浏览器和跨平台分别验收,失败即停并从检查点续跑。 |
241
-
242
- 四端参考:TS/JS 的 src/org-protocol.ts、src/facades.ts;Python 的 aun_core/org_protocol.py、org_facades.py;Go 的 org_protocol.go、org_facades.go。版本专项与真实验收边界见 [四端对齐清单](../../python/CHANGES_FOR_PARITY.md)。
1
+ # 凭证与组织服务 SDK 开发与移植指南
2
+
3
+ 面向 SDK 实现者,按“共同签验 → 请求绑定 → RPC → 持久状态 → 实体恢复”移植。协议为 Org `0.8`,本文对齐当前工作区四 SDK(包元数据 `0.5.27`),不据此声明扩展已发布。§3为结构化凭证,§4为语义凭证,§5—§6为绑定与 Facade,§7为组织编排。应用调用见 [集成指南](凭证与组织服务SDK应用集成指南.md),普通凭证字段以 [详细设计](../../../extensions/services/docs/org/0.8/17-凭证与组织服务详细设计.md) 为准,不以任一语言实现代替协议。
4
+
5
+ | 模式 | 版本与能力 | 验收路径 |
6
+ | --- | --- | --- |
7
+ | 普通结构化凭证 | `aun-cap/1`,非空 `cap` 与结构化约束 | 本文 §3、§5—§6,普通完整 Verify。 |
8
+ | 纯语义凭证 | `aun-cap/semantic-1`,`cap=[]` | §4,来源链、可信正文及语义判断。 |
9
+ | 混合凭证 | `aun-cap/semantic-1`,非空 `cap` | §4,结构化能力与语义范围同时满足。 |
10
+
11
+ ## 1. 实现层次和边界
12
+
13
+ | 层次 | 职责/TS 参考 |
14
+ | --- | --- |
15
+ | 连接/RPC | 认证、Gateway service-plane、设备/slot、重连、token、外层签名;[rpc-pipeline.ts](../../ts/src/client/rpc-pipeline.ts)。 |
16
+ | 协议 | canonical JSON、CAP、binding、回执、代表证明和完整 Verify;[org-protocol.ts](../../ts/src/org-protocol.ts)。 |
17
+ | Facade | 参数和 method 映射,不复制组织服务状态机;[facades.ts](../../ts/src/facades.ts)。 |
18
+ | 身份编排 | 组织实体身份、名片、rekey、checkpoint/恢复;[client.ts](../../ts/src/client.ts)。 |
19
+
20
+ Credential/Org 业务经 Gateway service-plane,不经 Kernel;health/诊断走控制面。服务独立部署、独立数据库,不共享私钥或通过代码 import 耦合。
21
+
22
+ ## 2. 共同密码学和 JSON 规则
23
+
24
+ ### 2.1 两类 canonical JSON
25
+
26
+ | 输入 | 规范 |
27
+ | --- | --- |
28
+ | CAP/binding/receipt/representation/服务事件 | 键按 Unicode code point 排序,字符串 NFC,数字最短十进制;签名时去掉对象对应签名字段。 |
29
+ | org 外层 client_signature 请求摘要 | 排序和数值规则相同,字符串保持原值,不 NFC。 |
30
+
31
+ 两者都拒绝孤立 surrogate、非有限或不安全数值。签名固定 ECDSA P-256/SHA-256,严格标准 Base64 的 ASN.1 DER;不能把 WebCrypto P1363 原样写入。
32
+
33
+ 内部签名片段,unsigned 已按对象规则去除签名字段:
34
+
35
+ ```ts
36
+ import { canonicalJson } from './org-protocol.js';
37
+ const result = signer.sign(Buffer.from(canonicalJson(unsigned)));
38
+ if (!result.ok) throw new Error(result.error.message);
39
+ const envelope = { cert_fingerprint: signer.certFingerprint,
40
+ timestamp: now, signature: result.data.signature };
41
+ ```
42
+
43
+ 封套仅允许 cert_fingerprint、timestamp、signature 及可选 public_key_fingerprint;拒绝 algorithm/kid/value。resolver 支持 PEM/DER bytes/标准 Base64 DER,核对指纹、证书 AID、P-256 和签名时间。
44
+
45
+ ### 2.2 摘要和随机数
46
+
47
+ 摘要为 `sha256:<64位小写hex>`;nonce 为安全随机字节的 base64url,默认 32 字节。时间使用 Unix 秒,整数限制为 ±9007199254740991。普通 CAP 当前验收窗口为 `nbf-300 ≤ now ≤ exp+300`,请求绑定为 `iat-300 ≤ now ≤ exp+300`;签署时点按原有效期检查。
48
+
49
+ 幂等键为 method+request_id,同键换参数摘要报 CREDENTIAL_IDEMPOTENCY_KEY_REUSE/Org IDEMPOTENCY_KEY_REUSE;register 与补偿 revoke 按 method 独立去重。JSON null/字段缺省和语言异步差异不得改变 wire。
50
+
51
+ ## 3. AUN-CAP/1 实现契约
52
+
53
+ ### 3.1 结构验证
54
+
55
+ CAP 必填 ver/cid/iat/iss/sub/aud/nbf/exp/cap/key_version/signature。拒绝未知字段、空身份/受众、非法时间、签名结构及超深父链。根深度缺省 0;子凭证显式为父深度+1,最大 8;security_level 为 low/medium/high,缺省 medium,不能降低业务最低策略。
56
+
57
+ 成员 CAP 固定为:iss=org_id、sub=member_aid、aud 包含 org_id,唯一能力为 org/role.<role>/org_id;ext 绑定 membership_id、binding_version、terms_hash,bound_to_membership=true、delegate=false,不允许 parent delegation。createMemberCredential 必须拒绝冲突覆盖。
58
+
59
+ ### 3.2 验证级别
60
+
61
+ ```mermaid
62
+ flowchart LR
63
+ A[结构、链、各层签名、时间和收窄] --> B[当前 CAP 及父链状态]
64
+ B --> C[实际能力和约束]
65
+ C --> D[实际请求 binding]
66
+ D --> E[成员双签和当前状态]
67
+ E --> F[代表证明、Ownership 和 actor 状态]
68
+ F --> G[重读时钟,复核全部有效窗口]
69
+ G -->|全部通过| H[授权结果]
70
+ ```
71
+
72
+ ```ts
73
+ import { verifyCredential } from './org-protocol.js';
74
+ const checked = verifyCredential(credential, verificationContext);
75
+ ```
76
+
77
+ verifySignature 只做结构/证书/数学验签,可选时间和受众,不授权。verify 的 context 必须绑定实际 audience、subject、能力和 method/target/params/requestId/nonce/requestBinding。
78
+
79
+ 有 parent 时,每个父文档必须取得强 resolveStatusProof;受信 resolver 先验服务签名、信任链/撤销及真实查询 request ID/nonce/target,SDK 再核对 aun-credential-status/1、CID/完整 CAP hash、active、responder 和证明窗口。缺失/畸形为 STATUS_UNKNOWN,明确 CID/hash 不符为 CHAIN_INVALID;不得降为字符串 active 或相信自报 verified。
80
+
81
+ 成员必须有当前 binding 双签及 resolveMembershipStatus=true;代表请求另验当前 Ownership 和 actor 状态。未指定 now 时,所有 resolver/异步等待后重读时钟,复核 CAP、证书、状态 proof、request binding 和代表证明;显式有效 now 按固定时间测试。校验不修改输入。
82
+
83
+ ### 3.3 委托链与约束收窄
84
+
85
+ ```mermaid
86
+ flowchart LR
87
+ A[根签发者:授予中间持有人] --> B[delegate=true,rows≤100,csv/json]
88
+ B --> C[中间持有人签子凭证]
89
+ C --> D[新持有人,rows≤10,仅csv]
90
+ D --> E[parent=根CID,proof_chain直接父在前]
91
+ E --> F[逐层登记并取得当前强状态]
92
+ F --> G[同步完整Verify,执行实际操作]
93
+ ```
94
+
95
+ 以下新增片段使用 Node TS;`issuerSigner`/`delegateSigner` 为 `CredentialSigner`,`businessAid`/`holderAid` 为部署与认证上下文。仓库相对 import 用于 SDK 移植参考。普通凭证完整应用流程见[应用指南 §3](凭证与组织服务SDK应用集成指南.md#3-结构化凭证接入)。
96
+
97
+ ```ts
98
+ import type { JsonObject, CredentialSigner, CredentialVerificationContext } from '@agentunion/fastaun';
99
+ const structuredGrant = (rows: number, formats: string[]): JsonObject => ({
100
+ ns: 'files', action: 'read', resource: '/reports', constraints: [
101
+ { type: 'max_value', field: 'rows', value: rows },
102
+ { type: 'allowed_values', field: 'format', value: formats },
103
+ ],
104
+ });
105
+ ```
106
+
107
+ ```ts
108
+ const rootCap = client.credential.create({
109
+ sub: delegateSigner.aid, aud: [businessAid], delegate: true,
110
+ cap: [structuredGrant(100, ['csv', 'json'])],
111
+ key_version: issuerSigner.certFingerprint,
112
+ }, issuerSigner);
113
+ const childCap = client.credential.create({
114
+ sub: holderAid, aud: [businessAid], parent: rootCap.cid,
115
+ proof_chain: [rootCap], cap: [structuredGrant(10, ['csv'])],
116
+ key_version: delegateSigner.certFingerprint,
117
+ }, delegateSigner);
118
+ ```
119
+
120
+ | 链上关系 | 移植校验 |
121
+ | --- | --- |
122
+ | 身份与层级 | `child.iss=parent.sub`、父 `delegate=true`;根深度0,子为父深度+1,最大8。 |
123
+ | 时间与受众 | 子 `iat/nbf` 不早于父,`exp` 不晚于父,`aud` 为父子集;省略 `exp` 时取签署证书截止时间与父期限的较早者。 |
124
+ | 能力与约束 | 每个子能力由父能力覆盖;上限变小、下限变大、允许集取子集、拒绝集取超集。资源路径收窄使用 `path_prefix`。 |
125
+ | 原件与状态 | 保存直接父到根的原件;完整 Verify 为当前凭证及每个父 CID 提供当前权威强证明。 |
126
+
127
+ ### 3.4 同步完整 Verify 与约束端口
128
+
129
+ ```mermaid
130
+ flowchart TD
131
+ A[固定实际请求与认证持有人] --> B[异步获取全部签署者证书]
132
+ B --> C[异步查询每层状态,按§6.5验真]
133
+ C --> D[只读证书Map与状态Map]
134
+ D --> E[同步Verify:实际参数、单位、IP与限速端口]
135
+ E -->|全部通过| F[执行同一实际请求]
136
+ E -->|抛错或状态未知| G[终止业务执行]
137
+ ```
138
+
139
+ 宿主端口 `structuredHost` 提供以下契约;`rateLimit.reserve` 同步检查真实限速状态,返回 `false` 即拒绝。限速服务需要网络时,先建立可供本次同步验收使用的预留。
140
+
141
+ ```ts
142
+ interface StructuredHostPorts {
143
+ authorityFor(issuerAid: string): string;
144
+ captureSession(): { aid: string; current(): boolean };
145
+ rateLimit: { reserve(cid: string, subject: string, field: string,
146
+ unit: string, limit: number): boolean };
147
+ execute(operation: JsonObject): Promise<unknown>;
148
+ }
149
+ ```
150
+
151
+ 先载入 §5.4 请求绑定 helper 和 §6.5 可信适配器,再顺序执行下列片段。`structuredHost`/`trustedCertificates` 由宿主按对应接口提供。
152
+
153
+ ```ts
154
+ const ordinarySession = structuredHost.captureSession();
155
+ const queryActorAid = ordinarySession.aid;
156
+ const documents = [childCap, rootCap];
157
+ const certificates = new Map<string, string>();
158
+ const statusProofs = new Map<string, Record<string, unknown>>();
159
+ for (const document of documents) {
160
+ const signature = ordinarySignature(document.signature);
161
+ certificates.set(signature.cert_fingerprint, await trustedCertificates.load(
162
+ String(document.iss), signature.cert_fingerprint, signature.timestamp));
163
+ }
164
+ ```
165
+
166
+ ```ts
167
+ for (const document of documents) {
168
+ const serviceAid = structuredHost.authorityFor(String(document.iss));
169
+ const query = bindOrdinaryRequest('credential.status', serviceAid,
170
+ String(document.cid), { credential_cid: document.cid, include_chain: true });
171
+ const raw = await client.credential.status(query);
172
+ statusProofs.set(String(document.cid),
173
+ await verifyStructuredStatus(raw, document, query, serviceAid));
174
+ }
175
+ ```
176
+
177
+ ```ts
178
+ const operation = bindOrdinaryRequest('files.read', businessAid,
179
+ String(childCap.cid), { rows: 5, format: 'csv' });
180
+ ```
181
+
182
+ ```ts
183
+ const verificationContext: CredentialVerificationContext = {
184
+ audience: businessAid, subject: holderAid, rootIssuer: issuerSigner.aid,
185
+ ns: 'files', action: 'read', resource: '/reports', params: operation,
186
+ method: 'files.read', target: businessAid, requestBinding: operation.request_binding,
187
+ requestId: String(operation.request_id), nonce: String(operation.nonce),
188
+ proofChain: [rootCap], resolveCertificate: fp => certificates.get(fp),
189
+ resolveStatusProof: cid => statusProofs.get(cid),
190
+ checkRateLimit: (cid, subject, field, unit, limit) =>
191
+ structuredHost.rateLimit.reserve(cid, subject, field, unit, limit),
192
+ };
193
+ ```
194
+
195
+ ```ts
196
+ if (!ordinarySession.current() || holderAid !== queryActorAid) throw new Error('认证上下文已变化');
197
+ client.credential.verify(childCap, verificationContext);
198
+ await structuredHost.execute(operation);
199
+ ```
200
+
201
+ | 约束 | 真实上下文 |
202
+ | --- | --- |
203
+ | `max_value/min_value/range/allowed_values/denied_values/regex/path_prefix` | `params` 中本次请求字段;字段缺失即拒绝。 |
204
+ | 带单位的数值 | 同时提供 `units[field]`;须与凭证单位一致。 |
205
+ | `time_window/ip_whitelist` | 当前 UTC 时间与真实连接 `clientIp`。 |
206
+ | `rate_limit` | 同步 `checkRateLimit(cid,subject,field,unit,limit)`。 |
207
+
208
+ Node TS 的 `resolveCertificate`、`resolveStatusProof`、`checkRateLimit` 均同步。Map 在预加载完成后作为本次只读快照;成员/代表场景再补 §3.2 所列当前绑定及身份端口。
209
+
210
+ ## 4. 语义凭证移植契约
211
+
212
+ 以 TS 当前实现核对调用顺序和参数;wire 以[正式规范](../../../extensions/services/docs/org/0.8/语义凭证扩展规范.md)为准。应用流程及 E01—E36 对照见[应用指南 §4](凭证与组织服务SDK应用集成指南.md#4-语义凭证接入)。代码中的 app/store/rpcCall 为宿主端口。
213
+
214
+ ### 4.1 公开入口与方法分流
215
+
216
+ ```mermaid
217
+ flowchart LR
218
+ A[credential facade 输入] --> B{方法及语义标记}
219
+ B -->|旧参数| C[普通 Credential 路径]
220
+ B -->|语义参数| D[语义字段校验]
221
+ D --> E[补管理 binding,调用六个 wire 方法]
222
+ E --> F[验证响应签名、请求与修订绑定]
223
+ F --> G[保存状态,返回类型化结果]
224
+ ```
225
+
226
+ | 能力 | Python client.credential | TS/JS client.credential | Go client.Credential() |
227
+ | --- | --- | --- | --- |
228
+ | 管理 | register/revise/get/status/revoke/revocations | register/revise/get/status/revoke/revocations | Register/Revise/Get/Status/Revoke/Revocations |
229
+ | 协调 | prepare_usage/commit_usage/abort_usage | prepareUsage/commitUsage/abortUsage | PrepareUsage/CommitUsage/AbortUsage |
230
+ | 签署 | sign_credential/sign_metadata/sign_allocation/sign_usage_record | signCredential/signMetadata/signAllocation/signUsageRecord | SignCredential/SignMetadata/SignAllocation/SignUsageRecord |
231
+ | 单项验证 | verify_original/verify_metadata/verify_receipt/verify_status/verify_usage_record | verifyOriginal/verifyMetadata/verifyReceipt/verifyStatus/verifyUsageRecord | VerifyOriginal/VerifyMetadata/VerifyReceipt/VerifyStatus/VerifyUsageRecord |
232
+ | 包/业务 | build_package/verify_package/build_request/verify_request/build_decision_input | buildPackage/verifyPackage/buildRequest/verifyRequest/buildDecisionInput | BuildPackage/VerifyPackage/BuildRequest/VerifyRequest/BuildDecisionInput |
233
+ | 消息证据 | archive_message/create_evidence_ref/resolve_evidence/verify_message_body | archiveMessage/createEvidenceRef/resolveEvidence/verifyMessageBody | ArchiveMessage/CreateEvidenceRef/ResolveEvidence/VerifyMessageBody |
234
+
235
+ | 同名方法 | 任一语义标记字段 |
236
+ | --- | --- |
237
+ | register | original、metadata、lineage_evidence、authority |
238
+ | status/get_status/getStatus/GetStatus | expected_revision、policy、authority、audience、query |
239
+ | revoke | authority |
240
+ | revocation_list/revocationList/RevocationList | authority、scope、after_event_seq |
241
+
242
+ 带语义标记的输入始终进入语义校验;cursor/limit 属于共有字段。get/revise/revocations/usage 快捷入口直接走语义路径,create/verify 保持普通契约。Python 使用 async;TS/JS 语义 helper 返回 Promise;Go 使用 context/value/error。TS/JS 的 buildPackage 接受 lineage 数组,Python 接受参数对象,Go 接受 context 和 lineage。
243
+
244
+ TS/JS 提供 verifyAllocation/verifyAllocationStatus;Go 通过 VerifyUsageRecord 的 usage-allocation/allocation-status 域验证配套对象,再完成业务绑定及额度预留。
245
+
246
+ ### 4.2 原字节、签名域与时间
247
+
248
+ ```mermaid
249
+ flowchart LR
250
+ A[SignedJsonBytes.bytes_base64] --> B[标准 Base64 解码,保留原字节]
251
+ B --> C[严格 UTF-8 JSON,检查重复键与数值]
252
+ C --> D[完整 canonical 摘要,包含嵌套签名]
253
+ C --> E[按对象提取签名 payload,加签名域]
254
+ E --> F[证书指纹、可信 AID、历史资格与签名]
255
+ ```
256
+
257
+ ```ts
258
+ import { decodeSignedJson, digest, signRecord } from './semantic-protocol.js';
259
+ const signedOriginal = await signRecord('semantic-credential', unsignedOriginal, issuerSigner);
260
+ const parsedOriginal = decodeSignedJson(signedOriginal);
261
+ const originalHash = digest(parsedOriginal);
262
+ const signedMetadata = await signRecord('registration-metadata', unsignedMetadata, confirmerSigner);
263
+ ```
264
+
265
+ SignedJsonBytes 使用标准带 padding Base64;完整摘要含嵌套签名。metadata 的签名投影只省略 confirmation.signature.signature,其余对象只省略自身 signature.signature。扩展签名保留 profile、指纹和 timestamp。
266
+
267
+ | 签名域 | 对象 |
268
+ | --- | --- |
269
+ | semantic-credential/registration-metadata | 原件/确认 metadata |
270
+ | registration-receipt/credential-status/semantic-crl | 登记回执/状态/撤销页 |
271
+ | usage-allocation/allocation-status | 跨权威分配/分配状态 |
272
+ | usage-transition/usage-checkpoint/usage-acceptance/ledger-abort | 切换/原账本检查点/接管/恢复确认 |
273
+
274
+ | 时间检查 | TS 当前规则 |
275
+ | --- | --- |
276
+ | 管理绑定声明 | 0 < exp-iat ≤ 300 |
277
+ | 当前绝对窗口 | iat/nbf/as_of 减 300 ≤ now ≤ exp/valid_until 加 300,按对象字段检查 |
278
+ | 业务发送/首次受理 | 发送 expires_at 在未来 300 秒内;首次受理 expires_at-600 ≤ now ≤ expires_at+300 |
279
+ | online | 声明≤30秒,本地查询到确认耗时<30秒 |
280
+ | bounded | TTL 为1—300秒,本地观察耗时<TTL,同时满足绝对窗口 |
281
+ | 证书/签署时点 | DER 指纹与可信身份匹配;原签署时点在原证书有效期内,当前窗口单独检查 |
282
+
283
+ ### 4.3 管理绑定与业务摘要
284
+
285
+ ```mermaid
286
+ flowchart TD
287
+ A[持久保存管理 ID、nonce 与完整业务参数] --> B{调用层}
288
+ B -->|facade| C[按当前时钟自动补八字段 binding]
289
+ B -->|low-level| D[buildManagementBinding 后提交完整 wire params]
290
+ C --> E[响应验真与持久状态]
291
+ D --> E
292
+ F[固定业务操作及来源链修订/回执摘要] --> G[buildRequest 补 params_hash 与业务 binding]
293
+ G --> H[刷新同修订 status,保留 request_hash]
294
+ ```
295
+
296
+ ```ts
297
+ import { isJsonObject } from '@agentunion/fastaun';
298
+ import { buildManagementBinding } from './semantic-protocol.js';
299
+ const method = 'credential.semantic.register';
300
+ const request_binding = buildManagementBinding(method, credentialServiceAid, registrationInput);
301
+ const wireParams = { ...registrationInput, request_binding };
302
+ if (isJsonObject(wireParams)) {
303
+ const registered = await client.call(method, wireParams);
304
+ }
305
+ ```
306
+
307
+ ```ts
308
+ const grant = await client.credential.buildPackage(lineage);
309
+ const request = await client.credential.buildRequest({
310
+ ...businessOperation, grant,
311
+ });
312
+ const requestHash = await client.credential.requestHash(request);
313
+ ```
314
+
315
+ | 层次 | 固定材料 |
316
+ | --- | --- |
317
+ | 管理 binding | method、target、params_hash、request_id、nonce、iat、exp、credential_cid |
318
+ | 管理 params_hash | 去根 request_binding 与服务认证注入字段,保留业务字段及全部嵌套签名 |
319
+ | 业务 request_hash | 真实操作、params_hash、各层 revision 与 receipt_hash;status 可独立刷新 |
320
+ | nonce | 管理、业务、online 查询分别生成,至少128位安全随机源 |
321
+ | 幂等恢复 | 保存原请求;状态/执行/transition 查询确认原结果 |
322
+
323
+ TS/JS 第二参数 {serviceAid}、Python target、Go NewCredentialFacade(client, serviceAID) 指定登记目标。Go low-level 可用 PrepareManagement 生成完整参数;facade 接收业务输入并自动补绑定。
324
+
325
+ ### 4.4 来源链验证与业务宿主
326
+
327
+ ```mermaid
328
+ flowchart TD
329
+ A[buildPackage:root 到 leaf] --> B{逐层版本}
330
+ B -->|aun-cap/1| C[legacyVerifier:完整 Verify 与原权威强状态]
331
+ B -->|semantic-1| D[原件/metadata/回执/status 验真]
332
+ C --> E[核对身份、delegate、深度、期限、aud 及父链]
333
+ D --> E
334
+ E --> F[verifyRequest:真实认证 actor/service 与固定 hash]
335
+ F --> G[逐层 VerifiedBody 与可信 facts]
336
+ G --> H[DecisionInput,模型三态与业务后置]
337
+ H --> I[当前状态复核,原子预留及幂等执行]
338
+ ```
339
+
340
+ ```ts
341
+ const packageReport = await client.credential.verifyPackage(request.grant, packageContext);
342
+ const requestReport = await client.credential.verifyRequest(request, authenticated, {
343
+ representedActor: verifyRepresentation,
344
+ });
345
+ const decisionInput = await client.credential.buildDecisionInput(request, bodies, facts, policy);
346
+ ```
347
+
348
+ | 边界 | 宿主提供 |
349
+ | --- | --- |
350
+ | 身份/确认权 | 真实认证上下文、原签署者、历史授权/policy 依据、当前代表关系 |
351
+ | 来源链 | 各层可信 serviceFor、旧父原验证器、全部适用正文与范围交集 |
352
+ | 状态 | 每层 query 与真实单调耗时,执行前复核 |
353
+ | 业务 | 前置名单/本次确认、固定模型输入、三态结果、后置条件、完整缓存键 |
354
+ | 执行 | 原业务幂等键、全部账户预留、Unknown 查询与结算 |
355
+
356
+ verifyPackage 报告 checked/required;verifyRequest 报告认证及请求绑定。两者的剩余业务职责由宿主完成,参考[参考宿主](../../examples/semantic-credential/reference-host/host.py)。
357
+
358
+ ### 4.5 消息证据适配
359
+
360
+ ```mermaid
361
+ flowchart LR
362
+ A[认证收件:envelope 原字节及接收记录] --> B[归档 ACL、对象版本与解密材料]
363
+ B --> C[inline/aun_ref 解析并核对原字节 hash]
364
+ C --> D[发送者历史身份、原收件集合/Merkle 证明]
365
+ D --> E[按原设备密钥解密,核对 payload 原字节]
366
+ E --> F[上下文与附件验真,形成 VerifiedBody]
367
+ ```
368
+
369
+ signed_core_hash 覆盖 ciphertext、tag、V2 canonical AAD、recipients_digest 的二进制拼接:
370
+
371
+ ```ts
372
+ import { canonicalJson as messageCanonical } from './v2/crypto/canonical.js';
373
+ import { bytesDigest } from './semantic-protocol.js';
374
+ const signedCoreHash = bytesDigest(Buffer.concat([
375
+ Buffer.from(envelope.ciphertext, 'base64'), Buffer.from(envelope.tag, 'base64'),
376
+ Buffer.from(messageCanonical(envelope.aad)), Buffer.from(envelope.recipients_digest, 'hex'),
377
+ ]));
378
+ ```
379
+
380
+ ```ts
381
+ const body = await client.credential.verifyMessageBody(original, {
382
+ ...recordContext, selfAid: originalRecipientAid, selfDeviceId: originalDeviceId,
383
+ resolveDecryptionKeys, resolveSenderKey, verifySenderIdentity,
384
+ resolvers: archiveResolvers,
385
+ });
386
+ ```
387
+
388
+ | 材料 | 适配要求 |
389
+ | --- | --- |
390
+ | 原 envelope | archiveMessage 保留输入原字节;aun_ref resolver 返回同对象版本 |
391
+ | P2P/群 | P2P 固定原 to;群固定 group_id/epoch/state_commitment 与原收件证明 |
392
+ | text+溯源 | 独立验 text 原件签名及 authorization.evidence |
393
+ | 依赖材料 | context 使用消息原件,attachment 使用原始字节;待补项进入 indeterminate |
394
+ | 留存 | 保存 received_by/received_at/source、历史证书/密钥和 retain_until |
395
+
396
+ ### 4.6 状态新鲜度与序号
397
+
398
+ ```mermaid
399
+ flowchart TD
400
+ A[记录本进程单调时钟起点] --> B[查询固定修订的状态]
401
+ B --> C[验签、原修订、独立 query 与绝对窗口]
402
+ C --> D[读取 authority/CID/aud 最大 state_seq]
403
+ D --> E[合并状态并保存首次观察]
404
+ E --> F[异步等待后重读时钟]
405
+ F -->|耗时在策略内| G[继续完整包与业务验收]
406
+ F -->|达到期限| H[重新查询]
407
+ ```
408
+
409
+ ```ts
410
+ const began = performance.now();
411
+ const proof = await client.credential.status(statusInput, operationOptions);
412
+ await client.credential.verifyStatus(proof, { ...recordContext,
413
+ expectedServiceAid: credentialServiceAid, policy: statusInput.policy,
414
+ query: statusInput.query, elapsedSeconds: (performance.now() - began) / 1000,
415
+ });
416
+ ```
417
+
418
+ online query 绑定固定业务 request_hash 与独立 nonce;bounded 输入包含 max_staleness_seconds。相同证明使用最早单调观察计时,异步验签/存储等待计入耗时。重启后 online 重新查询;当前 head、撤销证据和更大 state_seq 持久保留。
419
+
420
+ ### 4.7 持久状态、CRL 与恢复
421
+
422
+ ```mermaid
423
+ flowchart TD
424
+ A[确认当前身份与连接代次] --> B[载入独立语义状态]
425
+ B --> C[验签 CRL 页,固定 scope/after/limit/upper]
426
+ C --> D[按最大序号合并撤销与修订]
427
+ D --> E[原子保存页标识、状态与分页进度]
428
+ E -->|有 next_cursor| C
429
+ E -->|末页| F[推进 upper_event_seq]
430
+ F --> G[恢复原业务键、预留与 transition,重新取状态]
431
+ ```
432
+
433
+ SDK 内部适配端口示例:
434
+
435
+ ```ts
436
+ import { SemanticCredentialFacade } from './semantic-facade.js';
437
+ const semantic = new SemanticCredentialFacade({
438
+ call: rpcCall, aid: actorAid, currentAid: signer,
439
+ _semanticContextKey: () => contextKey,
440
+ _semanticVerificationContext: () => recordContext,
441
+ _loadSemanticState: () => store.loadSemantic(actorAid),
442
+ _saveSemanticState: state => store.saveSemanticAtomic(actorAid, state),
443
+ });
444
+ ```
445
+
446
+ | 存储范围 | 持久内容 |
447
+ | --- | --- |
448
+ | 普通 credential_revocations_v1 | authority 分区撤销、事件序号与旧列表进度 |
449
+ | 语义持久状态 | scope 进度、页标识、最大 state_seq、撤销、请求 ID/nonce 绑定、分配状态 |
450
+ | 应用账本 | 业务请求、DecisionRecord、原子预留、Unknown、累计账户及 transition 材料 |
451
+ | 身份与归档 | 原 AID/store、历史证书、原件/修订、解密能力及 ACL |
452
+
453
+ 持久 metadata 键:TS 为 semantic_credentials_v1,Python/JS 为 semantic_credential_state_v1,Go 为 semantic_revocations_v1。
454
+
455
+ CRL 首页 cursor=null,续页保持原 scope/after/limit,使用新管理 ID/nonce;next_cursor=null 才完成本轮水位。scope 改变从0建立独立进度,推送只合并自身证据。浏览器使用 IndexedDB,Node/Python/Go 使用各自持久 store。
456
+
457
+ ### 4.8 额度与 UsageRef 切换
458
+
459
+ ```mermaid
460
+ flowchart TD
461
+ A[确认 UsageContract 与全部账户] --> B[同权威原子预留,跨权威先验 allocation 与在线 active 状态]
462
+ B --> C{执行结果}
463
+ C -->|committed| D[结算累计值]
464
+ C -->|Unknown| E[保留预留并查询原结果]
465
+ C -->|确认无效果| F[释放预留]
466
+ G[prepareUsage:固定 candidate/fence] --> H[冻结并结清 in_flight/Unknown/分配]
467
+ H --> I[checkpoint 与 acceptance 逐账户连续]
468
+ I --> J{协调终态}
469
+ J -->|commit| K[CAS 原子发布新 UsageRef]
470
+ J -->|abort| L[按已签 recovery 与 ledger-abort 恢复]
471
+ ```
472
+
473
+ ```ts
474
+ const checkpoint = await client.credential.signUsageRecord(
475
+ 'usage-checkpoint', checkpointInput, ledgerSigner,
476
+ );
477
+ await client.credential.verifyUsageRecord('usage-checkpoint', checkpoint, recordContext);
478
+ const acceptance = await client.credential.signUsageRecord(
479
+ 'usage-acceptance', acceptanceInput, newLedgerSigner,
480
+ );
481
+ ```
482
+
483
+ | 对象/边界 | 必须匹配 |
484
+ | --- | --- |
485
+ | 全部切换材料 | service_aid、CID、original_hash、transition_id、fence、candidate_hash、from/to UsageRef |
486
+ | checkpoint | 完整账户键/累计值、in_flight=0、unknown=0、reserved='0'、已闭合分配 |
487
+ | acceptance | 同组检查点摘要、逐键累计值、新 usage 和目标权威 |
488
+ | allocation | 父子原件/epoch/aud、精确十进制额度,active→draining→closed |
489
+ | commit/abort | 原持久请求与服务 CAS;get(transition_id) 返回已签 prepared/committed/aborted |
490
+ | ledger-abort | aborted_receipt_hash、同 fence/UsageRef;恢复资格取 recovery |
491
+
492
+ prepareUsage/commitUsage/abortUsage 分别映射 credential.semantic.revise 的 usage_prepare/usage_commit/usage_abort,并直接返回 UsageTransitionReceipt。签验 helper 提供材料校验,宿主账本完成授权关系、账户原子性与预留结算;应用调用图见[应用指南 §4.8—§4.9](凭证与组织服务SDK应用集成指南.md#48-额度分配与执行结果e27e30)。
493
+
494
+ ### 4.9 错误与 E2E 验收入口
495
+
496
+ ```mermaid
497
+ flowchart LR
498
+ A[共同向量与类型检查] --> B[helper 与 facade 契约]
499
+ B --> C[六 RPC、十错误码、三协调动作]
500
+ C --> D[真实四 SDK 与 AUN 消息]
501
+ D --> E[双域、浏览器与原身份冷恢复]
502
+ ```
503
+
504
+ ```ts
505
+ import { SemanticRPCFailure } from '@agentunion/fastaun';
506
+ try {
507
+ await client.credential.revise(savedRevisionInput, operationOptions);
508
+ } catch (error) {
509
+ if (error instanceof SemanticRPCFailure) await app.recordError(error.code, error.data, error.retryable);
510
+ else throw error;
511
+ }
512
+ ```
513
+
514
+ 错误保留 JSON-RPC code=-32000、完整 data、稳定 SEMANTIC_* error_code 与 retryable;STATE_UNAVAILABLE 默认可重试。冲突先读 current/transition 确认原操作;固定材料使用原幂等键续跑。
515
+
516
+ | 场景 | 验收入口 |
517
+ | --- | --- |
518
+ | E01 普通兼容 | org-protocol.ts/facades.ts;旧向量与普通 Credential/Org E2E |
519
+ | E02—E18 材料、正文、来源链、确认与 CAS | semantic-protocol.ts/semantic-facade.ts;应用指南 §4.2—§4.7 |
520
+ | E19—E26 状态、三态判断、缓存与重传 | 本文 §4.4/§4.6—§4.7;真实宿主与 runner |
521
+ | E27—E31 额度、Unknown、分配与切换 | 本文 §4.8;参考账本及 E2E usage/transition 场景 |
522
+ | E32—E36 归档、重启、证书、双域与入口 | 本文 §4.2/§4.5/§4.7/§4.9;CLI 与真实浏览器 |
523
+
524
+ TS 基准:[semantic-protocol.ts](../../ts/src/semantic-protocol.ts)/[semantic-facade.ts](../../ts/src/semantic-facade.ts)/[facades.ts](../../ts/src/facades.ts)。四端参数映射按各自公开签名适配;六 RPC 与错误字段见[RPC 手册](09-凭证服务RPC手册.md#语义凭证公共约定),E01—E36 逐项见[scenarios.json](../../examples/semantic-credential/scenarios.json)。CN/本地 aid.com、固定模型、1/60/300秒 TTL 与365天 retain_until 的实际证据边界见[示例指南](../../examples/semantic-credential/示例使用指南.md#5-本轮验证记录与边界)。
525
+
526
+ ## 5. Membership binding、回执与请求绑定
527
+
528
+ ### 5.1 Binding
529
+
530
+ 成员侧和组织侧签同一不可变条款,签名 payload 去掉 member_signature/org_signature;绑定 membership_id、binding_version、terms_hash、request ID/nonce 和时间窗。binding 不是能力凭证,当前成员状态由 Org 保存。
531
+
532
+ ```ts
533
+ import { createMembershipBinding, signMembershipBinding } from './org-protocol.js';
534
+ const memberSigned = createMembershipBinding(bindingOptions, memberSigner);
535
+ const bothSigned = signMembershipBinding(memberSigned, orgSigner, 'org');
536
+ ```
537
+
538
+ 申请先成员签,审批者签组织侧;创建/邀请/续期的组织侧由实体签。每个邀请目标保存独立 binding/nonce/CAP/登记授权。
539
+
540
+ ### 5.2 登记授权和回执
541
+
542
+ ```mermaid
543
+ sequenceDiagram
544
+ participant I as issuer/Org SDK
545
+ participant O as Org 服务
546
+ participant C as Credential 服务
547
+ I->>O: 完整双签 binding、CAP 和登记授权
548
+ O->>C: register:原 CID、request ID、nonce 和请求绑定
549
+ C-->>O: 服务签名 registrationReceipt
550
+ O->>O: 核对服务、registrar=iss、CID/全文摘要、原请求和有效期
551
+ O->>O: 最终化成员及版本
552
+ O-->>I: 完整成员包
553
+ ```
554
+
555
+ registration_authorization 由 issuer 签,覆盖完整 CAP hash、CID、原 request ID/nonce、Credential target、relay 和期限;转交服务不是 registrar。回执只是登记见证,不代替授权;公开验回执和服务最终化均按当前窗口严格检查。
556
+
557
+ 受限跨期恢复只接受原期限内已持久且已发送的同一已批准操作,固定 CID/request ID/nonce、双签、登记授权和成员 CAS。原回执仅作私有历史来源,仍须新鲜签名状态绑定同一 CAP/全文摘要,并确认当前成员版本已生效;只记录既成结果,不重登记/重签。无原回执的 generic 恢复还须 registrationConfirmedAt,仅保存 issuer checkpoint,不导入 holder/发送 issued;holder 交付仍需有效原回执。unknown、revoked、未提交或不匹配拒绝,详见 [审查 §7.14](../../../extensions/services/docs/org/0.8/凭证与组织服务及四SDK审查报告.md)。
558
+
559
+ ### 5.3 Org request binding
560
+
561
+ ```mermaid
562
+ flowchart LR
563
+ A[业务参数+AAD+授权包] --> B[去 request_binding、client_signature、下划线字段、representation_proof]
564
+ B --> C[业务摘要:request binding/代表证明]
565
+ C --> D[完整准备后的参数]
566
+ D --> E[仅去 client_signature 和下划线字段,重算外层摘要]
567
+ E --> F[RPC 管线生成 client_signature]
568
+ ```
569
+
570
+ ```ts
571
+ const prepared = client.org.prepareRequest(
572
+ 'org.member.get', params, orgServiceAid, signer,
573
+ );
574
+ const result = await client.org.member.get(prepared);
575
+ ```
576
+
577
+ 两层 params_hash 不可复用。所有 org.* 加 aad.org_id,不匹配即拒绝;只有 lifecycle.list/health/status 允许 null。外层签名覆盖完整 method、AID、timestamp 和外层摘要,服务端独立重算。
578
+
579
+ 首次 join/inviteAccept 使用 membership_id 专用 admission binding,复用原 request ID/nonce,不含 credential_cid,不要求已有 active CAP。本人/代表都须目标成员签名;仅 for≠signer.aid 加 admission representation proof 和当前 Ownership/actor 检查。inviteAccept 保留原组织签名、CAP 模板及登记授权;已有绑定/签名冲突不能重写。
580
+
581
+ ### 5.4 Credential request binding
582
+
583
+ register/revoke/status/revocationList 均绑定真实 method、target、参数摘要、request ID/nonce、iat/exp 和 CID,满足 exp≤iat+300。仅 revocationList 的 CID 固定为空,摘要仍包含 issuer、cursor、limit、since_event_seq。
584
+
585
+ 普通 `aun-cap/1` 由调用方准备绑定。先合并业务字段、`request_id`、`nonce`,再对同一完整参数对象生成摘要;语义路径的自动绑定见 §4。
586
+
587
+ ```ts
588
+ import { randomBytes, randomUUID } from 'node:crypto';
589
+ import { buildRequestBinding } from '../../ts/src/org-protocol.js';
590
+ function bindOrdinaryRequest(method: string, target: string, cid: string,
591
+ fields: JsonObject): JsonObject {
592
+ const request_id = randomUUID(), nonce = randomBytes(32).toString('base64url');
593
+ const params = { ...fields, request_id, nonce }, now = Math.floor(Date.now() / 1000);
594
+ return { ...params, request_binding: buildRequestBinding(
595
+ method, target, params, cid, nonce, request_id, now, now + 300) as JsonObject };
596
+ }
597
+ ```
598
+
599
+ ```ts
600
+ const statusQuery = bindOrdinaryRequest('credential.status', credentialServiceAid,
601
+ String(childCap.cid), { credential_cid: childCap.cid, include_chain: true });
602
+ const rawStatus = await client.credential.status(statusQuery);
603
+ const trustedStatus = await verifyStructuredStatus(
604
+ rawStatus, childCap, statusQuery, credentialServiceAid);
605
+ ```
606
+
607
+ 业务 Verify 的 `params`、`requestId`、`nonce`、`requestBinding` 取同一请求对象。摘要 helper 按协议排除根 `request_binding`;原参数及幂等 ID 在重试前持久保存。
608
+
609
+ ## 6. Facade 和 RPC 映射
610
+
611
+ ### 6.1 四端公共能力
612
+
613
+ | 能力 | Python | TS/JS | Go |
614
+ | --- | --- | --- | --- |
615
+ | facade | client.credential/org | client.credential/org | Credential()/Org() |
616
+ | 请求准备 | org.prepare_request | org.prepareRequest | Org().PrepareRequest |
617
+ | 完整验证 helper | verify_credential | verifyCredential | VerifyCredential |
618
+
619
+ 协议 helper 和 facade 命名分开;创建高层入口分别为 org.lifecycle.create、client.createOrg、client.CreateOrg,不能将概念名当四端统一方法名。
620
+
621
+ ### 6.2 当前方法集合
622
+
623
+ Credential:register/revoke/status(getStatus 别名)/revocationList;本地 create/verifySignature/verify/verifyRegistrationReceipt/current。Org:lifecycle、member、credential、audit.query、authz.check;具体方法见 [RPC 手册](09-组织服务RPC手册.md)。
624
+
625
+ 保留的 resource.create/task.create/host.add/workflow.create 返回 NOT_IMPLEMENTED,其他未注册同类方法返回 Method not found。membership.reserve/get/commit/release 为内部受限接口,member.add 仅服务内部最终化使用。
626
+
627
+ ### 6.3 异步约定
628
+
629
+ 普通凭证/Org 的 Python RPC async、本地协议 helper 同步;Node TS 协议 helper 同步,浏览器 WebCrypto helper 返回 Promise,Go 返回 value/error。公共文件 AIDStore.sign/verify 的 TS/JS 接口均为 Promise<Result>;语义 facade 的签验也使用异步接口,见§4。这些差异不能进入 wire。
630
+
631
+ ### 6.4 撤销同步与持久快照(0.5.22)
632
+
633
+ ```mermaid
634
+ flowchart LR
635
+ A[推送/列表页] --> B[验证服务、目标、签名和时间窗]
636
+ B --> C[复核身份、连接和 worker 代次]
637
+ C --> D[读取当前 authority 快照,逐 CID 合并最大 event_seq]
638
+ D --> E[仅连续覆盖的列表页可推进分页位置]
639
+ E --> F[持久保存,再影响授权/通知应用]
640
+ ```
641
+
642
+ ```ts
643
+ import { verifyServiceEvent } from './org-protocol.js';
644
+ const verifiedEvent = await verifyServiceEvent(eventName, payload, eventContext);
645
+ ```
646
+
647
+ 四端使用既有 keystore/tokenstore 的 credential_revocations_v1 metadata(浏览器 IndexedDB),保存 version=1、authority 分区 revoked、since_event_seq、cursor、as_of、expires_at。单条推送不推进水位,旧页不覆盖新证据;本地未见 CID 不证明有效,完整 Verify 仍需新鲜状态 proof。
648
+
649
+ 兜底列表:连接后一小时首次,此后每小时,重连重新计时。长期凭证扫描:READY 后及在线每 60 秒;断线/关闭/身份变化取消旧代任务。续期成功立即保存完整 CAP、binding、terms、回执、服务版本和信息,供 credential.current 读取;只写当前成员账户,短期凭证保持 long_term=false。自动续期标识 aun-credential-renewal/1 不是跨版本冻结契约。
650
+
651
+ ### 6.5 普通状态证明的可信适配
652
+
653
+ ```mermaid
654
+ flowchart LR
655
+ A[原status查询与完整CAP] --> B[核对服务、认证接收者、ID和nonce]
656
+ B --> C[核对版本、CID、完整hash和凭证身份]
657
+ C --> D[证书CA链与当前撤销状态]
658
+ D --> E[原签署资格、DER数学验签与时间窗]
659
+ E -->|active| F[保存可信proof供同步Verify读取]
660
+ E -->|其他或未知| G[终止授权]
661
+ ```
662
+
663
+ 以下为应用侧参考适配器,移植基准是 [statusFresh](../../ts/src/org-protocol.ts)。`trustedCertificates.load` 由宿主从可信配置取得证书,完成 CA 链与当前撤销检查;签名验算在下方实现。`ordinarySession` 固定 §3.4 的认证身份与连接代次。
664
+
665
+ ```ts
666
+ import { canonicalJson, digest, validateRequestBinding } from '../../ts/src/org-protocol.js';
667
+ import { certFingerprint, publicKeyFingerprint, isStrictBase64,
668
+ verifySignatureWithCert } from '../../ts/src/cert-utils.js';
669
+ import { validateAgentMdCertificate } from '../../ts/src/agent-md-schema.js';
670
+ interface TrustedCertificatePorts {
671
+ load(aid: string, fingerprint: string, signedAt: number): Promise<string>;
672
+ }
673
+ type OrdinaryObject = Record<string, unknown>;
674
+ const ordinaryInteger = (value: unknown): value is number =>
675
+ typeof value === 'number' && Number.isSafeInteger(value);
676
+ ```
677
+
678
+ ```ts
679
+ function ordinaryObject(value: unknown): OrdinaryObject {
680
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
681
+ throw new Error('证明对象无效');
682
+ return value as OrdinaryObject;
683
+ }
684
+ interface OrdinarySignature extends OrdinaryObject {
685
+ cert_fingerprint: string; timestamp: number; signature: string;
686
+ public_key_fingerprint?: string;
687
+ }
688
+ ```
689
+
690
+ 标准 DER 的两个正整数须使用最短编码:
691
+
692
+ ```ts
693
+ function ordinaryDerEnd(bytes: Buffer, offset: number): number {
694
+ const length = bytes[offset + 1] ?? 0, first = bytes[offset + 2] ?? 0;
695
+ if (bytes[offset] !== 2 || length < 1 || length > 33 || offset + 2 + length > bytes.length
696
+ || first >= 128 || first === 0 && (length === 1 || (bytes[offset + 3] ?? 0) < 128))
697
+ throw new Error('签名DER整数无效');
698
+ return offset + 2 + length;
699
+ }
700
+ ```
701
+
702
+ ```ts
703
+ function ordinaryDer(value: unknown): Buffer {
704
+ if (typeof value !== 'string' || !isStrictBase64(value)) throw new Error('签名Base64无效');
705
+ const bytes = Buffer.from(value, 'base64');
706
+ if (bytes.length < 8 || bytes.length > 72 || bytes[0] !== 48 || bytes[1] !== bytes.length - 2
707
+ || ordinaryDerEnd(bytes, ordinaryDerEnd(bytes, 2)) !== bytes.length)
708
+ throw new Error('签名DER序列无效');
709
+ return bytes;
710
+ }
711
+ ```
712
+
713
+ ```ts
714
+ function ordinarySignature(value: unknown): OrdinarySignature {
715
+ const s = ordinaryObject(value), fingerprint = /^sha256:[0-9a-f]{64}$/i;
716
+ if (Object.keys(s).some(key => !['cert_fingerprint', 'public_key_fingerprint', 'timestamp', 'signature'].includes(key))
717
+ || typeof s.cert_fingerprint !== 'string' || !fingerprint.test(s.cert_fingerprint)
718
+ || !ordinaryInteger(s.timestamp) || s.timestamp < 0
719
+ || s.public_key_fingerprint !== undefined && (typeof s.public_key_fingerprint !== 'string' || !fingerprint.test(s.public_key_fingerprint)))
720
+ throw new Error('签名封套无效');
721
+ ordinaryDer(s.signature);
722
+ return s as OrdinarySignature;
723
+ }
724
+ ```
725
+
726
+ ```ts
727
+ function ordinaryWindow(from: unknown, until: unknown): void {
728
+ const now = Math.floor(Date.now() / 1000);
729
+ if (!ordinaryInteger(from) || from <= 0 || !ordinaryInteger(until)
730
+ || until <= from || until > from + 300 || from > now + 300 || now > until + 300)
731
+ throw new Error('证明时间窗无效');
732
+ }
733
+ ```
734
+
735
+ ```ts
736
+ function requireOrdinaryQuery(p: OrdinaryObject, q: JsonObject, serviceAid: string,
737
+ method: string, cid: string): void {
738
+ if (!ordinarySession.current() || !/^credential\.[^\s/]+$/.test(serviceAid)
739
+ || typeof q.request_id !== 'string' || !q.request_id || typeof q.nonce !== 'string' || !q.nonce
740
+ || p.responder !== serviceAid || p.target !== queryActorAid
741
+ || p.request_id !== q.request_id || p.nonce !== q.nonce) throw new Error('证明与认证查询不符');
742
+ validateRequestBinding(q.request_binding, method, serviceAid, q, cid,
743
+ q.nonce, q.request_id, Math.floor(Date.now() / 1000));
744
+ }
745
+ ```
746
+
747
+ ```ts
748
+ async function verifySignedOrdinary(p: OrdinaryObject, serviceAid: string, signedAt: number): Promise<void> {
749
+ const s = ordinarySignature(p.signature), unsigned = { ...p }; delete unsigned.signature;
750
+ if (s.timestamp !== signedAt) throw new Error('签署时点不符');
751
+ const pem = await trustedCertificates.load(serviceAid, s.cert_fingerprint, s.timestamp);
752
+ if (certFingerprint(pem).toLowerCase() !== s.cert_fingerprint.toLowerCase()
753
+ || s.public_key_fingerprint !== undefined && publicKeyFingerprint(pem).toLowerCase() !== s.public_key_fingerprint.toLowerCase())
754
+ throw new Error('证明证书指纹不符');
755
+ validateAgentMdCertificate(pem, serviceAid, s.timestamp, { requireCurrentValidity: true });
756
+ if (!verifySignatureWithCert(pem, ordinaryDer(s.signature), Buffer.from(canonicalJson(unsigned)))) throw new Error('证明签名无效');
757
+ }
758
+ ```
759
+
760
+ ```ts
761
+ async function verifyStructuredStatus(raw: unknown, document: JsonObject,
762
+ query: JsonObject, serviceAid: string): Promise<OrdinaryObject> {
763
+ const p = ordinaryObject(raw);
764
+ requireOrdinaryQuery(p, query, serviceAid, 'credential.status', String(document.cid));
765
+ if (p.ver !== 'aun-credential-status/1' || p.credentialCid !== document.cid || p.credential_hash !== digest(document)
766
+ || p.issuerAid !== document.iss || p.subjectAid !== document.sub || p.expiresAt !== document.exp || p.status !== 'active') throw new Error('凭证状态无效');
767
+ ordinaryWindow(p.asOf, p.proofExpiresAt); await verifySignedOrdinary(p, serviceAid, Number(p.asOf));
768
+ requireOrdinaryQuery(p, query, serviceAid, 'credential.status', String(document.cid));
769
+ ordinaryWindow(p.asOf, p.proofExpiresAt); return p;
770
+ }
771
+ ```
772
+
773
+ | 普通状态字段 | 匹配对象 |
774
+ | --- | --- |
775
+ | `credentialCid/credential_hash` | 原 CAP 的 CID/完整 canonical hash。 |
776
+ | `issuerAid/subjectAid/expiresAt` | 原 CAP 的 `iss/sub/exp`。 |
777
+ | `responder` | 宿主可信配置选定的 Credential 服务。 |
778
+ | `target` | 原查询的认证 AID,即 `queryActorAid`;查询绑定的 `target` 为服务 AID。 |
779
+ | `asOf/proofExpiresAt/signature.timestamp` | ≤300秒证明声明、当前±300秒窗口、签署时点等于 `asOf`。 |
780
+
781
+ 异步证书检查完成后重验查询与时间窗。`verifyServiceEvent` 用于 §6.4 的推送信封;普通 RPC 响应使用本节适配器。
782
+
783
+ ### 6.6 普通 CRL 分页与原子持久
784
+
785
+ ```mermaid
786
+ flowchart TD
787
+ A[载入当前身份与权威独立进度] --> B[按cursor或since_event_seq生成查询]
788
+ B --> C[核对原查询、issuer过滤、签名与时间]
789
+ C --> D[检查事件严格递增与next_cursor]
790
+ D --> E[事务中读取最新状态,逐CID合并最大序号]
791
+ E --> F[同事务保存事件、cursor与since_event_seq]
792
+ F -->|next_cursor非空| B
793
+ F -->|next_cursor为空字符串| G[本轮完成]
794
+ ```
795
+
796
+ 普通列表字段使用 snake_case。参考 [CredentialRenewal 的分页实现](../../ts/src/org-protocol.ts),`next_cursor=''` 表示末页。
797
+
798
+ ```ts
799
+ interface OrdinaryRevocationEvent extends OrdinaryObject {
800
+ event_seq: number; cid: string; reason_code: string;
801
+ revoked_at: number; revoked_by_aid: string;
802
+ }
803
+ interface VerifiedOrdinaryCrl extends OrdinaryObject {
804
+ events: OrdinaryRevocationEvent[]; next_cursor: string;
805
+ as_of: number; expires_at: number;
806
+ }
807
+ ```
808
+
809
+ ```ts
810
+ function ordinaryCrlQuery(q: JsonObject): { after: number; limit: number } {
811
+ const after = q.cursor === undefined ? q.since_event_seq ?? 0 : Number(q.cursor);
812
+ const limit = q.limit ?? 1000;
813
+ if (q.cursor !== undefined && (typeof q.cursor !== 'string' || !/^(0|[1-9][0-9]*)$/.test(q.cursor))
814
+ || !ordinaryInteger(after) || after < 0 || !ordinaryInteger(limit) || limit < 1 || limit > 1000
815
+ || typeof (q.issuer_aid ?? '') !== 'string') throw new Error('撤销查询参数无效');
816
+ return { after, limit };
817
+ }
818
+ ```
819
+
820
+ ```ts
821
+ function checkedOrdinaryEvent(raw: unknown, last: number, asOf: number): OrdinaryRevocationEvent {
822
+ const e = ordinaryObject(raw);
823
+ if (!ordinaryInteger(e.event_seq) || e.event_seq <= last || typeof e.cid !== 'string' || !e.cid
824
+ || typeof e.reason_code !== 'string' || !e.reason_code || !ordinaryInteger(e.revoked_at)
825
+ || e.revoked_at < 0 || e.revoked_at > asOf || typeof e.revoked_by_aid !== 'string'
826
+ || !/^[^\s/]+\.[^\s/]+$/.test(e.revoked_by_aid)) throw new Error('撤销事件无效');
827
+ return e as OrdinaryRevocationEvent;
828
+ }
829
+ ```
830
+
831
+ ```ts
832
+ function checkedOrdinaryEvents(raw: unknown, after: number, limit: number,
833
+ asOf: number): OrdinaryRevocationEvent[] {
834
+ if (!Array.isArray(raw) || raw.length > limit) throw new Error('撤销页数量无效');
835
+ let last = after;
836
+ return raw.map(value => {
837
+ const event = checkedOrdinaryEvent(value, last, asOf); last = event.event_seq;
838
+ return event;
839
+ });
840
+ }
841
+ ```
842
+
843
+ ```ts
844
+ function checkedOrdinaryCursor(raw: unknown, after: number, last: number): string {
845
+ if (typeof raw !== 'string' || raw !== '' && (!/^[1-9][0-9]*$/.test(raw)
846
+ || !ordinaryInteger(Number(raw)) || Number(raw) <= after || Number(raw) < last))
847
+ throw new Error('撤销分页未前进');
848
+ return raw;
849
+ }
850
+ ```
851
+
852
+ ```ts
853
+ async function verifyStructuredCrl(raw: unknown, query: JsonObject, serviceAid: string): Promise<VerifiedOrdinaryCrl> {
854
+ const p = ordinaryObject(raw), { after, limit } = ordinaryCrlQuery(query);
855
+ requireOrdinaryQuery(p, query, serviceAid, 'credential.revocationList', '');
856
+ if (p.issuer_aid !== (query.issuer_aid ?? '')) throw new Error('撤销页issuer过滤不符');
857
+ ordinaryWindow(p.as_of, p.expires_at); const events = checkedOrdinaryEvents(p.events, after, limit, Number(p.as_of));
858
+ const next_cursor = checkedOrdinaryCursor(p.next_cursor, after, events.at(-1)?.event_seq ?? after);
859
+ await verifySignedOrdinary(p, serviceAid, Number(p.as_of)); requireOrdinaryQuery(p, query, serviceAid, 'credential.revocationList', '');
860
+ ordinaryWindow(p.as_of, p.expires_at);
861
+ return { ...p, events, next_cursor, as_of: Number(p.as_of), expires_at: Number(p.expires_at) };
862
+ }
863
+ ```
864
+
865
+ `structuredStore` 是应用持久端口;`commitPageAtomic` 在事务内核对原查询进度与 `guard.current()`,完成下表合并与落盘。
866
+
867
+ ```ts
868
+ interface StructuredStorePorts {
869
+ load(aid: string, serviceAid: string): Promise<{ since_event_seq: number; cursor: string }>;
870
+ commitPageAtomic(aid: string, serviceAid: string, query: JsonObject,
871
+ page: VerifiedOrdinaryCrl, guard: { current(): boolean }): Promise<void>;
872
+ }
873
+ ```
874
+
875
+ ```ts
876
+ let checkpoint = await structuredStore.load(queryActorAid, credentialServiceAid);
877
+ for (;;) {
878
+ const query = bindOrdinaryRequest('credential.revocationList', credentialServiceAid, '',
879
+ { cursor: checkpoint.cursor || String(checkpoint.since_event_seq), limit: 1000 });
880
+ const page = await verifyStructuredCrl(await client.credential.revocationList(query), query, credentialServiceAid);
881
+ await structuredStore.commitPageAtomic(queryActorAid, credentialServiceAid, query, page, ordinarySession);
882
+ if (page.next_cursor === '') break;
883
+ checkpoint = { cursor: page.next_cursor, since_event_seq: Number(page.next_cursor) };
884
+ }
885
+ ```
886
+
887
+ | 原子持久边界 | 更新规则 |
888
+ | --- | --- |
889
+ | 分区与并发 | 当前 AID+可信 authority;原查询进度须仍匹配,身份/连接代次变化终止旧任务。 |
890
+ | 事件合并 | 逐 CID 保存最大 `event_seq`,旧页保留已存在的新撤销证据。 |
891
+ | 页内水位 | 非末页取 `Number(next_cursor)`;末页取原 after 与本页最后序号的最大值。 |
892
+ | 提交 | 事件、`since_event_seq/cursor/as_of/expires_at` 同事务落盘;提交前复核证明窗口。 |
893
+ | 推送 | 验签后只合并该条证据;分页位置由已连续覆盖的列表页推进。 |
894
+
895
+ ## 7. 高层实体身份和转让编排
896
+
897
+ ```mermaid
898
+ flowchart LR
899
+ A[校验 proposal、owner 和双方完整成员材料] --> B[生成新 P-256 私钥并持久保存 checkpoint]
900
+ B --> C[持久保存 rekey 参数后调用]
901
+ C --> D[导入匹配新公钥/指纹的证书]
902
+ D --> E[新实体签双方 binding/CAP,保存独立 finalize 请求]
903
+ E --> F[finalize:提交新成员包并撤旧 CAP]
904
+ F --> G[新实体主签、owner token 上传名片]
905
+ G --> H[保存 completed 结果,后续可精确重放]
906
+ ```
907
+
908
+ ```ts
909
+ const result = await client.completeOrgTransfer(
910
+ originalTransferParams, { aidStore },
911
+ );
912
+ ```
913
+
914
+ checkpoint 固定输入摘要、transfer ID、owner、proposal、原新私钥、公钥、阶段 request ID/nonce/params。恢复不复制旧组织私钥、不生成替代 key;输入或公钥变化报 conflict,缺原候选私钥报 persistence_unavailable。rekey/finalize 使用不同持久 request ID。
915
+
916
+ 0.5.22:Python 缺 key 保留 checkpoint 并拒绝;Go 导入前核对证书与 CA 结果指纹;TS/JS 保留 completed,精确同任务返回原结果,重连跳过完成态。不补造缺失的历史恢复材料。
917
+
918
+ 组织名片实体主签、owner token 上传,可选当前 owner 附签且全验;现代指纹严格匹配,Org 不开放旧实体上传。完整多签原文、原 CAS、固定 owner/证书代际留待显式重试,重连不自动 PUT;转让/rekey/解散停止旧代待办。通用文件签验见 [Ownership 移植 §4](Ownership2.1-SDK开发与移植指南.md#4-原始文件严格解析与签名),不改变 CAP/binding 算法。
919
+
920
+ 解散先以当前 binding/CAP 准备请求,服务确认 dissolved 后才标记实体证书失效,清创建/转让及名片待办和设备/slot 恢复状态;保留磁盘私钥,不追溯失效已加载 signer,后续 AIDStore.load 拒绝该证书。
921
+
922
+ ## 8. 服务端兼容实现要点
923
+
924
+ | 服务 | 必须保持 |
925
+ | --- | --- |
926
+ | Credential | register 只收完整 CAP 并签回执;status/列表返回签名证明;revoke 从持久去重恢复原请求摘要、CID 和 revoked,冲突或损坏不当成功。 |
927
+ | Org 授权 | 独立验 client signature/AAD;受保护读写验 CAP、binding、request binding 和当前 actor/member。公开 lifecycle 读取按可见性处理。 |
928
+ | 跨服务收尾 | 外部登记/吊销不在 Org SQL 事务内,失败保留续跑;创建补偿遇幂等冲突停止自动补偿,槽位提交丢响应先复读原操作。 |
929
+ | 转让/通知 | 新换钥前核对代际,以规范 SPKI DER Base64 调 CA;成员替换及新旧 owner 完整包通知同次持久保存,失败按组织轮转重试。 |
930
+ | 事件/跨域 | 先验签,再按 responder/event_id/current_aid 去重(最近4096项,不承诺重启 exactly-once);实体跨域重试用上传者域 token,向实体域提交原文件/CAS。 |
931
+
932
+ 以上需要配套服务部署;不能从 SDK 版本推断服务已具备。
933
+
934
+ ## 9. 错误模型与兼容性
935
+
936
+ 保留结构化业务码及 retryable;不要解析错误文本。签名/链/摘要/binding/权限/成员状态/幂等键冲突不盲重试,服务/数据库/证书状态未知可等待但不授权。
937
+
938
+ 跨平台必须固定 Unicode/JSON 数值、PEM/DER/Base64 边界、UTC Unix 时间及各对象含边界的 ±300 秒当前验收窗口;不把 None/undefined/nil 混成相同字段,也不将 Promise/exception/error 写入 wire。
939
+
940
+ ## 10. 测试与验收
941
+
942
+ | 顺序 | 验收范围 |
943
+ | --- | --- |
944
+ | 共同向量 | [org-0.8/protocol.json](../../tests/vectors/org-0.8/protocol.json):canonical bytes、DER、CID/hash、binding、receipt、representation 和错误码。 |
945
+ | 本地 helper | 创建、签名预检、完整 Verify、请求准备;父链缺证明、错误 CID/版本、过期、代表目标及两层摘要冲突必须拒绝。 |
946
+ | Credential RPC | register/status/revoke/列表,持久幂等、失败补偿、撤销推送/分页乱序、重启快照。 |
947
+ | Org/恢复 | join/approve/invite/renew/offboard,rekey/finalize 断线、缺 key、完成重放、完整成员包、解散失效。 |
948
+ | 网络 | 单域 → cross-SDK → 双域;真实数据库、多实例、浏览器和跨平台分别验收,失败即停并从检查点续跑。 |
949
+
950
+ 四端参考:TS/JS 的 src/org-protocol.ts、src/facades.ts;Python 的 aun_core/org_protocol.py、org_facades.py;Go 的 org_protocol.go、org_facades.go。版本专项与真实验收边界见 [四端对齐清单](../../python/CHANGES_FOR_PARITY.md)。