@agentunion/fastaun 0.5.31 → 0.5.32

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 (81) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/_packed_docs/CHANGELOG.md +39 -1
  3. package/_packed_docs/sdk/0.5.32-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +350 -0
  4. package/_packed_docs/sdk/0.5.32-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +319 -0
  5. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +12 -5
  6. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +459 -0
  7. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/274/200/345/217/221/347/247/273/346/244/215/346/214/207/345/215/227.md +554 -0
  8. package/_packed_docs/sdk/INDEX.md +27 -5
  9. package/_packed_docs/sdk//345/255/220AID/344/277/256/346/224/271agent.md/351/200/232/347/237/245Owner/345/234/272/346/231/257/346/214/207/345/215/227.md +234 -0
  10. package/dist/client/control-messages.d.ts +22 -6
  11. package/dist/client/control-messages.js +208 -146
  12. package/dist/client/control-messages.js.map +1 -1
  13. package/dist/client/delivery.d.ts +3 -0
  14. package/dist/client/delivery.js +67 -3
  15. package/dist/client/delivery.js.map +1 -1
  16. package/dist/client/group-state.d.ts +3 -0
  17. package/dist/client/group-state.js +48 -14
  18. package/dist/client/group-state.js.map +1 -1
  19. package/dist/client/lifecycle.js +3 -2
  20. package/dist/client/lifecycle.js.map +1 -1
  21. package/dist/client/rpc-pipeline.d.ts +9 -3
  22. package/dist/client/rpc-pipeline.js +53 -22
  23. package/dist/client/rpc-pipeline.js.map +1 -1
  24. package/dist/client/v2-e2ee.d.ts +13 -1
  25. package/dist/client/v2-e2ee.js +32 -19
  26. package/dist/client/v2-e2ee.js.map +1 -1
  27. package/dist/client.d.ts +6 -1
  28. package/dist/client.js +57 -21
  29. package/dist/client.js.map +1 -1
  30. package/dist/group-fs.js +1 -1
  31. package/dist/group-fs.js.map +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +1 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/ownership-workflow.js +3 -3
  36. package/dist/ownership-workflow.js.map +1 -1
  37. package/dist/pubsub/binding.d.ts +54 -0
  38. package/dist/pubsub/binding.js +343 -0
  39. package/dist/pubsub/binding.js.map +1 -0
  40. package/dist/pubsub/channel.d.ts +45 -0
  41. package/dist/pubsub/channel.js +137 -0
  42. package/dist/pubsub/channel.js.map +1 -0
  43. package/dist/pubsub/client.d.ts +93 -0
  44. package/dist/pubsub/client.js +318 -0
  45. package/dist/pubsub/client.js.map +1 -0
  46. package/dist/pubsub/credentials.d.ts +51 -0
  47. package/dist/pubsub/credentials.js +360 -0
  48. package/dist/pubsub/credentials.js.map +1 -0
  49. package/dist/pubsub/errors.d.ts +5 -0
  50. package/dist/pubsub/errors.js +6 -0
  51. package/dist/pubsub/errors.js.map +1 -0
  52. package/dist/pubsub/index.d.ts +48 -0
  53. package/dist/pubsub/index.js +152 -0
  54. package/dist/pubsub/index.js.map +1 -0
  55. package/dist/pubsub/scheduler.d.ts +58 -0
  56. package/dist/pubsub/scheduler.js +249 -0
  57. package/dist/pubsub/scheduler.js.map +1 -0
  58. package/dist/pubsub/service.d.ts +100 -0
  59. package/dist/pubsub/service.js +650 -0
  60. package/dist/pubsub/service.js.map +1 -0
  61. package/dist/pubsub/types.d.ts +119 -0
  62. package/dist/pubsub/types.js +2 -0
  63. package/dist/pubsub/types.js.map +1 -0
  64. package/dist/storage/lowlevel.js +44 -7
  65. package/dist/storage/lowlevel.js.map +1 -1
  66. package/dist/storage/read-cache.d.ts +7 -3
  67. package/dist/storage/read-cache.js +137 -18
  68. package/dist/storage/read-cache.js.map +1 -1
  69. package/dist/storage/vfs.d.ts +1 -0
  70. package/dist/storage/vfs.js +11 -1
  71. package/dist/storage/vfs.js.map +1 -1
  72. package/dist/tools/cross-sdk-agent.d.ts +1 -1
  73. package/dist/tools/pubsub-actor.d.ts +27 -0
  74. package/dist/tools/pubsub-actor.js +260 -0
  75. package/dist/tools/pubsub-actor.js.map +1 -0
  76. package/dist/transport.d.ts +25 -1
  77. package/dist/transport.js +175 -10
  78. package/dist/transport.js.map +1 -1
  79. package/dist/version.d.ts +1 -1
  80. package/dist/version.js +1 -1
  81. package/package.json +1 -1
