diff --git a/docs/integration/runtime-contract.md b/docs/integration/runtime-contract.md index 3f65e32..6c94acb 100644 --- a/docs/integration/runtime-contract.md +++ b/docs/integration/runtime-contract.md @@ -98,7 +98,7 @@ HM 实现 #60(计费 key 注入)前需 Swarm 定死的参数,逐条口径 | A.1 | `sk-` 粒度 + mint 时机 | **每用户一把**(首次蜂群开通 mint、跨该用户所有 run 复用);同一用户的专家 agent 注入同一把 `sk-`、计费归一到发起用户。 | | A.1 | KV secret 命名约定 | `swarm-model-key-`(Swarm 只取 `secret_ref` 末段做名,不强约束路径;命名供 HM 定位)。 | | A.2 | KV secret **value 格式** | **JSON `{"openai_api_key":"sk-..."}`**(对齐 callback 签名密钥的 `{"callback_signing_secret":"..."}` 约定,可扩展)。Swarm 解析字段名 = `openai_api_key`;裸 `sk-` 字符串亦兼容(`_extract_model_key`);解析不到/字段缺失 → 不伪造、agent keyless 明确报错。 | -| A.3 | Swarm 读 KV 身份 / RBAC | **⚠ 待定(联调阻塞前置)**:Swarm 运行时需对 `heicode-kv`(`https://heicode-kv.vault.azure.net`)有 `Key Vault Secrets User`(经 Workload Identity / MI)。该 RBAC 授权归属(运维 / Swarm 部署侧)**尚未敲定**,须先确认——不通则 `secret_ref` 解不出。生产 KV 适配器(`SECRET_RESOLVER`)在仓外经部署接线;dev/CI 用 `HEICODE_SECRET_` 环境映射。 | +| A.3 | Swarm 读 KV 身份 / RBAC | **⚠ 联调阻塞前置(分两步)**:① **Swarm 侧先 provision Pod 的 Workload Identity**(建 UAMI → federate 到 `orchestrator-sa` SA → 用 `client-id` 注解 SA)——当前**尚未建**(仓内无 `azure.workload.identity/client-id` 注解、无 IaC);② **HM/运维** 在其库 `heicode-kv`(`https://heicode-kv.vault.azure.net`)给该身份授 `Key Vault Secrets User`(只读、限 `swarm-model-key-*`)。`heicode-kv` 是 HM 侧库,故授权由 HM/运维做;Swarm 只提供 Pod 身份 id + 收窄作用域。不通则 `secret_ref` 解不出。生产 KV 适配器(`SECRET_RESOLVER`)在仓外经部署接线;dev/CI 用 `HEICODE_SECRET_` 环境映射。 | | A.4 | `OPENAI_API_BASE` | **Swarm 部署常量**(`AGENT_OPENAI_API_BASE`/`OPENAI_API_BASE`,设为 HM 网关 `https://code.xinghanlab.com/v1`),**不**经 create 的 `billing_context` 下发。 | | A.5 | 吊销信号 | **事件驱动(方案 A)**:`sk-` per-user 长存;`stop` 是唯一**终态**(`completed`/`failed` 可经 `POST …/input` 重开,故仍保留 key)。当某用户**所有 run 均被 stop**(retained 集清空)时,运行时发**恰好一次** `swarm.pool_terminated{user_id, secret_ref}`,HM 收到即吊销 `sk-` + 清 KV。单 run 的 `swarm.stopped` **不**触发吊销(key per-user 复用);账户停用 / HM 主动 delete 走同一 stop 路径。 | diff --git a/docs/integration/security-boundary.md b/docs/integration/security-boundary.md index 2b302df..c7ddc6b 100644 --- a/docs/integration/security-boundary.md +++ b/docs/integration/security-boundary.md @@ -61,7 +61,7 @@ - **每用户并发 Agent 配额**:一个 `user_id` 同时连接的 Agent 数上限为 `MAX_AGENTS_PER_USER`(env,默认 10)。注册(WS `register` 消息携带 `user_id`)超额即被拒绝(回 `registration_rejected` 并关闭,code 1008),断开后释放名额。归因主轴仍为 `user.id`/`channelId`。未带 `user_id` 的 Agent 为 unbound,不计入该配额。实现:`ConnectionManager.can_bind_user/bind_user/unbind` + 注册处强制;测试 `scripts/test-max-agents-per-user.py`。 - **Swarm 拉起 agent + 服务端解析 key(team 决议,runtime-contract §3.3)**:由 **Swarm 运行时**(`orchestrator/agent_launcher.py`)拉起专家 agent 池(拉起数 `min(池大小, MAX_AGENTS_PER_USER − 已连)`,与上面的注册兜底一致)。模型 key 由 **Swarm 从 `billing_context.secret_ref`(`azkv://`)服务端解析**后注入被拉起 agent 的 env——**不入** create 请求体 / 回调 / 日志 / argv(`command` 后端的密钥经进程 env 传入,不上命令行)。azkv 真实解析为部署侧 SecretResolver;dev/CI 用 `HEICODE_SECRET_`。解析不到即 keyless 启动并明确报错(不伪造)。KV secret value 为 JSON `{"openai_api_key":"sk-..."}`(HM #60;裸串兼容),解析字段 `openai_api_key`。 - **per-user key 吊销握手(HM #60,runtime-contract §3.3.1 A.5)**:`sk-` per-user 长存;`stop` 为唯一终态(completed/failed 经 `…/input` 可重开故保 key)。某用户全部 run 被 stop 时运行时发一次 `swarm.pool_terminated{user_id, secret_ref}`(HM 控制面事件,非客户端、非冻结集),HM 据此吊销 `sk-` + 清 KV,杜绝永不吊销 token。 -- 🟡 **待定(联调阻塞前置)**:Swarm 运行时读 `heicode-kv` 的身份/RBAC(Workload Identity / MI + `Key Vault Secrets User`)**归属未敲定**(运维 / Swarm 部署侧),须先确认——不通则 `secret_ref` 解不出。 +- 🟡 **联调阻塞前置(两步)**:Swarm 运行时读 `heicode-kv` 的身份/RBAC。① **Swarm 侧先 provision Pod 的 Workload Identity**(UAMI → federate 到 `orchestrator-sa` → `client-id` 注解 SA)——**当前未建**(仓内无 workload-identity 注解 / IaC);② **HM/运维** 在其库 `heicode-kv` 给该身份授 `Key Vault Secrets User`(只读、限 `swarm-model-key-*`)。`heicode-kv` 属 HM,故授权方为 HM/运维,Swarm 仅提供 Pod 身份 id。不通则 `secret_ref` 解不出。 - 🟡 待接入:多租户运行时隔离(命名空间/网络/配额)由 Agent 平台(AKS Workload Identity)承载,非本仓编排器;归因主轴为 `user.id`/`channelId`(见 `usage-billing-schema.md`),不引入 tenant 概念。 ## 7. 外部 API 与传输