Files
qinglong/docs/adr/ADR-0263-explicit-cluster-ai-composition-and-provider-credential-authority.md
T

7.9 KiB
Raw Blame History

ADR-0263:显式 Cluster AI 组合与 Provider Credential Authority

  • 状态:Accepted
  • 日期:2026-08-02
  • 关联:RFC D-08、D-12、D-85、D-124、D-156、D-159、D-244ADR-0167、ADR-0169、ADR-0261、ADR-0262

背景

ADR-0169 已定义 Project-bound Provider credential、可清零 Secret material 和 audit-before-network,但当时还没有 Cluster 产品组合:binding 与 audit 没有 PostgreSQL authoritySecret material 没有可部署的 Cluster adapter,默认 Cluster 镜像也不能安全地 选择性包含 AI。

QingLong 3.0 同时服务低配路由设备和多副本 Cluster,因此不能通过以下方式补齐能力:

  • 让默认 control image、Edge 或 Standalone 常驻安装/加载 AI
  • 为 binding、Secret adapter、composition 或 CLI 各拆一个单文件 workspace package
  • 让常驻 control 持有 Kubernetes API、Secret list/watch 或 credential mutation 权限;
  • 把静态环境变量 token、ConfigMap 明文或进程内 cache 当作 credential authority
  • 只发布一个内容随构建参数变化、却共享同一镜像身份的 Cluster Control artifact。

决策

1. Credential catalog 与 audit 使用 append-only PostgreSQL authority

在既有 @qinglong/ai 包内新增显式 catalog/storage subpath,不新增 workspace package。 pg-9012-ai-model-provider-credential-catalogql3_ai schema 追加:

  • immutable binding revision
  • generation/CAS 驱动的 bind|revoke transition
  • content-free credential use audit。

runtime 只取得 current binding read 与 audit appendbinding mutation 只属于既有 ql3_ai_maintenance authority。exact mutation 可重放,generation、command 或 binding identity 漂移必须冲突。audit 只保存 Project、Provider、request、operation、binding revision/digest 和数据库时间,不保存 SecretRef、token、header、Prompt 或输出。

2. Cluster Secret material 使用只读 projected-file adapter

首个 Cluster material adapter 接受一个部署注入的绝对只读目录。文件名是 canonical SecretRef 的 lowercase SHA-256;每次调用重新解析并读取一个有界 regular file

  • 允许 Kubernetes atomic-writer symlink,但最终文件必须仍位于受信 root 内;
  • 拒绝可写、可执行、other-readable、hard-link、路径/identity/size 漂移;
  • material 以 owned bytes 返回并由 authorization lease 清零;
  • 不使用 Kubernetes client、ServiceAccount token、list/watch、cache、timer 或后台进程。

该 adapter 可以消费 Kubernetes Secret、CSI 或 Secret operator projection,但不把这些 projection 冒充最终 KMS/HSM。轮换由外部 authority 原子替换 projection,下一次调用自然 读取新 material。

3. Cluster AI 必须是显式 composition root

AI 产品组合留在 @qinglong/cluster-control/ai-production,复用既有认证、Project Policy、 route registry、生命周期和 PostgreSQL runtime contract。只有独立 AI entrypoint 解析 QL3_CLUSTER_AI_ENABLED=true 及 AI 配置,并按以下顺序 fail closed

  1. 校验 projected Secret root 与可选 Prompt output keyring
  2. 建立独立、有上限的 PostgreSQL runtime pool
  3. 装配 credential reader/audit、Secret material 与 projected Provider authority
  4. 执行有界 Prompt recovery
  5. 将 Prompt execution capability 注入既有产品 route
  6. 再启动 Cluster Control,并在任一侧不可用时统一有界停止。

默认并发为 4、recovery page 为 32、AI 数据库连接为 4,且都有硬上限。该增量只开放 已经受审的 Prompt executionPrompt output read、credential management 和通用裸 model gateway route 仍保持关闭。

4. 默认与 AI 镜像是两个独立供应链 artifact

同一 Dockerfile 提供两个显式 target

  • runtime / qinglong3-cluster-control:只含 runtime-core、cluster-postgres、 cluster-control,使用干净输出目录编译并断言不存在 AI package、AI composition 和 AI CLI
  • runtime-ai / qinglong3-cluster-control-ai:额外包含 @qinglong/ai 和独立 AI CLI。