@@ -0,0 +1,554 @@
1
+ # AUN事件发布订阅开发移植指南
2
+
3
+ 面向 SDK 维护者和新语言移植者,定义当前工作区 EventService 的分层、消息与状态契约、关键执行顺序及验收场景。应用接线见[应用集成指南](AUN事件发布订阅应用集成指南.md),完整设计见[事件订阅发布方案](../../../docs/AUN文档/AUN事件订阅发布方案.md)。本文整理现有实现,不新增框架接口。
4
+
5
+ ## 1. 分层与移植顺序
6
+
7
+ ### 1.1 整体系统与 SDK 边界
8
+
9
+ ```mermaid
10
+ %%{init: {"theme":"base","themeVariables":{"fontFamily":"Microsoft YaHei, sans-serif","primaryTextColor":"#0f172a","lineColor":"#94a3b8"},"flowchart":{"curve":"linear","nodeSpacing":32,"rankSpacing":38}}}%%
11
+ flowchart TB
12
+ subgraph AGENTS["Agent 进程 · 各自的 AUNClient.EventService"]
13
+ direction LR
14
+ P["Pub SDK<br/>目录缓存 · 发布队列 · 发送结果"]:::pub
15
+ B["Bus SDK · 唯一设备实例<br/>目录 · 授权 · 设备级订阅<br/>64条衔接队列 · 公平fanout"]:::bus
16
+ S["Sub SDK<br/>订阅意图 · 接收校验 · 应用回调"]:::sub
17
+ end
18
+ G["Gateway<br/>认证连接 · 可信AID/device/slot · RPC转发"]:::gateway
19
+ subgraph INFRA["AUN 基础服务"]
20
+ direction LR
21
+ M["消息服务 Message<br/>Bus实例租约 · 定向TTL收件箱<br/>Push / Pull / ACK"]:::infra
22
+ C["凭证服务 Credential<br/>凭证登记 · 签名状态证明 · 撤销"]:::infra
23
+ end
24
+ P <-->|"sdk_control · E2EE P2P"| G
25
+ B <-->|"P2P及实例/凭证RPC"| G
26
+ S <-->|"sdk_control · E2EE P2P"| G
27
+ G <--> M
28
+ G <--> C
29
+ classDef pub fill:#eff6ff,stroke:#93c5fd,color:#1e40af,rx:10,ry:10
30
+ classDef bus fill:#eef2ff,stroke:#818cf8,stroke-width:2px,color:#312e81,rx:12,ry:12
31
+ classDef sub fill:#f0fdf4,stroke:#86efac,color:#166534,rx:10,ry:10
32
+ classDef gateway fill:#fff7ed,stroke:#fdba74,color:#9a3412,rx:10,ry:10
33
+ classDef infra fill:#f8fafc,stroke:#cbd5e1,color:#334155,rx:10,ry:10
34
+ style AGENTS fill:#ffffff,stroke:#e2e8f0,rx:12,ry:12
35
+ style INFRA fill:#f8fafc,stroke:#e2e8f0,rx:12,ry:12
36
+ ```
37
+
38
+ Pub → Bus 与 Bus → Sub 是两次独立 P2P 投递,均经过 Gateway 和 Message。图中的 Gateway 汇总表示各 SDK 的接入连接;跨域时由 AUN 路由到目标域。正文在 SDK 端加解密,事件授权、订阅表和 fanout 在 Bus SDK;Message 负责承接实例与通用消息投递。凭证模式下,Bus 的签发、验证适配器通过 Gateway 调用 Credential。
39
+
40
+ ### 1.2 按 Bus、Pub、Sub 推进实现
41
+
42
+ | 实现视角 | 先实现的内部链路 | 对外验收入口 | 对应章节 |
43
+ | --- | --- | --- | --- |
44
+ | Bus | Binding注册与失租 → 核心授权/设备订阅 → 原子受理 → 公平调度 → 注销 | start、issue_grant、update_config、stats、close | §2、§4、§6、§9 |
45
+ | Pub | 新授权与目录暂存 → 有界发布队列 → 后台发送 → request/event关联与完成结果 | authorize、publish、wait_published | §4—§6 |
46
+ | Sub | 订阅意图与设备租约 → 有界接收/去重 → handler或等待队列 → 续期与恢复 | authorize、subscribe、on_event或next_event | §4、§5、§7 |
47
+
48
+ 按上表逐端实现与验收;公开方法用 Python 风格标识,各语言命名见 §8。本地 Bus 管理直接进入当前服务核心,远端 Pub/Sub 经 Channel、Gateway、Message 与 Bus Binding 通信。
49
+
50
+ | 层次 | 职责 | 不应混入的状态 |
51
+ | --- | --- | --- |
52
+ | EventService 门面 | 按 client 与 bus_aid 管理角色、生命周期、公开方法和 JSON 对象校验 | 不向应用暴露 Binding 或实例注册令牌 |
53
+ | 发布/订阅客户端 | 授权材料、租约、目录缓存、订阅意图、有界发送与接收、完成结果和恢复 | 不承诺跨进程订阅或事件恢复 |
54
+ | Bus 核心 | 目录、白名单/凭证校验、设备级租约与订阅、受理去重、快照、fanout、统计 | 不依赖服务端存储事件订阅表 |
55
+ | Binding / Channel | E2EE 控制消息、可信来源与设备定向、入站准入、回复和实例注册 | 不将业务处理结果解释为底层 ACK |
56
+ | 凭证适配器 | 完整 Verify、签发登记、撤销、真实提交记录及恢复 | 不以只验签或 CID 查询替代完整校验 |
57
+ | AUN Message / Gateway | 唯一承接实例、可信会话身份、正 TTL 收件箱、Push/Pull/ACK、注册代次隔离 | 不解释事件目录、权限或订阅 |
58
+
59
+ 推荐按“既有 AUN E2EE 与控制流 → Channel/Binding → Bus 核心及调度 → 客户端 → 统一门面”落地,并先做同语言场景,再做有向跨 SDK 组合。各语言独立实现语言无关 JSON 协议,内部类、线程机制和状态文件不构成共享代码依赖。
60
+
61
+ 公开边界固定:Python/TS/JS 为 `client.EventService`,Go 为 `client.EventService()`。同 client、同 Bus 只绑定 `pub/sub/bus` 一种角色;bus 身份必须等于当前 client AID。签发、撤销及统计只允许已启动的本地 Bus 宿主调用。网络目标以 `bus_aid` 传入;内部 wire 使用 `service_aid`,应用事件由门面补充 `bus_aid`。
62
+
63
+ ## 2. 场景一:启动唯一 Bus 实例
64
+
65
+ Message 只维护 `Bus AID + service=event_service` 的实例注册;目录、租约、订阅均留在 Bus SDK。SDK 内部复用以下现有 RPC,应用不直接调用。
66
+
67
+ | RPC | 参数与行为 |
68
+ | --- | --- |
69
+ | `message.evtsvc.register` | `{service: 'event_service', registration_id}`;首次注册成功,同令牌重试幂等,其他实例返回 `DEVICE_CONFLICT` |
70
+ | `message.evtsvc.renew` | 校验原可信设备、slot 和令牌,延长承接实例期限 |
71
+ | `message.evtsvc.unregister` | 仅原实例可移除自己的注册,不影响后来接管者 |
72
+
73
+ 可信 AID、device、slot、connection 来自 Gateway 认证会话,不能从请求正文自报。成功回复含原 `registration_id`、实例地址、`expires_at`(毫秒)、`ttl_seconds=100`、`renew_after_seconds=30`。RPC 错误 `-32009` 的 `data.code` 区分 `DEVICE_CONFLICT`、`SERVICE_LEASE_LOST`、`SERVICE_UNAVAILABLE`;发往未启用服务的消息返回 `SERVICE_NOT_STARTED`。
74
+
75
+ 本次 start 生成新令牌,验证回复令牌一致。本地承接截止取“请求开始的单调时钟 + ttl_seconds”,不能用回复到达时间重新计满 100 秒;回复延迟不增加有效期。按下一期限安排续期,续期与重试不得超过原实例剩余期限。
76
+
77
+ 失租或到期后同时停止入站、排队处理及 fanout;不自动争抢后来实例。显式再次 start 清理旧状态,以新令牌注册。旧启动代次的迟到回复、任务或定时器不能修改新实例。关闭须停止接收、清理/等待已接管工作并注销;失败启动也须释放本地资源。
78
+
79
+ 服务请求在 Message 内绑定承接实例注册代次,TTL 不超过该实例租约。注销、到期或同 slot 重新注册后,旧代次消息不能被新实例 Pull/ACK,也不能作为新代次重复发送结果。
80
+
81
+ ### 2.1 Bus 视角:注册成功后承接,失租后停止
82
+
83
+ ```mermaid
84
+ sequenceDiagram
85
+ participant A as Bus宿主应用
86
+ participant B as Bus SDK/Binding
87
+ participant G as Gateway
88
+ participant M as 消息服务
89
+ A->>B: start(bus_aid, catalog, allowlist可选)
90
+ B->>B: 生成本次启动令牌,记录单调起点
91
+ B->>G: message.evtsvc.register
92
+ G->>M: 可信AID/device/slot + 注册令牌
93
+ alt 该Bus已有其他实例
94
+ M-->>G: DEVICE_CONFLICT
95
+ G-->>B: 启动失败
96
+ B-->>A: 返回错误,释放本地资源
97
+ else 注册成功
98
+ M-->>G: 实例租约100秒,30秒后续期
99
+ G-->>B: 原注册令牌及期限
100
+ B->>B: 启用接收、处理队列及fanout
101
+ B-->>A: start成功
102
+ loop 按下次期限等待,正常每30秒
103
+ B->>G: message.evtsvc.renew
104
+ G->>M: 原实例身份与令牌
105
+ M-->>G: 新期限或SERVICE_LEASE_LOST
106
+ G-->>B: 续期结果
107
+ end
108
+ Note over B,M: 失租/到期立即停止承接与分发,重新启动由宿主决定
109
+ A->>B: close(bus_aid)
110
+ B->>B: 停止接收并清理/等待已接管工作
111
+ B->>G: message.evtsvc.unregister
112
+ G->>M: 仅注销原实例
113
+ M-->>G: 注销结果
114
+ G-->>B: 注销结果
115
+ B-->>A: close完成
116
+ end
117
+ ```
118
+
119
+ ```text
120
+ 启动代次 = 当前代次;注册令牌 = 新随机值;起点 = 单调时钟()
121
+ 回复 = await RPC(message.evtsvc.register, 注册令牌)
122
+ 若代次已变或回复令牌不同:结束旧启动流程并清理资源
123
+ 承接截止 = 起点 + 回复.ttl_seconds
124
+ 若单调时钟() >= 承接截止:停止承接并按失租处理
125
+ 启用接收;按下次期限安排续期,close时注销原实例
126
+ ```
127
+
128
+ 来源:[Python Binding](../../python/src/aun_core/pubsub/binding.py)、[TS Binding](../../ts/src/pubsub/binding.ts)、[Go Binding](../../go/pubsub_binding.go)、[Message 接口](../../../extensions/services/message/go/cmd/message-go/control_messages.go)。
129
+
130
+ ## 3. 线上消息与请求关联
131
+
132
+ P/C 与 Bus 之间全部复用 E2EE P2P `message.send`。加密外层 `payload.type=e2ee.p2p_encrypted`,解密后为 `type=app.pubsub.v1`。接收适配器必须校验已成功解密的消息及受保护头,不能仅按 payload.type 匹配后接受。
133
+
134
+ | 受保护头 | 取值或来源 |
135
+ | --- | --- |
136
+ | `message_class` | `sdk_control` |
137
+ | `sdk_control_protocol` / `sdk_control_version` | `aun.pubsub` / `1` |
138
+ | `service_target` | 客户端发往 Bus 时为 `event_service`,由目标域解析承接实例 |
139
+ | `target_device_id` / `target_slot_id` | Bus 回复及事件使用可信请求来源或订阅租约中的实例地址 |
140
+
141
+ 请求发送者取 SDK 验签结果;Bus 入站还要求可信 `sender_device_id/sender_slot_id`。租约必须绑定 `AID + device_id + slot_id`,不能被同 AID 其他实例复用。回复仅回原请求实例,事件仅发给订阅实例。底层收件箱按 AID/device 保存,定向检查继续限制 slot;`volatile_cursor` 只是定位符,不能代替消费权限。
142
+
143
+ ### 3.1 业务请求、回复与事件
144
+
145
+ 公共请求字段为 `type`、`op`、`service_aid`、`request_id`、`nonce`、`issued_at`、`expires_at`。应用消息时间为 UTC Unix 毫秒;控制请求一般 60 秒有效,真实 Binding 入站拒绝超前超过 5 秒的 `issued_at`。内部裸核心容差不能当作线上入口标准。
146
+
147
+ | op | 关键输入 | 结果 |
148
+ | --- | --- | --- |
149
+ | `authorize` | auth_mode、scope、凭证模式的 grant | lease_id、auth_mode、scope、expires_at、renew_after_seconds、catalog 首页 |
150
+ | `subscribe` / `unsubscribe` | lease_id、完整事件名数组 events | 关系变更状态及事件名 |
151
+ | `renew` | lease_id;凭证模式带原授权绑定材料 | 原范围的新到期时间 |
152
+ | `catalog` | 可选 event_name、limit、page_token;普通调用方带租约 | items、revision/catalog_revision、next_page_token |
153
+ | `publish` | lease_id、event_id、event_name、schema_version、data、expires_at、reply_mode | none 无业务回复;accepted 返回快速受理结果 |
154
+ | 本地 `issue_grant` | subject_aid、scope、grant_expires_at | grant、cid、receipt 或 registration_confirmation |
155
+ | 本地 `revoke_grant` | cid | 撤销结果 |
156
+ | 本地 `stats` | 可选 event_name | since、as_of、scope、counts |
157
+
158
+ 有回复的操作使用 `<op>.result`,携带 `service_aid/request_id/expires_at` 及 `result` 或 `error:{code,message}`。`publish.result` 另关联 event_id,截止不晚于原请求。客户端先核验回复来自所绑定 Bus,再验证 op、request_id 和相应 event_id;不得由不匹配回复完成 pending。
159
+
160
+ 上表三个本地方法复用 Bus 内部处理能力,不向远端开放。普通消息入口收到 issue_grant、revoke_grant、stats 均拒绝,即使发送 AID 与 Bus 相同;本地调用资格来自 SDK 内部调用路径,不能由请求正文声明。
161
+
162
+ 事件字段为 `type=app.pubsub.v1`、`op=event`、`event_id`、`event_name`、`publisher_aid`、`schema_version`、`expires_at`、`data`。`publisher_aid` 是 Bus 根据可信发布者写入,客户端实际消息发送方仍是 Bus。
163
+
164
+ ### 3.2 凭证请求绑定与幂等
165
+
166
+ 凭证操作复用现有 `request_binding`:`request_id/nonce/target/method/params_hash/iat/exp/credential_cid`,method 为 `pubsub.<op>`,target 为 Bus AID。摘要采用现有 canonical JSON + SHA256,排除 request_binding、client_signature 和下划线内部字段;绑定 iat/exp 使用秒。应复用或独立实现同一凭证协议,不能使用任意 JSON 序列化替代规范摘要。
167
+
168
+ | 层级 | 幂等键与约束 |
169
+ | --- | --- |
170
+ | 控制请求 | 可信发送 AID、device、slot、request_id 绑定完整请求指纹;并发重复复用首次结果,改参报 `REQUEST_CONFLICT` |
171
+ | 发布客户端 | 同 event_id 保持 event_name/schema_version/data,复用原 expires_at;冲突报 `IDEMPOTENCY_CONFLICT` |
172
+ | Bus 发布受理 | `(publisher_aid,event_id)` 缓存首次事件指纹和受理结果至原期限;冲突报 `EVENT_CONFLICT`;去重记录不保留正文 |
173
+ | 目标投递 | 稳定 message_id 包含服务、发布者、事件、订阅 AID 和设备/slot;超时重试复用原 ID 和截止 |
174
+ | 订阅接收 | `(Bus AID,publisher_aid,event_id)` 去重至原期限,内存有界 |
175
+
176
+ 支持发布 `none → accepted` 升级,不重复扇出。续期的重复请求返回首次到期时间,不因重试再延长。以上去重都是有限运行时状态,不能承诺跨重启的 exactly-once。
177
+
178
+ 来源:[客户端构造与绑定](../../python/src/aun_core/pubsub/client.py)、[通用凭证协议](../../python/src/aun_core/org_protocol.py)、[Bus 受理](../../python/src/aun_core/pubsub/service.py)。
179
+
180
+ ## 4. 场景二:授权、订阅与撤销
181
+
182
+ ### 4.1 白名单与本地宿主
183
+
184
+ 目录按完整事件名登记;allowlist 每项区分 `publish_aids/subscribe_aids`。事件条目 `"*"` 与精确条目按并集合并,AID 数组 `"*"` 允许任意已通过 AUN 身份认证的主体;目录外事件仍被拒绝。它不是事件订阅模式匹配语法。
185
+
186
+ Bus 宿主本地维护全部配置并调用签发、撤销和统计。远端发布者、订阅者仅持各自设备租约,目录查询按该租约过滤;本地 Bus 可查询全部目录。
187
+
188
+ 配置缩权时立即裁剪 allowlist 租约 scope、删除无剩余权限的租约和相应订阅。新增权限只在重新 authorize 时授予;renew 不能扩大原 scope。凭证租约不会因白名单缩权而改变授权来源。
189
+
190
+ 多事件沿用相同协议:Bus 目录登记多个完整事件名,allowlist 对每个事件分别配置。客户端一次 authorize 的 scope 可包含多个事件;同一请求任一项未获准则整体失败。subscribe/unsubscribe 的 events 数组可选择获准范围内的一部分,每次 publish 仍只包含一个 event_name 和一份 JSON 正文。回调依据 event_name 分发;不同事件共享当前 Bus 的容量与公平调度预算。应用示例见[应用指南 §3](AUN事件发布订阅应用集成指南.md#3-场景一白名单接入)。
191
+
192
+ ### 4.2 完整凭证校验
193
+
194
+ 凭证模式的启动顺序是“Bus AUN 身份连接就绪 → 本地 start 配置目录 → 宿主审批并本地签发凭证 → 发布/订阅方 authorize → 订阅方 subscribe”。Bus 使用自身 AID 接入 AUN,start 要求 bus_aid 等于当前身份,随后自动调用 message.evtsvc.register;凭证模式省略 allowlist 即可,凭证适配器由 SDK 自动创建。
195
+
196
+ 凭证模式在 SDK 内组装默认适配器、核心与 Binding;两事件的应用启动参数见[应用指南 §4](AUN事件发布订阅应用集成指南.md#4-场景二应用审批与凭证授权):
197
+
198
+ ```text
199
+ 凭证适配器 = 自定义适配器或默认适配器(client, 自身AID元数据存储)
200
+ 核心 = 创建Bus核心(自身AID, 已加载目录, allowlist可选, 凭证适配器)
201
+ Binding = 创建接收与定向发送适配(client, 核心)
202
+ await Binding.start():先注册唯一实例,再启用承接
203
+ ```
204
+
205
+
206
+ Bus 的 AUN 连接先认证自身身份。本地宿主审批后直接调用 issue_grant,无需再申请发布订阅租约;远端 authorize 再根据可信发布/订阅 AID、设备和完整 grant 鉴权。
207
+
208
+ **签发者是 Bus AID,验证者是 Bus SDK。** 宿主的 issue_grant 使用 Bus 私钥签名;authorize 在 Bus SDK 内验证完整凭证及权限,并按需查询可信状态证明。AUN 凭证服务负责登记、签名状态证明与撤销处理。
209
+
210
+ 订阅凭证的 scope 明确列出两项,发布凭证逐项使用 action=publish,sub 分别绑定实际订阅者或发布者;SDK 不能把一个事件的权限扩展到其他事件:
211
+
212
+ ```json
213
+ [
214
+ { "event_name": "task.completed", "action": "subscribe" },
215
+ { "event_name": "task.failed", "action": "subscribe" }
216
+ ]
217
+ ```
218
+
219
+ ```json
220
+ [
221
+ { "event_name": "task.completed", "action": "publish" },
222
+ { "event_name": "task.failed", "action": "publish" }
223
+ ]
224
+ ```
225
+
226
+ Bus 使用自身身份签发 `aun-cap/1`,CAP 的根签发者为 Bus,sub 为调用方,aud 包含 Bus;能力项 `ns=event`、`action=publish|subscribe`、`resource=完整事件名`,并核验约束。完整 Verify 包括链、证书、签名状态证明、主体受众、期限、能力和实际请求绑定,不能只验凭证签名。
227
+
228
+ 授权租约默认 `min(now + 12000秒, 凭证链最早截止)`,白名单模式无凭证截止。租约回复每 3600 秒续期。内部 `authorization_until` 还受状态证明 `proofExpiresAt` 限制,必须与租约和凭证分别计时;证明过期且无法刷新时暂停操作。
229
+
230
+ 发布、订阅、续期和实际 fanout 都复核授权。内部复核只保存约束涉及的参数投影,不应缓存全部历史事件正文;复核缓存默认最多 4096 条/8 MiB,并计入正文总预算。
231
+
232
+ 撤销先持久记录暂停 CID 并使关联租约/订阅失效,再调用凭证服务。结果未知时保持暂停,继续以原材料核实;外部有效撤销同样使关系失效。晚到的验证结果不能恢复已撤销或关闭代次的授权。
233
+
234
+ **Bus 视角:宿主签发,凭证服务登记**
235
+
236
+ ```mermaid
237
+ sequenceDiagram
238
+ participant A as Bus宿主应用
239
+ participant B as Bus SDK/凭证适配器
240
+ participant G as Gateway
241
+ participant C as 凭证服务
242
+ A->>A: 审批目标AID、事件和操作范围
243
+ A->>B: 本地issue_grant(bus_aid, subject_aid, scope)
244
+ B->>B: 用Bus私钥签发CAP,保存原材料
245
+ B->>G: credential.register + 请求绑定
246
+ G->>C: 已签名凭证及登记请求
247
+ C-->>G: 登记回执
248
+ G-->>B: 回执
249
+ B->>B: 校验回执并保存结果
250
+ B-->>A: grant、cid、receipt
251
+ A->>A: 通过应用业务通道交付给指定Pub/Sub
252
+ ```
253
+
254
+ **Pub/Sub 视角:鉴权和目录一次接入**
255
+
256
+ Pub 与 Sub 使用同一鉴权链路,分别申请 publish 或 subscribe。下图的客户端表示其中一端;白名单模式直接匹配名单,凭证模式由 Bus 完整 Verify,状态证明按需查询。
257
+
258
+ ```mermaid
259
+ sequenceDiagram
260
+ participant P as Pub或Sub SDK
261
+ participant G as Gateway
262
+ participant M as 消息服务
263
+ participant B as Bus SDK
264
+ participant C as 凭证服务
265
+ P->>G: message.send:authorize(scope, grant可选)
266
+ G->>M: sdk_control,service_target=event_service
267
+ M-->>G: 路由到已注册Bus实例
268
+ G-->>B: 密文及可信发送设备,SDK解密
269
+ alt credential模式
270
+ opt 需要查询或刷新状态证明
271
+ B->>G: credential.status
272
+ G->>C: 查询凭证状态
273
+ C-->>G: 签名状态证明
274
+ G-->>B: 状态证明
275
+ end
276
+ B->>B: 完整Verify及实际请求权限校验
277
+ else allowlist模式
278
+ B->>B: 逐项核对事件与AID白名单
279
+ end
280
+ B->>B: 创建设备租约及获准目录首页
281
+ B->>G: 定向authorize.result
282
+ G->>M: 目标Pub/Sub设备及slot
283
+ M-->>G: 定向投递
284
+ G-->>P: 租约及catalog首页
285
+ opt 存在next_page_token
286
+ P->>G: 自动catalog续页:新lease_id及签名token
287
+ G->>M: 发往Bus的控制消息
288
+ M-->>G: 定向到Bus
289
+ G-->>B: 续页请求
290
+ B->>G: 定向catalog.result
291
+ G->>M: 目标设备回复
292
+ M-->>G: 定向投递
293
+ G-->>P: 目录后续页,重复直至取齐
294
+ end
295
+ P->>P: 所有页成功且代次有效后提交缓存与新租约
296
+ ```
297
+
298
+ ```text
299
+ 代次 = 当前代次;回复 = await authorize(新申请范围与授权材料)
300
+ 暂存目录 = 当前目录副本;页 = 回复.catalog
301
+ 循环:暂存(页.items);无next_page_token则退出
302
+ 页 = await catalog(回复.lease_id, 页.next_page_token, 本次新授权材料)
303
+ 所有页成功且代次仍有效:一次提交暂存目录、授权材料和新租约
304
+ 任一页失败或已关闭:保留旧状态,不提交部分结果
305
+ ```
306
+
307
+ Pub 随后可 publish;Sub 还须 subscribe 才有事件订阅关系。续页使用新鉴权材料,缓存提交不能早于最后一页成功。
308
+
309
+ ### 4.3 登记回复丢失与恢复
310
+
311
+ 签发前保存原 grant 和登记请求;真实提交前保存提交时间及请求摘要,正常返回需验登记回执。有效绑定窗口内重试原材料;超窗后只允许依据真实发送证据与最新签名状态证明确认原登记,不得仅凭“凭证已存在”认定该次操作成功。
312
+
313
+ 确认成功后返回同一 grant/cid,`receipt=null` 并附 `registration_confirmation`。撤销同样保留原请求和结果。服务配置与凭证适配器是两个持久化域;SDK 自动保存默认凭证材料,应用按需配置服务状态存储。
314
+
315
+ 来源:[Python 凭证适配器](../../python/src/aun_core/pubsub/credentials.py)、[TS 凭证适配器](../../ts/src/pubsub/credentials.ts)、[Go 凭证适配器](../../go/pubsub_credentials.go)、[凭证移植指南](凭证与组织服务SDK开发与移植指南.md)。
316
+
317
+ ## 5. 目录、版本与内容校验
318
+
319
+ 目录条目包含 `description/schema_version/content_schema/default_ttl_seconds/max_ttl_seconds`,回复另有 event_name、allowed_actions。白名单与凭证 authorize 均返回嵌套 catalog 首页,客户端自动取完后续页并缓存,然后提交新授权状态;成功返回后即可发布或订阅接收。独立 catalog 保留,用于查询与刷新。
320
+
321
+ - authorize 附带目录只按新租约 scope 生成。续页 token 额外绑定该租约,不能用于另一租约。
322
+ - authorize 附带 catalog 的 UTF-8 JSON 最多 2 KiB,完整 authorize.result 仍最多 64 KiB。首页容不下一条时返回空 items 与 offset=0 的续页 token,SDK 自动按普通目录页补齐。这样默认 4096 条幂等缓存新增目录的 UTF-8 JSON 总量最多 8 MiB,不把大 schema 逐请求复制留存。
323
+ - 客户端暂存各页,在全部成功且运行代次仍有效后更新目录、授权和租约;续页失败或关闭不提交部分状态。返回值保留有界首页,不能再把所有页合并进租约或幂等回包。
324
+
325
+ - TTL 取整数 1—3600 秒,default 不大于 max;缺省 default 为 30 秒。发送校验当前 schema_version,内容必须为 JSON 对象。
326
+ - `content_schema` 当前支持基础 type、对象 required/properties、数组 items,以及简写字段类型约束;不承诺完整 JSON Schema 关键字语义。
327
+ - 目录分页默认 50、最大 200;包括封装的明文回复最多 64 KiB,单条目录配置最多 48 KiB。不能只按条数截断后生成超限回复。
328
+ - token 绑定调用身份、可见权限、过滤条件、目录 revision、偏移和期限;授权目录续页还绑定 lease_id。变更后分别返回 `CATALOG_CHANGED` 或 `INVALID_PAGE_TOKEN`,重新鉴权或从查询首页读取。
329
+ - 客户端按已获取旧版本及其 TTL 保留短期版本窗口,使切版前在途事件有机会交付;不能因此接受任意未知版本。
330
+
331
+ 来源:[Python `_op_catalog/_validate_content`](../../python/src/aun_core/pubsub/service.py)、[TS `catalogOp/validateContent`](../../ts/src/pubsub/service.ts)。
332
+
333
+ ## 6. 场景三:异步受理、TTL 与公平分发
334
+
335
+ ### 6.1 受理与快照顺序
336
+
337
+ 发布客户端先做目录、版本、TTL、JSON 与本地容量检查,入有界队列立即返回 event_id。后台发送结果进入完成等待表或回调,不在 publish 中串行等待整个网络及 fanout。
338
+
339
+ 同一事件的订阅视图在受理时固定。随后新订阅不能加入旧事件;取消、撤销或替换订阅代次后,发送前复核可以阻止尚未发送的旧快照任务。快照采用共享引用计费,不能为每次 publish 无界复制整张订阅表。
340
+
341
+ **Pub 与 Bus 视角:异步受理和分发**
342
+
343
+ ```mermaid
344
+ sequenceDiagram
345
+ participant A as 发布应用
346
+ participant P as Pub SDK
347
+ participant G as Gateway
348
+ participant M as 消息服务
349
+ participant B as Bus SDK
350
+ A->>P: publish(bus_aid, event_name, data)
351
+ P->>P: 校验目录并放入有界发送队列
352
+ P-->>A: event_id
353
+ P->>G: 后台message.send:publish,原始expires_at
354
+ G->>M: 正TTL的sdk_control密文
355
+ M-->>G: 投递或Pull响应
356
+ G-->>B: 发布消息及可信设备来源
357
+ B->>B: 预留64条衔接容量与正文预算
358
+ alt 无接管容量
359
+ B->>B: 暂停对应事件Pull,等待容量信号
360
+ Note over B,M: 未接管消息不提交去重、不ACK
361
+ else 已接管
362
+ B->>B: 校验权限/期限/去重,原子提交快照及fanout任务
363
+ opt reply_mode=accepted
364
+ B->>G: publish.result,定向原Pub设备
365
+ G->>M: 受理结果
366
+ M-->>G: 定向投递
367
+ G-->>P: 校验Bus、request_id、event_id后完成等待
368
+ end
369
+ B->>B: 分批生成目标,每订阅AID独立worker
370
+ B->>G: event:目标订阅设备/slot、剩余TTL
371
+ G->>M: 定向内存投递
372
+ B->>B: 释放预算,唤醒受背压入口
373
+ end
374
+ Note over A,P: none以AUN接受发送为完成;accepted以Bus受理为完成
375
+ ```
376
+
377
+ ```text
378
+ Pub.publish:校验后有界入队,立即返回event_id;worker后台发送
379
+ Bus入口:尝试预留条数与字节,失败则暂停对应Pull且不ACK/去重
380
+ 成功接管后提交消息去重与ACK,按发布AID轮转处理
381
+ 通过授权/版本/期限/事件去重后,原子受理快照及fanout任务
382
+ 若reply_mode=accepted:定向回复;分批生成目标任务
383
+ 每订阅AID的worker出队复核,沿原deadline提交AUN
384
+ 全局目标容量满则等待;单AID额度满跳过并计skipped_overloaded;释放预算唤醒入口
385
+ ```
386
+
387
+ 全局目标容量满时 fanout 等待容量,背压继续传到64条衔接队列及事件Pull;单个订阅AID额度满时跳过该目标并计入skipped_overloaded,其余目标继续推进。图中的“受理”先于实际分发完成,订阅设备收到后如何交付应用见 §7。
388
+
389
+ ### 6.2 期限与结果
390
+
391
+ 第一跳使用原始事件 expires_at,第二跳目标截止不晚于事件期限和订阅租约到期。排队、限流、加密与网络等待都消耗同一预算;每次发送取 `floor((deadline-now)/1000)`,不足 1 秒直接跳过。禁止把它改成 ttl=0,因为 0 进入持久消息路径。
392
+
393
+ `publish` 返回、本地 `message.send` 成功、`publish.result(status=accepted)` 是三个不同阶段;受理确认不等目标匹配及发送完成。目标发送超时表示结果未知,重试沿用 ID 与期限;确认后只入账一次,不能把超时直接计为明确失败。
394
+
395
+ 每个订阅 AID 一个逻辑 worker,同 AID 多设备共享公平份额,每个 AID 最多一条事件发送在途。使用 DRR 按工作量推进,成本为明文字节加默认 128 字节固定开销;发布 AID 与目标 AID 都计相应预算,避免只按事件条数轮转导致大消息独占。
396
+
397
+ ### 6.3 容量与调度预算
398
+
399
+ | 范围 | 默认预算及必要行为 |
400
+ | --- | --- |
401
+ | P 发布队列 | 256 条/1 MiB,默认 4 个发送 worker;满时 `OVERLOADED`,未入队 |
402
+ | C 接收队列 | 256 条/1 MiB;64 KiB 单封装限制,校验与去重后交付;满载或超接收速率允许丢弃 |
403
+ | Bus 发布接管至 fanout | 合计最多 64 条;满载暂停对应事件 Pull,容量释放后唤醒,不先 ACK 再丢弃 |
404
+ | Bus 正文总预算 | 入站、受理、目标队列、在途及凭证复核保留材料合计硬封 64 MiB |
405
+ | 目标发送队列 | 合计 4096 条/8 MiB;每 AID 512 条/2 MiB |
406
+ | 租约/订阅 | 各 16384 条/8 MiB;过期/缩权及时回收 |
407
+ | 保留订阅快照 | 默认最多 256 份/8 MiB,共享引用只计一次 |
408
+ | 通道速率 | 40 次/秒、256 KiB/秒;突发 4 次/64 KiB;C 接收使用同预算 |
409
+ | RPC 并发 | 全部共用总池 16;background 最多 8,其中 event 最多 4、pull 最多 4,子池互不借用 |
410
+
411
+ 前台只用总池,因此后台满载时仍留至少 8 个名额。事件上下文派生的凭证/加密准备 RPC 也归 event,不能经内部调用绕过预算;鉴权、目录、续期和取消与 publish 分离入站队列。前台排队或写缓冲达到 64 KiB 时暂停新事件发送。
412
+
413
+ 计算密集任务与普通计算隔离,不能阻塞消息消费循环;Python 当前事件/普通加解密各 4 线程、各最多 64 项等待,取消后实际计算结束才释放名额。其他语言保持相同隔离及有界语义,不要求采用相同线程 API。
414
+
415
+ 来源:[Python 调度器](../../python/src/aun_core/pubsub/scheduler.py)、[速率控制](../../python/src/aun_core/pubsub/rate.py)、[RPC 传输](../../python/src/aun_core/transport.py)、[TS 调度器](../../ts/src/pubsub/scheduler.ts)、[Go 调度器](../../go/pubsub_scheduler.go)。
416
+
417
+ ## 7. 场景四:接收、重连与关闭
418
+
419
+ ### 7.1 Sub 视角:登记订阅并交给应用
420
+
421
+ ```mermaid
422
+ sequenceDiagram
423
+ participant A as 订阅应用
424
+ participant S as Sub SDK
425
+ participant G as Gateway
426
+ participant M as 消息服务
427
+ participant B as Bus SDK
428
+ Note over A,B: 已authorize成功,获准目录已缓存
429
+ A->>S: on_event(bus_aid, handler)
430
+ A->>S: subscribe(bus_aid, events)
431
+ S->>G: subscribe + 当前lease_id
432
+ G->>M: 发往Bus的sdk_control
433
+ M-->>G: 定向到Bus
434
+ G-->>B: 可信AID/device/slot及订阅请求
435
+ B->>B: 建立设备级订阅关系
436
+ B->>G: 定向subscribe.result
437
+ G->>M: 回复原设备/slot
438
+ M-->>G: 定向投递
439
+ G-->>S: 订阅成功
440
+ S-->>A: subscribe完成
441
+ B->>G: fanout事件至已订阅设备/slot
442
+ G->>M: TTL内存收件箱
443
+ M-->>G: Push或Pull响应,仅目标设备可消费
444
+ G-->>S: sdk_control事件密文及可信来源
445
+ S->>S: 解密,校验Bus/租约/事件/版本/TTL并去重
446
+ S-->>A: on_event回调,或next_event返回
447
+ Note over S,M: 底层ACK表示SDK已接管,不表示应用业务提交
448
+ ```
449
+
450
+ ```text
451
+ 解密并核验受保护头、可信Bus来源和目标设备/slot
452
+ 校验当前租约/订阅、事件必填字段、版本与TTL
453
+ 若已见(Bus, publisher_aid, event_id):忽略重复
454
+ 若接收条数/字节/速率无容量:丢弃;否则提交去重与接收预算
455
+ 有handler则进入串行回调队列,否则进入next_event等待队列
456
+ 出队再次核验期限与代次,处理完成释放接收预算
457
+ ```
458
+
459
+ ### 7.2 Pub/Sub 视角:授权续期与断线恢复
460
+
461
+ ```mermaid
462
+ sequenceDiagram
463
+ participant P as Pub或Sub SDK
464
+ participant G as Gateway
465
+ participant M as 消息服务
466
+ participant B as Bus SDK
467
+ Note over P,B: 授权默认200分钟有效,正常每1小时续期
468
+ P->>P: 等待授权续期时点,或连接重新ready
469
+ P->>G: renew:当前lease_id及原授权材料
470
+ G->>M: 发往Bus的控制消息
471
+ M-->>G: 定向到承接实例
472
+ G-->>B: renew及可信设备来源
473
+ B->>B: 核验原范围、当前凭证状态及设备身份
474
+ B->>G: 定向renew.result
475
+ G->>M: 回复原设备
476
+ M-->>G: 定向投递
477
+ G-->>P: 新期限或REAUTH_REQUIRED
478
+ alt 原关系仍有效
479
+ P->>P: 更新期限,继续收发
480
+ else 需要重新鉴权
481
+ P->>P: 用保存材料重新authorize并自动加载目录
482
+ opt 当前角色为Sub
483
+ P->>P: 经同一消息路径补subscribe,仅恢复未取消事件
484
+ end
485
+ end
486
+ ```
487
+
488
+ ```text
489
+ 无已保存授权材料:结束;否则按当前生命周期串行恢复
490
+ 租约未过期:先renew,仅REAUTH_REQUIRED转重新鉴权
491
+ 租约已过期或renew返回REAUTH_REQUIRED:重新authorize并自动加载完整目录
492
+ Sub需要补订阅时:锁定意图,subscribe仍未取消的事件
493
+ 成功才清除待恢复标记;暂时失败保留意图并有界重试
494
+ 已关闭或代次已变化:结束旧恢复流程
495
+ ```
496
+
497
+ Bus 实例续期是 §2 的30秒/100秒链路,Pub/Sub授权续期是这里的1小时/200分钟链路。恢复使用相同消息路径与设备身份,凭证证明另按自身期限刷新。
498
+
499
+ 接收端依次核对可信消息来自所绑定 Bus、协议与目标正确、当前订阅和租约有效、事件未过期、目录版本可接受、publisher/event_id 非空,再按 `(Bus,publisher,event_id)` 去重并交付。业务层不能直接订阅原始 `message.received` 把内部控制消息当普通消息消费。
500
+
501
+ `on_event/onEvent/OnEvent` 与 `next_event/nextEvent/NextEvent` 是两种消费模式;有 handler 时新事件进入 handler 路径。Python 异步回调 await、同步回调在线程中串行执行;TS/JS await 返回 Promise;Go 回调为函数。回调失败不自动重发,慢回调占用接收额度。
502
+
503
+ 连接 ready 或授权续期到期触发进程内恢复:优先 renew,有效关系不重复创建;`REAUTH_REQUIRED` 时按保存的原授权重新 authorize,再恢复未取消的订阅。订阅恢复失败应保留待恢复状态,不因一次失败丢失意图;取消与恢复要串行,不能把已取消事件重新加入。门面没有公开 recover API。
504
+
505
+ close 清租约、授权、订阅、pending、发布结果、队列和运行时去重;阻止旧代次回调继续交给后续 handler。支持同对象显式再次 start,原角色约束保留;重新 authorize/subscribe 后继续接收新事件,目录随鉴权加载。drain 等待已接管工作及仍运行的回调,不能把 AUN 接受发送当作远端业务完成。
506
+
507
+ 底层 Push/Pull/ACK 沿用设备、slot 和类别隔离;只有成功接管才提交去重与 ACK。事件交给应用后没有独立业务 ACK、历史游标或 replay API;应用回调返回不提供持久交付保证。
508
+
509
+ ## 8. 状态持久化与四端差异
510
+
511
+ | 状态 | 保存边界 |
512
+ | --- | --- |
513
+ | 服务配置 | 目录、白名单、签发记录、暂停 CID;重启恢复的配置优先于构造初始配置 |
514
+ | 凭证适配器 | 原凭证、登记/撤销请求、真实提交证据及结果;支持原操作恢复 |
515
+ | 运行时 | 实例令牌、租约、订阅、快照、队列、事件正文、发布结果、事件去重和统计不作为持久恢复账本 |
516
+
517
+ | SDK | 入口和配置 | 源码入口 |
518
+ | --- | --- | --- |
519
+ | Python | 凭证默认使用自身 AID 的 aun.db;显式 state_path 目录保存服务与凭证两份 JSON 文件 | [门面](../../python/src/aun_core/pubsub/__init__.py)、[Binding](../../python/src/aun_core/pubsub/binding.py) |
520
+ | TS | 凭证默认使用自身 AID 的 aun.db;start.state 保存服务配置,credentialOptions.state 覆盖凭证存储 | [门面与 options](../../ts/src/pubsub/index.ts)、[凭证](../../ts/src/pubsub/credentials.ts) |
521
+ | JS | 凭证默认使用 IndexedDB aun-keystore/metadata,按自身 AID 隔离;显式适配器与 TS 相同 | [门面](../../js/src/pubsub/index.ts)、[凭证](../../js/src/pubsub/credentials.ts) |
522
+ | Go | 凭证默认使用自身 AID 的 aun.db;显式 StatePath 目录保存服务与凭证两份 JSON 文件 | [门面](../../go/pubsub_service.go)、[类型](../../go/pubsub_types.go) |
523
+
524
+ 原生端数据库路径统一为 `{aun_path}/AIDs/{self-aid}/aun.db`,复用 TokenStore 和 metadata_kv,凭证键为 `pubsub_credentials_v1`;浏览器复用现有 IndexedDB 元数据层和同一键。显式文件或 credentialOptions.state 优先,不同时写默认数据库。凭证重启恢复、AID 隔离和自定义存储优先级均需验证。
525
+
526
+ Python 使用 snake_case,TS/JS 使用 camelCase(发布选项也接受 snake_case),Go 使用导出方法和 `PubSubPublishOptions`。Python/TS/JS next_event 超时为秒,Go 通过 context 控制。内部持久字段及分页 token 不承诺跨语言交换;移植应保证线上协议互通,不照搬另一语言运行时对象布局。
527
+
528
+ 文件适配采用平台路径 API 和 UTF-8,浏览器用其可用持久层;保存完成后才确认需要持久保证的配置/签发变更。独立 SDK 不依赖 Kite 模块共享代码,也不增加语言专有 IPC。
529
+
530
+ ## 9. 统计、错误与验收场景
531
+
532
+ stats 仅由本地 Bus 宿主调用,可按 event_name 过滤,含 service_aid、scope、since、as_of、counts;重启重置累计计数。`active_subscribers` 是去重 AID 数,`active_subscriptions` 是 AID/device/slot/event 关系数。`matched` 来自受理快照,`submitted` 是 AUN 接受的目标设备投递,`unconfirmed` 是结果未知;均不等于应用处理成功。具体字段见[应用指南 §7](AUN事件发布订阅应用集成指南.md#7-持久化统计与故障定位)。
533
+
534
+ | 错误族 | 当前边界 |
535
+ | --- | --- |
536
+ | `BUS_NOT_BOUND/BUS_ROLE_CONFLICT/ROLE_MISMATCH` | 本地绑定与角色不满足 |
537
+ | `SERVICE_IDENTITY_MISMATCH/DEVICE_CONFLICT/SERVICE_LEASE_LOST/SERVICE_NOT_STARTED` | 宿主身份或实例生命周期不满足 |
538
+ | `FORBIDDEN/REAUTH_REQUIRED/CREDENTIAL_UNAVAILABLE` | 权限、租约或凭证证明不可用;按来源重新核验,不自动扩大授权 |
539
+ | `CATALOG_REQUIRED/UNKNOWN_EVENT/SCHEMA_MISMATCH/UNSUPPORTED_SCHEMA_VERSION/INVALID_CONTENT` | 目录、版本或内容不满足 |
540
+ | `REQUEST_CONFLICT/IDEMPOTENCY_CONFLICT/EVENT_CONFLICT` | 相应层级同幂等键改参 |
541
+ | `OVERLOADED/MESSAGE_TOO_LARGE/EXPIRED/TIMEOUT/CLOSED` | 容量、期限或生命周期不满足;TIMEOUT 保留结果未知语义 |
542
+
543
+ 移植按以下场景验收;测试发现首个失败先定位和修复,再从失败处续跑,不把旧版本归档结果当作本次测试通过。
544
+
545
+ | 场景 | 必须断言 | 现有依据 |
546
+ | --- | --- | --- |
547
+ | 白名单/凭证完整应用流程 | 多事件授权自动加载目录、分别发布/订阅/回调、角色隔离、JSON 对象、回执分层、两种接收方式 | [Python application](../../python/tests/unit/test_pubsub_application.py)、[TS client](../../ts/tests/unit/pubsub-client.test.ts)、[Go client](../../go/pubsub_client_test.go) |
548
+ | 凭证状态与恢复 | 完整绑定、撤销、证明到期、登记回复丢失、超窗确认及持久材料 | [Python credentials](../../python/tests/unit/test_pubsub_credentials.py)、[Go credentials](../../go/pubsub_credentials_test.go) |
549
+ | 设备与实例 | 同 AID 多设备独立订阅、同设备不同 slot、冲突、失租、旧代次不可消费 | [恢复 E2E](../../python/tests/e2e_test_event_pubsub_recovery.py)、[Message 测试](../../../extensions/services/message/go/cmd/message-go/main_test.go) |
550
+ | 容量与公平 | 64 条/64 MiB、未接管不 ACK、容量唤醒、每 AID 单飞、16/8/4/4 和前台推进 | [容量契约](../../python/tests/unit/test_pubsub_capacity_contracts.py)、[Pull Gate](../../python/tests/unit/test_pubsub_pull_gate.py)、[Go scheduler](../../go/pubsub_scheduler_test.go) |
551
+ | 目录与关系变更 | authorize 全封装预算、自动续页与关闭代次、租约范围目录、token 隔离、版本窗口、缩权和取消 | [Python scenarios](../../python/tests/unit/test_pubsub_scenarios.py)、[JS service](../../js/tests/unit/pubsub-service.test.ts) |
552
+ | 跨 SDK | 四种 Bus 分别验证两种授权、同端、有向跨端和设备场景 | [验收 runner](../../tests/cross-sdk/runner/run_pubsub_acceptance.py)、[实施计划](../../../docs/AUN文档/AUN事件订阅发布TDD实施计划.md) |
553
+
554
+ 本轮[128项归档](../../tests/cross-sdk/artifacts/pubsub-4sdk-20261105/remove-manager-acceptance/final-acceptance-summary.json)由四种Bus各32/32组成,覆盖两种授权、同SDK和有向跨SDK;256份内嵌目录经核对,未显式调用catalog。默认凭证入库另由真实SQLite/IndexedDB重开专项签收,详见[实施计划 §9.9](../../../docs/AUN文档/AUN事件订阅发布TDD实施计划.md#99-凭证默认存储授权目录与移除管理身份)。历史208项保留;真实浏览器、macOS、真实双域设备E2E、实填64 MiB/RSS压力未纳入。