@agentunion/fastaun 0.5.28 → 0.5.29

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 (60) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/_packed_docs/CHANGELOG.md +19 -0
  3. package/_packed_docs/INDEX.md +9 -4
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +4 -3
  5. 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
  6. 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
  7. 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
  8. package/_packed_docs/sdk/0.5.28-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +228 -0
  9. package/_packed_docs/sdk/0.5.28-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +146 -0
  10. package/_packed_docs/sdk/0.5.29-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +180 -0
  11. package/_packed_docs/sdk/0.5.29-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +164 -0
  12. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +90 -90
  13. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +5 -3
  14. package/_packed_docs/sdk/INDEX.md +12 -4
  15. 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 +1 -1
  16. 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 +1 -1
  17. package/_packed_docs//346/216/247/345/210/266/346/266/210/346/201/257/344/270/216/350/256/276/345/244/207/350/203/275/345/212/233/345/215/217/345/225/206/346/226/271/346/241/210.md +426 -0
  18. package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/344/273/243/347/240/201/345/256/241/346/237/245/351/227/256/351/242/230/346/270/205/345/215/225.md +279 -229
  19. package/_packed_docs//351/243/216/346/216/247/345/223/215/345/272/224/344/270/216SDK/345/215/217/345/220/214/346/226/271/346/241/210.md +381 -0
  20. package/dist/agent-md-changes.d.ts +2 -14
  21. package/dist/agent-md-changes.js +41 -212
  22. package/dist/agent-md-changes.js.map +1 -1
  23. package/dist/agent-md-schema.js +2 -2
  24. package/dist/agent-md-schema.js.map +1 -1
  25. package/dist/agent-md.d.ts +4 -6
  26. package/dist/agent-md.js +53 -53
  27. package/dist/agent-md.js.map +1 -1
  28. package/dist/aid-store.d.ts +3 -1
  29. package/dist/aid-store.js +61 -40
  30. package/dist/aid-store.js.map +1 -1
  31. package/dist/client/control-messages.js +28 -6
  32. package/dist/client/control-messages.js.map +1 -1
  33. package/dist/client/delivery.js +2 -1
  34. package/dist/client/delivery.js.map +1 -1
  35. package/dist/client/group-state.js +2 -3
  36. package/dist/client/group-state.js.map +1 -1
  37. package/dist/client/lifecycle.js +0 -2
  38. package/dist/client/lifecycle.js.map +1 -1
  39. package/dist/client/v2-e2ee.js +8 -4
  40. package/dist/client/v2-e2ee.js.map +1 -1
  41. package/dist/client.d.ts +2 -2
  42. package/dist/client.js +96 -46
  43. package/dist/client.js.map +1 -1
  44. package/dist/discovery.js +2 -1
  45. package/dist/discovery.js.map +1 -1
  46. package/dist/ownership-v21.d.ts +2 -1
  47. package/dist/ownership-v21.js +17 -3
  48. package/dist/ownership-v21.js.map +1 -1
  49. package/dist/ownership-workflow.js +9 -5
  50. package/dist/ownership-workflow.js.map +1 -1
  51. package/dist/semantic-facade.js +4 -2
  52. package/dist/semantic-facade.js.map +1 -1
  53. package/dist/semantic-protocol.js +2 -0
  54. package/dist/semantic-protocol.js.map +1 -1
  55. package/dist/tools/cross-sdk-agent.d.ts +1 -1
  56. package/dist/tools/cross-sdk-agent.js +16 -34
  57. package/dist/tools/cross-sdk-agent.js.map +1 -1
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/package.json +8 -8
@@ -0,0 +1,381 @@
1
+ # 风控响应与 SDK 协同方案
2
+
3
+ > 状态:此前服务端、四端 SDK 与控制台响应链路已实施并完成受影响本地回归;本次风控签名改为 P-256,新增定向验证结果另记,尚未部署或通过新增机制线上验收。日期:2026-10-30。
4
+
5
+ ## 1. 目标与确定原则
6
+
7
+ 当前优先打通“服务端执行限制 → SDK 配合 → 应用获知 → 服务端观察 → 恢复或升级 → 控制台处置”的响应闭环。异常识别规则、阈值和模型可后续完善;第一阶段允许由已有确定性规则、管理员或受控测试触发同一处置流程。
8
+
9
+ | 项目 | 确定规则 |
10
+ | --- | --- |
11
+ | 控制权 | 服务端始终执行限制,不等待 SDK 回执,不依赖客户端自律 |
12
+ | SDK 的作用 | 使限速、暂停、重连等待等处置更柔性,减少无效请求,并通知应用 |
13
+ | 能力声明 | 客户端在握手中声明支持风控测试;按当前连接协商结果决定是否可协同 |
14
+ | 不支持时 | 触发风险后,带错误信息断连,并在规定期限内拒绝新连接 |
15
+ | 支持时 | 服务端立即限制,同时指令 SDK 执行;严重风险仍可直接断连、封禁 |
16
+ | 应用边界 | 原始指令仅 SDK 消费;应用收到独立的结构化风控状态事件 |
17
+ | 合规判断 | 服务端观察实际请求;ACK 只表示客户端报告,不证明真实遵从 |
18
+ | 归因边界 | 遵从不证明一定是 Bug;不遵从也不单独证明恶意动机 |
19
+ | 持续违规 | 对可归因的重复违规递增临时封禁;有上限、衰减、到期和人工解封 |
20
+ | 控制台 | 展示风险、证据、策略和执行进度;支持有权限人员解封并留存审计 |
21
+
22
+ 验收核心是响应链路可用、服务端限制不可被能力声明绕过、新旧 SDK 行为明确。本文不新增应用消息类别、用户消息优先级、SEQ 或 cursor。
23
+
24
+ ## 2. 当前机制与目标差距
25
+
26
+ | 当前机制 | 可复用部分与差距 |
27
+ | --- | --- |
28
+ | Gateway 入口保护 | 已有认证前限流、登录并发、部分通知/联邦限流、RPC 队列背压和连接配额;尚无统一风控策略、挑战观察和递增临时封禁 |
29
+ | Audit 黑名单 | 已有持久状态、revision 增量同步、Gateway 执行及回执;属于独立审核封禁,不能直接改造成自动风控到期状态 |
30
+ | SDK RPC | 已有并发控制,尚无统一服务端暂停/速率门控 |
31
+ | SDK 重连 | 已有指数退避和抖动;仅靠断线码不能保证旧 SDK 遵守等待时间 |
32
+ | SDK HTTP | agent.md、Storage、Ownership、认证与发现等调用分散;仅改 RPC 入口不能暂停这些请求 |
33
+ | 通知分发 | 未识别的普通通知可能进入应用层;新指令必须协商并在通用分发前截获 |
34
+ | 既有控制消息方案 | `sdk_control > control > normal` 属于独立消息协议;风控响应不依赖它先落地 |
35
+
36
+ 源码主要入口:Gateway `extensions/services/gateway/go/internal/httpserver/server.go`;Audit `extensions/services/audit/go/internal/rpc/service.go`;控制台 `extensions/services/aun_console/`;Python `transport.py`、`_client/lifecycle.py`,Go `transport.go`、`client_lifecycle.go`,TS/JS `src/client/lifecycle.ts`。路径均相对仓库根。
37
+
38
+ ## 3. 总体架构与职责
39
+
40
+ ```mermaid
41
+ flowchart LR
42
+ A[识别规则 / 管理员 / 受控测试] --> B[Gateway 风控权威状态]
43
+ B --> C[Gateway 与 HTTP 入口执行限制]
44
+ B --> D[受信控制通道通知 SDK]
45
+ D --> E[SDK 本地请求门控与重连等待]
46
+ E --> F[应用风控状态事件]
47
+ E --> G[风控回执]
48
+ C --> H[服务端观察与处置升级]
49
+ G --> H
50
+ H --> B
51
+ I[AUN Console 风控面板] --> B
52
+ B --> I
53
+ ```
54
+
55
+ Gateway 逻辑服务负责本 issuer 的风控权威状态、连接能力、在线控制投递、策略查询和管理操作。同一 issuer 的多个 Gateway 必须使用一致的权威存储或按主体路由至唯一写入者,不能分别写互不关联的数据库后都自称权威。
56
+
57
+ Message、Group、Storage、Stream 等服务各自在实际入口执行资源限制,并通过认证的 service plane 报告风险信号、查询策略、接收变更;不得直接读取 Gateway 数据库,也不得 import 跨模块风控库。SDK 各语言独立实现同一协议。Console 经后端调用管理接口,不直接访问风控表。
58
+
59
+ Audit 人工黑名单与自动风控分别生效。两者取更严格的结果;解除任意一种,不解除另一种。网络层洪泛由边缘防护处理,不依赖 SDK 合作。
60
+
61
+ ## 4. 风险输入与可信作用域
62
+
63
+ ### 4.1 本期识别输入
64
+
65
+ 统一风险输入包含:`incident_id`、可信主体、来源入口、规则/测试编号、原因类别、建议动作、有效期及可核对的证据摘要。识别端提出建议,Gateway 校验权限和动作范围后生成权威策略。后续更换规则或模型不改变 SDK 响应契约。
66
+
67
+ 最初只接入已有资源上限和可解释信号:请求/连接速率、加权方法成本、并发、流量字节、认证失败、重连风暴和限流后的重复请求。服务端过载、业务突发、多个合法设备、正常连接配额驱逐,单独不等同攻击。风控不读取 E2EE 消息明文。
68
+
69
+ ### 4.2 作用域
70
+
71
+ | 主体 | 适用范围 |
72
+ | --- | --- |
73
+ | 未认证来源 IP / 连接 | 边缘及认证前短时保护;采用受信代理链确定来源 IP |
74
+ | 已认证连接 | `(issuer, aid, device_id, slot_id, connection_id)`;连接 ID/代次由 Gateway 生成 |
75
+ | 已认证设备 | `(issuer, aid, device_id)`;换 slot 或重连不能绕过 |
76
+ | AID | 同 issuer 下该 AID 的全部设备;用于有足够证据的身份级异常 |
77
+
78
+ 不能凭握手请求里自报的 AID、device、slot 或 HTTP 参数给别人记分、拉黑。认证前只可靠处理来源,身份处罚必须经过密码学身份验证。设备字段必须绑定现有认证上下文;若当前认证不能证明设备归属,就不能把设备隔离强度表述为可抵御伪造设备 ID。
79
+
80
+ 柔性测试优先作用于具体连接。需要在断连后拒绝重连时,至少建立设备级或 AID 级策略;单纯连接级状态不能满足该要求。IP 不作为身份长期封禁的替代键,避免共享网络连坐。
81
+
82
+ ## 5. SDK 能力声明与握手
83
+
84
+ ### 5.1 v1 能力
85
+
86
+ 拟定新增 `auth.connect.capabilities.risk_test_v1: true`。缺省或 `false` 表示当前连接不支持。v1 必须完整支持本文的指令验证、RPC 门控、SDK 托管 AUN HTTP 门控、重连等待、状态回执和应用事件,不能只完成 RPC 暂停就宣称完整支持。
87
+
88
+ 服务端握手响应返回 `capabilities.risk_test_v1` 协商结果;仅协商成功时返回适用于当前身份的 `risk_state` 初始快照,未协商时只返回兼容的标量限制信息。服务端自身未完成协议时不得广告该能力。新 SDK 只有协商成功才接受专用控制事件;旧服务端没有该字段时采用普通连接兼容行为,但服务端错误中的重试时间仍可由新 SDK 按既有契约处理。
89
+
90
+ SDK 不提供“关闭本地闸门但继续声明支持”的应用选项。应用不能以能力声明为免限流凭据;恶意端谎报支持只会进入可观察的协同路径,服务端资源限制仍立即执行。
91
+
92
+ ### 5.2 设备记录和连接状态
93
+
94
+ 复用[设备能力方案](控制消息与设备能力协商方案.md)的 Gateway 权威原则:设备最近完整声明可持久保存在 Gateway 自有 `agent_devices`,当前连接单独保存协商值、连接代次和声明修订号;本功能可先单独扩展所需字段,不依赖 Message/Group 三流实现。
95
+
96
+ 设备离线不等于不支持,但发送在线风控指令必须看当前连接。相同设备的不同 slot 同时运行新旧 SDK 时分别处置;旧 slot 不继承新 slot 能力。只限制旧连接却要阻止该设备重连时,设备封禁也会影响同设备新连接,控制台必须展示实际扩大后的范围。
97
+
98
+ 握手声明按完整快照替换,缺失能力不能从历史合并回来。未实现持久能力字段时不能假报持久化完成;历史能力只用于记录和规划,绝不覆盖当前连接判断。已有有效封禁优先于能力协商:升级 SDK、改声明或更换 slot 不解除封禁。
99
+
100
+ ## 6. 策略模型与组合规则
101
+
102
+ ### 6.1 指令封套
103
+
104
+ 同一主体使用单调的 `revision`,每次下发该主体的**完整有效策略快照**,避免部分更新和消息丢失造成永久残留。不同主体分别比较 revision;清空设备策略不清空 AID 策略。
105
+
106
+ | 字段 | 语义 |
107
+ | --- | --- |
108
+ | `version` | 风控协议版本,v1 固定为 1 |
109
+ | `issuer`、`subject` | 签发域、主体类型及经过验证的身份字段;连接主体还绑定 slot/连接代次 |
110
+ | `revision` | 该主体的持久单调修订号;不能因重启、解封或数据库记录到期归零 |
111
+ | `delivery_id` | 本次投递关联 ID,用于回执和追踪;不改变策略期限 |
112
+ | `server_time_ms` | 快照生成时的服务端时间 |
113
+ | `policies` | 当前有效策略数组;空数组是明确解除该主体策略的快照 |
114
+ | 签名材料 | 握手 `risk_signing_key.algorithm=ECDSA-P256-SHA256`,公钥为 SEC1 未压缩 65 字节(按 `04`、`X32`、`Y32` 顺序连接),key ID 为 `base64url(SHA-256(pub65))`;封套 `signature` 为 P1363 的 64 字节 `r32`、`s32` 顺序连接,公钥与签名使用无填充 base64url |
115
+
116
+ 每个 policy 包含 `policy_id`、`incident_id`、`reason`、`issued_at_ms`、`effective_at_ms`、`expires_at_ms`、`actions`,以及需观察时的 `grace_ms`、`observation_window_ms`。期限由服务端确定;重发、转发、重连不得重新开始完整 TTL。风控有效期是策略有效期,不使用消息 `ttl` 参数或消息持久/volatile 语义。
117
+
118
+ 策略身份只比较稳定正文 `issuer + subject + revision + policies` 的规范化内容,策略/动作按固定顺序编码;`delivery_id`、`server_time_ms` 和签名等投递元数据可以随重发变化,不参与“同 revision 内容冲突”的判断。签名仍覆盖本次完整封套,不能据此放宽元数据验证。
119
+
120
+ ### 6.2 有限动作集
121
+
122
+ | 动作 | 必须指定的参数 | 服务端执行 / SDK 配合 |
123
+ | --- | --- | --- |
124
+ | `pause_requests` | `request_scope`、`until_ms` | 拒绝范围内新请求 / 本地立即拒绝对应新调用 |
125
+ | `limit_requests` | `request_scope`、`rate_per_sec`、`burst`、`max_inflight` | 服务端令牌桶与并发门禁 / SDK 同范围门控 |
126
+ | `reconnect_after` | `not_before_ms` | 截止前拒绝对应身份新连接 / 停止提前重连 |
127
+ | `disconnect` | 安全原因说明 | 按有界发送/关闭流程断连 / 进入受限或封禁状态 |
128
+
129
+ `request_scope` 只允许已登记的 AUN RPC 方法/方法组,或由受信服务发现确定的 AUN HTTP 服务与路由组,可表达全部业务请求。不能携带任意 URL、代码、shell 命令或客户端任意配置。HTTP 限制跟随服务身份,不能因切换 IP、端口或发现地址失效,也不能被不可信重定向扩大到无关网站。
130
+
131
+ v1 的 HTTP 覆盖为 SDK 托管的 AUN 网络请求,包括 agent.md、Storage/GroupFS/Ownership 数据面以及认证、发现等调用入口;每条调用标注固定请求类别。仅 ACK、必要保活、受限认证和状态恢复使用服务端固定的控制白名单与独立小配额。应用自建 HTTP、外部 OSS 签名 URL 和第三方网络不在可强制覆盖承诺内;对应服务仍执行自身配额,签名 URL 权限与带宽另行约束。
132
+
133
+ `limit_requests` 的预算按 `(issuer, subject, request_scope, policy_id)` 在集群内共享,多 Gateway、RPC/HTTP 副本不能各自获得完整额度。实施选择按配额键唯一 owner 或原子共享计量;`max_inflight` 同样全局计数,并为异常退出设置有界占用租约。入口本地桶只能作为额外硬保护,不能替代策略总预算;计量不可用时拒绝相应新准入并返回可重试错误。
134
+
135
+ ### 6.3 多策略、时间和更新
136
+
137
+ 多个主体/策略同时适用时取交集:任一封禁即拒绝;暂停优先于速率限制;速率/并发取较小值;最早重连时间取较晚值。解除一个 policy 不解除其他 policy。暂停到期后的速率限制可继续生效。
138
+
139
+ SDK 使用当前可信服务端时间估计计算剩余时间,再使用单调时钟等待;同 revision 重复投递不延长截止。更小 revision 丢弃;同 revision 的稳定策略正文不同视为协议异常,保留已验证限制并走有界恢复,不执行互相矛盾的动作。只有更高 revision 的完整快照可以提前解除同主体限制。
140
+
141
+ 可信时间基准来自与本次握手/受限查询关联的 WSS/HTTPS 响应,记录请求往返误差并按单调经过时间推进;推送内签名的旧 `server_time_ms` 不能倒拨该基准,也不能直接用 `expires_at_ms - 旧 server_time_ms` 重新等待。没有可用基准时先有界获取新快照,策略接近到期且超出已知误差范围时重新确认;按当前基准已过期的策略只更新水位,不再安装。重启先以已保存绝对截止恢复,再由受限握手校正,不能将磁盘快照当成刚签发的新策略。
142
+
143
+ 策略到期后停止该策略的限制,不因状态查询失败无限续期。主体最新 revision 的终态/水位需保留,防止迟到旧指令复活已解除策略。SDK 重启使用墙钟和已保存时间恢复剩余限制,并在受限握手校正;服务端截止时间才是最终准入依据,修改本地时钟不能绕过服务端。
144
+
145
+ ## 7. 服务端先执行的处置流程
146
+
147
+ ### 7.1 支持协同
148
+
149
+ 1. 校验风险输入和主体,持久提交策略、主体 revision 与可靠分发任务;当前入口同时启用限制,不等待 SDK ACK。
150
+ 2. Gateway 对当前连接安装门禁,相关 HTTP 服务更新策略;分别记录入口执行进度。向明确协商支持的连接下发快照。
151
+ 3. SDK 验证后安装本地闸门、回报状态、通知应用。普通请求仍受服务端独立校验。
152
+ 4. 观察窗口结束后恢复、继续限流或升级断连/临时封禁。严重风险可以跳过协同,直接进入断连封禁。
153
+
154
+ SDK 未回执时标记“未确认协同”,可在有界等待后转为断连和限时拒绝以继续保护服务;不得仅因没有 ACK 就记录“已证实恶意”。协同超时不影响此前已生效的门禁。
155
+
156
+ ### 7.2 未声明、不支持或版本不兼容
157
+
158
+ 1. 触发风险后直接建立带截止时间的设备级或 AID 级重连封禁,并安装当前入口限制。
159
+ 2. 经既有兼容错误/断线通知给出稳定原因码、安全说明、剩余等待时间和封禁截止,再有界关闭连接;不发送新风控指令。
160
+ 3. 所有 Gateway 在规定期限内拒绝经过身份验证的新连接;新设备或新 slot 是否受限取决于原策略作用域,不能无意扩大。
161
+
162
+ 断线通知可能丢失;服务端不得等其送达才执行封禁。客户端下一次连接仍得到拒绝和剩余时间。未支持协同不是恶意证据,但触发风险后按此直接处置。
163
+
164
+ ### 7.3 受限握手与独立 HTTP
165
+
166
+ 未认证时先做便宜的 IP/连接保护;验证声称的身份后再查询身份封禁,避免攻击者自报他人 AID 造成误封或枚举信息泄漏。受限握手只允许最低成本的身份验证和策略返回,不开放业务、消息投递或额外会话;也必须单独限频。
167
+
168
+ RPC 转发前和 HTTP 实际入口都检查策略。Storage 上传下载按可信 token/ticket 签发主体、对象和成本限制;只在签发 URL 的 RPC 限流无法约束已经签发的数据面访问。无法验证设备的 HTTP 入口只能承诺 AID/token/资源级执行,不能凭 SDK Header 假装实现设备级隔离。
169
+
170
+ 已在服务端接纳的操作遵循各方法既有提交语义。限制默认阻止后续准入,不将已经提交的写入回滚;必要时可按既有安全取消机制停止传输,但不能告诉调用方它一定未执行。
171
+
172
+ ## 8. 控制通道、错误与旧端隔离
173
+
174
+ ### 8.1 专用传输
175
+
176
+ 拟定专用服务端事件 `event/gateway.risk_control`,SDK 在公共 `notification`、原始应用事件和用户消息分发前验证并消费,不把原始封套交给应用。仅在当前连接协商成功时发送。风控事件不经过 P2P/Group 队列,不分配消息 SEQ/cursor,不受普通消息积压阻塞。
177
+
178
+ 同一签名快照可通过风控事件、RPC 拒绝的 `data.risk_state`、HTTP 429 响应或受限握手返回。直连 HTTP 服务通过内部查询获得 Gateway 快照,不自行伪造 Gateway 签名;浏览器需配置可读的错误正文和 `Retry-After` 等头。所有入口使用正式 WSS/HTTPS,不关闭 TLS 校验。
179
+
180
+ 独立 HTTP 只有在现有认证材料能可信绑定具体设备/连接,并经 Gateway 查询确认该连接已协商能力时,才返回 `risk_state`;不能凭用户自填 Header 判断绑定。仅能验证 AID、公共请求或没有这种绑定时,只返回标准标量错误/重试时间。新 SDK 将此类 429 作为当前服务/路由的受限反馈处理,不据它伪造跨身份策略或风控 ACK;完整跨范围指令仍由已协商的 Gateway 通道送达。
181
+
182
+ SDK 从受信任的协商握手接收 Gateway 专用 P-256 风控公钥;各端 WSS/TLS/issuer 主机检查边界见 [0.5.28 移植指南](sdk/0.5.28-SDK开发与移植指南.md#5-风控握手策略与服务依赖)。签名输入是去掉 `signature` 后的完整 UTF-8 canonical JSON(保留合法 Unicode 字符)的 SHA-256,覆盖目标、revision、时间和动作,验签接受合法 high-S/low-S,旧 Ed25519 风控键不再接受。普通 AID、Owner、群成员或另一域服务不能凭用户消息暂停客户端。
183
+
184
+ ### 8.2 错误契约
185
+
186
+ | 场景 | 拟定兼容表达 |
187
+ | --- | --- |
188
+ | 风控拒绝 RPC/握手 | 复用限流错误 `-32029`,`data.reason=RISK_RESTRICTED` 或 `RISK_BLOCKED` |
189
+ | 风控拒绝 HTTP | HTTP 429,`Retry-After` 秒值向上取整;JSON 提供相同 reason 和毫秒剩余时间 |
190
+ | 临时风控断连 | WS 4029,加安全的短原因;完整信息在此前错误/兼容断线通知中尽力送达 |
191
+ | 服务过载 | 保留现有 `-32429` 及其过载原因,不据此自动记恶意违规 |
192
+
193
+ 结构化错误包含 `incident_id`、适用时的 `policy_id`、`server_time_ms`、`retry_after_ms`、适用时的 `blocked_until_ms`;只有协商成功的端点才返回 `risk_state` 新控制封套。以上映射是目标契约,实施需验证四 SDK 现有错误解析;不把 `-32429` 统一改作风控。
194
+
195
+ 不能复用现有 4003 人工黑名单/无效身份、4009 会话替换或 4013–4015 配额等具有不同重连语义的关闭码。4029 已有容量/限频用途,SDK 必须结合结构化 reason 区分;单独收到 4029 时按普通限流退避处理,不猜测永久封禁或精确期限。
196
+
197
+ ### 8.3 控制预算
198
+
199
+ ACK、必要保活、受限握手和策略恢复使用固定小额度,与普通业务配额分开,但都有频率、并发和大小上限。服务器不允许客户端用“控制消息”标记为任意业务申请豁免。接收循环只安装状态和调度任务,不等待同接收循环才能完成的 RPC,避免控制面死锁。
200
+
201
+ ## 9. SDK 执行、回执与应用事件
202
+
203
+ ### 9.1 执行顺序
204
+
205
+ 四端一致:验证来源/目标/版本 → 对 revision 幂等比较 → 原子安装本地门禁 → 保存跨重启所需限制 → 安排独立回执 → 发布应用状态事件。应用回调异常或阻塞不能延迟门禁;回执和应用事件不在接收循环中同步等待业务处理。
206
+
207
+ 本地暂停时,对新业务调用直接返回 `RiskRestrictedError`,带范围、原因和重试时间;不累积无界等待队列。限速超过本地预算同样返回明确受限错误。已进入内部队列但尚未写出的请求,在写出前再次检查策略并结束为未发送;已经写出的请求按原响应/超时契约完成,结果未知的写操作不能自动重放。
208
+
209
+ 同一 SDK 实例所有 RPC、后台 pull/ack 编排、自动重试和托管 HTTP 共享门禁。业务 ACK 是否允许由服务端登记的请求类别决定,不能默认所有 ACK 都豁免。大文件上传、续传、HTTP 重试和重定向在新请求发出前均检查门禁;应用传入自定义 HTTP 客户端时仍在 SDK 调用包装层拦截,不能要求用户配置代理才能生效。
210
+
211
+ ### 9.2 回执
212
+
213
+ 拟定 `gateway.risk_ack`,在认证连接或受限恢复通道上使用。内容只引用 `delivery_id`、主体、revision、关联策略以及 `received` / `applied` / `unsupported` / `failed` 状态;错误说明采用固定枚举和有界文本。相同回执幂等,不能推进到尚未发送的 revision。
214
+
215
+ SDK 先装闸后回报 `applied`。安装失败不得谎报成功,应报告 `failed`,暂停其能够安全停止的业务并进入断连等待;服务端保持限制并可转为硬断连。过期策略可回报已过期,不重新执行。
216
+
217
+ 应用不能直接代 SDK ACK,也不能确认“解除”。ACK 消失或作假不绕过服务端门禁;服务端可重发当前快照,但重发有界且不延长期限。已断连后无需为了补发旧 ACK 违反重连等待。
218
+
219
+ ### 9.3 应用接口
220
+
221
+ 拟定应用事件 `risk_state_changed`,只发布处理后的状态:`restricted`、`blocked`、`recovered`,以及原因类别、作用范围、策略标识、重试时间、恢复截止和本地执行结果。不包含原始签名指令、内部服务地址或完整识别阈值。
222
+
223
+ 事件提供稳定标识和 revision,重复策略不重复发布同一状态。SDK 提供只读当前状态查询,供应用晚订阅或重启后恢复展示;不承诺跨进程崩溃的事件 exactly-once。应用可以停止业务循环、展示提示或引导更新 SDK,但不能取消门禁。恢复事件只表示相应策略已移除/到期;如还有其他限制,状态仍为受限。
224
+
225
+ ## 10. 重连、离线与提前解封
226
+
227
+ 唯一重连状态机维护 `next_retry_at`:取本地指数退避截止、服务端重连截止和其他仍有效等待的最大值,之后只添加正向抖动。用户调用 `connect()`、自动恢复、token 刷新、半开检测都不能创建绕过等待的第二条连接循环;`connect()` 在受限期内返回带剩余时间的错误。
228
+
229
+ SDK 持久保存按 issuer/身份隔离的已验证策略、水位和截止;签名/目标不合法的磁盘数据不得生成新权限,服务端仍有最终准入控制。连接级策略只适用于旧连接,持久设备/AID 策略在新握手重新核对。P-256 替换不迁移旧客户端风控缓存:TS/JS 仅含旧 Ed25519 键的记录恢复返回 `risk_state_unavailable`;Python/Go 不恢复旧键及其快照,不能声称旧等待已自动恢复。
230
+
231
+ 离线限制保存在服务端,客户端上线/重连时取当前快照;不依赖一条离线消息送达。SDK 按指令保持断线时,Console 提前解封不会自动被客户端知道。v1 不增加封禁期轮询业务连接:在线受限连接可收到解除快照;已断开的端通常最迟在原等待到期后的重连中恢复。人工主动重试仍先受本地等待,不能承诺“点解封即刻恢复所有离线客户端”。
232
+
233
+ 等待期限结束后只放行一次状态机重试;若还有其他策略或人工黑名单,继续按最新拒绝响应等待。不得因单个风控策略到期重置全部重连失败计数或绕过其他限制。
234
+
235
+ ## 11. 合规观察与递增临时封禁
236
+
237
+ ### 11.1 两种独立状态
238
+
239
+ 处置状态记录 `restricted / blocked / expired / released`;协同状态另记 `not_supported / pending / received / applied / failed / unknown`。服务端执行状态和“客户端声称已应用”分别展示。风险归因另记“疑似异常、观察到违令、已复核”,不能通过一个布尔值统称恶意。
240
+
241
+ 观察以服务端真实准入计数为准:指令被确认送达,生效时间与合理宽限已过,且同一可归因范围内仍有新业务请求超过允许预算,才构成协同违令证据。排除发令前已在途、合法控制预算、其他设备/连接、未协商、掉线或未确认送达。宽限只影响记分,不让超限请求通过。
242
+
243
+ 攻击者拒不 ACK 时,服务端照常限制并在超时后断连/暂封,无须无限等待。缺失 ACK 记“协同未确认”;后续仍可依据独立、可验证的资源滥用或封禁规避证据升级,但不能伪称已完成遵从测试。
244
+
245
+ ### 11.2 升级规则
246
+
247
+ 建议初始临时封禁阶梯为 30 秒、2 分钟、10 分钟、1 小时,自动封禁上限 24 小时;具体参数是实施前冻结的服务端策略,不由 SDK 硬编码。长于自动上限或永久封禁走授权人工/既有审核流程。
248
+
249
+ 每个 `incident_id + 观察窗口 + 处罚主体` 最多增加一次 strike;同一事件被多个入口上报需幂等合并。封禁期内重复重连先拒绝和聚合计数,不逐请求延长截止;只有满足独立升级规则、生成新的处置版本时才延长。升级后的截止由权威端固定,重发不变。
250
+
251
+ 示例恢复规则:连续 24 小时没有新的已确认违规后降低一级,长期干净后归零;封禁到期恢复为观察状态,不立即抹除历史。未支持能力的客户端按直接封禁路径处理,不能因未 ACK 加 strike;若持续可归因地超出服务端拒绝/重试契约,可按独立的重复滥用规则递增临时封禁。
252
+
253
+ ## 12. 持久化、缓存与跨服务一致性
254
+
255
+ ### 12.1 Gateway 自有权威状态
256
+
257
+ 以下是逻辑数据集合,不要求与其他模块共享表;具体 DDL 在实施时结合现有 Gateway 存储冻结。
258
+
259
+ | 集合 | 最小内容 |
260
+ | --- | --- |
261
+ | `risk_incidents` | 事件 ID、来源/规则、可信主体、证据摘要、分类、首次/末次时间、聚合次数、关联策略 |
262
+ | `risk_subject_states` | 主体唯一键、单调 revision、有效策略集合、strike/上次违规/衰减基准和固定截止 |
263
+ | `risk_deliveries` | 策略版本、目标实例/连接、SDK 能力、发送与回执、入口执行状态、重试与观察结果 |
264
+ | `risk_changes` | 持久变更/outbox、幂等键、管理员与原因、前后版本和解除终态;用于可靠分发与审计 |
265
+
266
+ 策略、revision、去重记分和 outbox 在权威事务中提交。Gateway 专用 `gateway_risk_keys` 的新 `active-p256` 槽保存 32 字节 P-256 私钥 scalar,保留旧 `active` 槽,不清空既有策略及 revision;槽名不是 wire key ID,后续快照用新键签署。短期统计可在内存或 Redis,当前处罚、截止、记分不能只靠 TTL 或在线状态;清理不能删除防迟到更新的水位。业务记录不保存消息正文、私钥或 token,签名私钥仅由专用键存储管理。
267
+
268
+ ### 12.2 分发与回源
269
+
270
+ 复用 service plane 定义内部 `gateway.risk.report`、`gateway.risk.query`、`gateway.risk.list_changes`、`gateway.risk.enforcement_ack`。Gateway 在线通知和相关服务执行变更均从可靠 outbox 触发,按 revision 幂等;可借鉴现有 Audit 快照、增量及执行回执,不改变 Audit 黑名单的权威归属。
271
+
272
+ 入口使用 TTL 有界缓存与变更失效,初始建议最长 5 秒;缓存值必须含主体 revision、策略截止和缓存租期。过期后回源 Gateway,不能将查询故障缓存为“无风险”。并发查询合并,批量查询限制大小;早于失效发出的查询响应不得重新填入旧状态。
273
+
274
+ 新连接的身份封禁准入从权威状态确认,不用陈旧的“允许连接”缓存放行。已知封禁离线/重启仍生效;首次查不到权威状态的新连接或需要刷新策略的业务返回有界可重试的服务不可用,不记恶意、不绕过未知状态。已知策略在其截止前按现有租约执行,租约过期仍无法确认则采用可重试拒绝;已经到期的策略不能伪装成继续封禁。
275
+
276
+ ### 12.3 生效和解封传播
277
+
278
+ 跨入口存在传播时间,不能宣称所有独立 HTTP 服务与 Gateway 瞬间原子切换。触发入口立即限制;其他入口接收变更或最迟缓存租期结束重新确认。每个执行方报告 applied revision;系统区分“权威已提交、部分入口生效、已确认生效”。只有已确认生效且完成 SDK 送达的对应入口才纳入违令观察。
279
+
280
+ 人工解除、自然到期和策略替换都产生更高 revision/终态。迟到分发、旧查询、旧 ACK 不得复活限制。执行方宕机恢复时必须先取快照再接纳业务;Console 对长期未确认的执行方显示 degraded,不能假报完成。服务端本地保护在持久提交失败时仍可拒绝/断连,但必须标记降级,不能声称跨实例封禁已经持久化。
281
+
282
+ ## 13. 控制台面板与解封闭环
283
+
284
+ ### 13.1 展示
285
+
286
+ | 页面 | 必备内容 |
287
+ | --- | --- |
288
+ | 风险概览/列表 | 时间、严重度、原因、issuer/AID/设备/连接、入口、当前处置、重复次数、封禁截止;按状态/主体/规则/SDK 版本筛选 |
289
+ | 风险详情 | 规则与证据摘要、能力声明、策略及 revision、发送/回执/观察时间线、合规/未知判断、升级历史 |
290
+ | 执行分布 | 各 Gateway/HTTP 服务 applied revision、执行时间、pending/degraded;SDK 自报与服务端执行分开 |
291
+ | 主体状态 | 当前全部生效限制及来源,区分自动风控、人工风控、Audit 黑名单,展示实际恢复条件 |
292
+ | 操作历史 | 操作员、时间、原因、幂等键、前后版本及结果;保留误报标记和解封历史 |
293
+
294
+ 风险详情只展示可解释证据和计量摘要,不展示 E2EE 正文、密钥、完整 token 或无必要的个人信息。IP 和敏感身份按既有权限处理。
295
+
296
+ ### 13.2 管理操作
297
+
298
+ 第一阶段支持:查看/检索、对指定 policy 解除封锁、标记误报及备注、必要时重发当前状态。管理员可用同一受审计接口对指定测试身份下发有界策略,以验收响应链路;不通过伪造攻击流量制造触发。
299
+
300
+ 解封接口必须校验管理权限、必填原因、`expected_revision` 和幂等操作 ID。事务提交新状态与审计/outbox;并发版本不符返回冲突,操作员重新核对,不盲目覆盖刚升级的策略。正常解封不默认清零 strike、不删除事件;误报复核若需撤销对应 strike,应作为单独显式权限操作并记录依据。
301
+
302
+ 面板呈现“解封已提交 → 传播中 → 执行方已确认”;失败显示未确认的入口和重试状态。服务端解除成功不等于断线 SDK 已获知,按照第 10 节说明客户端最早恢复时机。解除单个 policy 后仍有其他封禁时,清楚显示“仍受限”及剩余来源。
303
+
304
+ ### 13.3 权限与后端接口
305
+
306
+ Console 后端经认证服务调用 Gateway 的风险查询、事件详情、解除和误报复核接口;浏览器不直接调用内部 RPC或读数据库。只读查看与解封/修正记分分权,并记录现有登录身份。跨 issuer 管理只使用既有授权服务路由,不把任意客户端提供的 URL 当管理目标。
307
+
308
+ 接口的具体 HTTP 路径按 `aun_console` 现有命名在实施阶段冻结;本设计不声明这些接口已存在,也不直接实施对外接口变更。
309
+
310
+ ## 14. 端到端场景
311
+
312
+ | 场景 | 预期行为 |
313
+ | --- | --- |
314
+ | 新 SDK 因 Bug 高频 RPC | 服务端立即限流;SDK 装闸并发应用事件;后续请求减少,观察后有序恢复,不能因此宣布已证明 Bug |
315
+ | 新 SDK 谎报支持/假 ACK | 服务端始终拒绝超限;观察同范围新请求后升级,ACK 不换取放行 |
316
+ | 旧 SDK 触发风险 | 不发新指令;带安全错误断连,设备/AID 封禁固定期限,所有 Gateway 拒绝重连 |
317
+ | 指令未送达/无 ACK | 继续强制限制;协同未知,超时后可断连暂封,不冒称完成遵从测试 |
318
+ | 多设备/混合 slot | 按当前连接能力选择协同;处罚按明确主体,设备/AID 扩大范围时同步控制其他目标 |
319
+ | HTTP 上传已开始 | 新请求先拒绝,在途按提交/取消契约处理,不能把传输取消等同写入未发生 |
320
+ | 客户端重启/换 Gateway | 服务端策略和 strike 不变;SDK 恢复本地等待,受限握手返回最新状态 |
321
+ | 管理员提前解封 | 新版本防旧封禁复活;在线 SDK 得解除快照,断线 SDK 可能继续等到原截止 |
322
+ | 自动风控和 Audit 并存 | 只解自动策略仍被 Audit 拒绝;面板显示两种独立来源 |
323
+
324
+ ## 15. 服务端与 SDK 改动范围
325
+
326
+ | 模块 | 与本需求直接相关的改动 |
327
+ | --- | --- |
328
+ | Gateway | 当前连接能力、设备最近声明、权威风险存储/事务、RPC/握手门禁、专用控制投递、ACK、断连错误、跨实例同步、管理查询/解封 |
329
+ | Message / Group | 信号上报、策略查询/失效和 RPC 实际准入;无需为了风控实现三类消息 SEQ |
330
+ | Storage / Stream 等 HTTP 服务 | 可信身份/资源配额、策略执行、429 错误和执行回执;不借客户端自报 Header 放宽限制 |
331
+ | Audit | 保留原人工黑名单边界;按既有机制接收必要操作审计,不把自动到期写成 Audit 解封 |
332
+ | AUN Console | 风险列表/详情、执行状态、来源区分、权限与带版本解封/误报复核 |
333
+ | Python / Go / TS / JS SDK | 能力与签验、内部事件截获、统一请求准入、HTTP 逐入口覆盖、唯一重连状态机、本地持久限制、回执和应用状态 |
334
+
335
+ SDK 接入先列出实际网络出口清单,覆盖认证/发现、agent.md、Storage/GroupFS、Ownership 及后台同步等;不为此重构无关模块。HTTP 调用统一经过本 SDK 内的准入器,可以在既有调用包装层接入,无须统一 HTTP 库。四端只共享协议和测试向量,不引入跨模块代码依赖。
336
+
337
+ ## 16. 实施顺序与发布兼容
338
+
339
+ | 阶段 | 工作 | 完成条件 |
340
+ | --- | --- | --- |
341
+ | P0 | 冻结封套/签验共同向量、错误、作用域、能力、时间和状态机 | 无“支持即免控制”、旧端泄漏和范围歧义 |
342
+ | P1 | Gateway 持久策略、主体门禁、固定期限、断连拒绝和内部查询/同步 | 可人工/测试触发,跨 Gateway 不因重启清零 |
343
+ | P2 | 实际 HTTP/RPC 入口执行、执行回执和故障语义 | 所声明服务范围都能独立拒绝,不依赖 SDK |
344
+ | P3 | 四端 SDK 请求门控、HTTP 覆盖、重连/持久恢复、ACK/应用事件 | 四端完整满足 v1 才广告能力 |
345
+ | P4 | Console 展示、带版本解封、误报复核和审计 | 提交/传播/执行状态可核对,旧状态不复活 |
346
+ | P5 | 单域、分布式、cross-SDK、受控公网验收 | 目标已部署版本与报告一致;识别算法可继续独立迭代 |
347
+
348
+ 服务端先部署兼容接口但不强制新能力,再上线完整 SDK 并按协商开启。旧端仍可正常使用普通业务;只有触发策略才进入断连封禁。回滚/关闭协同不能清除服务端有效处罚;无法协同的连接回到直接处置路径,不能把原始控制事件透传应用。
349
+
350
+ 实施采用 TDD,在受影响现有测试中先固定失败证据再实现最小改动,按授权范围更新契约测试;本次不新增测试代码、不部署、不启动 Kite。
351
+
352
+ ## 17. 验收矩阵与证据要求
353
+
354
+ | 范围 | 必验项目 |
355
+ | --- | --- |
356
+ | 服务端强制 | ACK 前即拒绝超限;假 ACK、虚报能力、绕开 SDK 请求均不放行 |
357
+ | 旧端 | 缺省/不兼容能力触发后安全错误断连、固定期限拒绝重连;不收新内部指令、不按无 ACK 记恶意 |
358
+ | 新端 | 完整协商、签验/目标校验、先装闸后应用事件、超时未协同转硬处置 |
359
+ | 动作 | RPC/HTTP 暂停、速率/并发、跨实例共享预算、指定方法和服务范围、重连截止、控制预算有界 |
360
+ | 调用结果 | 未写出队列二次检查、在途请求排除、上传取消不误判提交、结果未知写入不重放 |
361
+ | 时间/版本 | 时钟偏差/往返误差、迟到旧时间不续期、重发元数据变化、乱序/正文冲突、到期、更高版本解除、重启持久等待 |
362
+ | 身份隔离 | AID/设备/连接、多 slot 新旧并存、来源 IP/NAT、伪造身份不封受害者 |
363
+ | HTTP 覆盖 | 所有 SDK 托管出口、自定义 HTTP 客户端、重定向/续传、能力可信绑定/无绑定标量错误、浏览器 CORS、数据面原生配额 |
364
+ | 记分 | 多入口重复上报去重、送达/宽限/观察边界、无 ACK/掉线未知、固定处罚截止与衰减 |
365
+ | 跨实例 | 独立数据库、权威路由、缓存失效与在途旧响应、漏事件回源、重启快照、权威故障可重试拒绝 |
366
+ | 控制台 | 权限、原因/幂等/CAS、误报显式修正、各入口执行进度、迟到旧封禁、Audit 并存、断线提前解封边界 |
367
+ | 兼容/非回归 | Python/Go/TS/JS 共同向量,Windows/Linux/macOS,普通消息/Ownership/ETag/连接配额和过载语义 |
368
+
369
+ 先完成受影响单元、lint/类型检查,再按[单域指南](aun测试运行指南.md)、[分布式指南](aun分布式测试运行指南.md)进行集成/cross-SDK/多 Gateway 验收。首个失败立即停止,修复后从检查点续跑,不重复未受影响的通过项;Python 测试使用 `-X utf8`。
370
+
371
+ 公网按[公网测试指南](aun公网测试运行指南.md)使用 `evolagent.cn`、正式 TLS、独立测试身份和运行目录。本机制用受权限保护、只绑定测试身份的策略触发验证,不对公网做洪泛/压测来模拟攻击。报告记录服务端与 SDK 版本、策略 ID/revision、作用域、触发/执行/回执/恢复时间及断点;现有 runner 未覆盖的新断言应在实施时补齐,不能以旧用例通过冒充本机制通过。
372
+
373
+ 本次实施已完成受影响单元回归、构建与静态检查;尚未完成新增机制的部署、服务集成或线上 E2E,不存在“已上线/已验收”结论。前一份控制消息方案记录的历史公网结果不作为本文风控协议的证据。
374
+
375
+ ## 18. 参考
376
+
377
+ - [控制消息与设备能力协商方案](控制消息与设备能力协商方案.md):设备能力权威、当前连接协商和普通 SDK 控制消息边界。
378
+ - [服务连接稳定性完整方案](../../docs/服务连接稳定性完整方案.md):既有退避、过载、重试与写入结果未知语义。
379
+ - [协议安全考虑](protocol/09-安全考虑.md):既有连接/速率/大小建议,不等于本文机制已经实施。
380
+ - [Gateway 源码](../../extensions/services/gateway/go/internal/httpserver/server.go)、[Audit RPC](../../extensions/services/audit/go/internal/rpc/service.go)、[Console 服务目录](../../extensions/services/aun_console/):现有服务边界与后续接入入口。
381
+ - [Python 传输层](../python/src/aun_core/transport.py)、[Go 传输层](../go/transport.go)、[TS 生命周期](../ts/src/client/lifecycle.ts)、[JS 生命周期](../js/src/client/lifecycle.ts):请求、通知与重连的现有入口。
@@ -7,18 +7,11 @@ type OwnerFacts = {
7
7
  export declare class AgentMdChangeCoordinator {
8
8
  private readonly client;
9
9
  private readonly options;
10
- private timer;
11
- private periodic;
12
- private requested;
13
- private running;
14
10
  private closed;
15
11
  private readonly refreshes;
16
12
  private delivery;
17
- private flushing;
18
- private flushRequested;
19
13
  constructor(client: any, options?: {
20
14
  ownerResolver?: (aid: string, type?: string) => Promise<OwnerFacts>;
21
- rosterLoader?: () => Promise<string[]>;
22
15
  });
23
16
  private identity;
24
17
  private scope;
@@ -30,13 +23,8 @@ export declare class AgentMdChangeCoordinator {
30
23
  private owner;
31
24
  refresh(aid: string): Promise<void>;
32
25
  private refreshOnce;
33
- deliverEvents(): Promise<void>;
34
- flushNotifications(): Promise<void>;
35
- private flushOnce;
36
- private roster;
37
- private storageRoster;
38
- recover(): Promise<void>;
39
- schedule(): void;
26
+ private deliverEvent;
27
+ committed(aid: string, notice: Record<string, any>, manager?: any): Promise<void>;
40
28
  close(): void;
41
29
  }
42
30
  export {};