CI、OCI layout、SBOM、OS vulnerability scan、签名/证明和 release matrix 均把 control-ai 作为第四个独立 artifact,与 control、admin、local 分别 pin digest。不能用 同一 tag 的隐藏 build arg 改变依赖闭包。

5. Kubernetes Cluster AI 是 opt-in component

默认 ql3-cluster base 保持 AI-free。components/cluster-ai 只在显式 overlay 中:

  • 将 control image 替换为独立 AI image
  • 注入 canonical Provider authority ConfigMap
  • 0440 只读 Secret volume 提供 digest-named material
  • 保持 automountServiceAccountToken: false,不增加 Kubernetes RBAC/API egress
  • 要求私有 overlay 独立 pin AI image digest。

6. Package 边界不因该能力继续细拆

workspace 保持 19 个 package。catalog、repository、projection adapter、composition 与 CLI 分别进入已经承担对应依赖/权限/部署责任的 ql3-aiql3-cluster-control subpath。 源码文件数量只能触发复审,不能单独决定 package;新增 package 仍必须满足 RFC D-85 的 部署、权限、依赖闭包、多个 production consumer 或独立供应链责任门。

当前唯一单源 package @qinglong/local-command-file 有三个 production importer,并提供 共同的 POSIX private-file/TOCTOU 安全协议,因此暂时保留;如果 consumer 收敛为单一同包 入口,或不再形成共享安全边界,则必须回并为现有 subpath。

被否决方案

  1. 默认 control image 直接安装 AI:让禁用 AI 的节点承担依赖、漏洞与演进成本。
  2. 每个 AI adapter 一个 package:没有独立部署或权限收益,重复单文件包问题。
  3. 环境变量保存 Provider token:无法形成 Project-bound revision、轮换和 durable audit。
  4. 常驻 control 读取 Kubernetes Secret API:扩大 ServiceAccount、RBAC、网络和缓存边界。
  5. 一个镜像 tag 通过环境变量切换 AI:无法由 SBOM、签名和 digest 证明实际闭包。
  6. 把 projection 宣称为 KMS:它只解决 Pod 内只读 material delivery,不解决外部 key custody、wrapping、灾备或 lost-secret ceremony。

验证

  • @qinglong/cluster-control:175 项中 173 通过、2 条外部条件跳过、0 失败;
  • @qinglong/ai:154 项中 151 通过、3 条外部 PostgreSQL 条件跳过、0 失败;
  • Cluster AI composition 独立测试 3/3deployment audit 40/40
  • release/SBOM/OCI/OS vulnerability policy 合并测试 117/117
  • frozen lockfile install、Cluster dependency audit、deployment audit 和 release audit 全绿;
  • 实际 Docker runtime 不含 AI package/CLI/compositionruntime-ai 三者均存在且 AI CLI 可执行;实际 control-ai inventory 为 47 components,其中 4 个 internal、43 个 external
  • PostgreSQL 18.4 arm64 physical-streaming HA 完成 remote_apply、timeline 1→2、旧主 fencing、pg_rewind、双 fresh controlbinding bind/revoke/rebind 与 content-free audit 在晋升后保持一致,总 gate passed=true
  • 所有 ql3-ha-* 临时资源已清理,既有 evidence control-plane 未被修改;
  • workspace 仍为 19 个 packagedependency findings 为空。

后续门禁

  1. 为 binding bind/revoke、测试连接和 audit query 增加经过认证、Policy 与 quota 约束的 Cluster 管理 ceremony;常驻 runtime 不取得 mutation authority
  2. 引入真正的 KMS/Vault/HSM custody adapter、首次 provision、active rotation、备份恢复与 lost-secret 人工流程;
  3. 在真实 Kubernetes API/CSI/Secret operator 上完成投影轮换、Pod rollout、网络隔离和 credential 丢失故障注入;
  4. 补齐真实 Provider 的计费、限流、超时和故障注入后,再评审更多 HTTP/MCP/UI route
  5. 持续以镜像字节、文件数、loaded modules、RSS、连接数和冷启动验证低配与 Cluster 两端预算,不能用 package 数替代运行成本证据。