Files
qinglong/docs/adr/ADR-0265-bounded-one-shot-model-provider-credential-test-connection.md
T

120 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0265:有预算的一次性 Model Provider Credential Test Connection
- 状态:Accepted
- 日期:2026-08-03
- 关联:RFC D-08、D-12、D-159、D-175ADR-0169、ADR-0263、ADR-0264
## 背景
Provider credential 的 bind/revoke ceremony 已有独立 manager,但“测试连接”若直接放进
manager,会迫使常驻管理进程同时持有 Provider Secret 与任意外网访问能力;若只由客户端传 URL、
deadline 或 retry,又会成为 SSRF、费用放大和绕过部署网络策略的入口。低配路由设备不应为一个
偶发操作长期支付 listener、连接池、timer 或 AI provider 依赖;Cluster 则需要 durable quota、
崩溃后不重复请求 provider、同步复制和晋升后的明确结果。
## 决策
### 1. Manager 只签发计划,不接触 Secret 或 Provider 网络
公开操作固定为 `provider-credential.test.plan`。请求只接受 `requestId/testId/projectId/provider`
principal 由 transport 注入;endpoint、adapter、deadline、response/model/cost/retry 预算全部从
server-owned exact allowlist 解析。manager 要求五分钟内的 strong User、`secret.manage` 和当前
Project/RoleBinding fence,在一个 SERIALIZABLE transaction 内消费 durable quota、写 immutable plan
和 allowed SecurityAudit。相同业务 identity 与相同 lifetime 的晚到 API retry 返回原始 plan,不重复
消费 quota;调用方不能提交 URL、SecretRef、deadline、budget 或 retry。
allowlist 是不含 Secret 的 ConfigMap,单文件不超过 64 KiB、最多 16 个 provider。endpoint 只允许
canonical HTTPSdeadline 115 秒、response 1256 KiB、models 1256、费用固定为零、retry 固定为零;
plan 最长存活五分钟。manager Deployment 继续不挂 Provider Secretbase NetworkPolicy 继续没有
Provider egress。
### 2. Provider authority 只存在于 caller-driven one-shot tester
新增能力仍留在既有 `@qinglong/ai``@qinglong/cluster-admin`
`@qinglong/cluster-postgres` 的显式 subpath,不新增 workspace package。tester 只接受 exact
`executionId/testId/allowlist` command,使用专用 `ql3_ai_credential_tester` PostgreSQL role 和
`maxConnections=1`
1. 先提交 immutable execution intent
2. 只有 `status=created` 才在同一个 one-shot Pod 内执行不带 credential 的 CNI 收敛屏障:exact
provider TCP 必须可达,同时 deny-canaryCluster 使用 Kubernetes API Service)必须不可达;两者共用
原始 deadline,不重试 Provider API
3. 屏障成立后才逐请求解析 current binding、读取一个 Project-bound projected Secret,并在 Provider
请求前提交 content-free credential-use audit
4. 对计划中的 `/models` 只执行一次 GET
5. 只持久化 `reachable|unreachable`、model count、duration 与 digest,不保存 endpoint response、
SecretRef、token/header 或 raw error
6. completion 暂时不可用时只重试同一个结果写入一次,绝不重打 provider。
已有 intent 但无 result 表示 `outcome_unknown`,必须停止且不得网络重放;已有 result 返回 exact
existing。deadline 使用 AbortSignal 和 monotonic clock 双围栏;超时、provider error、响应超限或
model 超限统一保存 content-free `unreachable`
### 3. PostgreSQL 与部署权限分离
`pg-9015` 新增 plan/quota/execution/result 四张表。manager 只能写 plan/quota/allowed audittester
只能读 plan/current binding、append execution/result/use audittester 不能读写 quota、SecurityAudit、
Prompt output、invocation、price、Package、Worker、admin、maintenance 或 migration authority,也没有
`ql3` schema USAGE。readiness 对拒绝权限使用 `pg_catalog` OID 检查,不能为让自检通过而扩权。
Kubernetes tester 是 opt-in、caller-driven Job`backoffLimit=0``activeDeadlineSeconds=60`
`ttlSecondsAfterFinished=300`、tokenless ServiceAccount、non-root/read-only/drop ALLrequest 为
25m CPU/48 MiB、limit 为 500m/192 MiB。base 只有 DNS egress,因此故意不能访问 provider
CloudNativePG overlay 只增加 tester role 的 PostgreSQL 5432,部署者再以私有 overlay 为 exact private
provider `/32:443` 放行。标准 NetworkPolicy 不能表达 FQDN,故应用 exact HTTPS allowlist 与部署 CIDR
必须同时成立;公网 SaaS 需要 CNI FQDN policy 或受审 egress proxy,不能静默放宽 `0.0.0.0/0`
### 4. Edge/Standalone 保持零常驻成本
本 ADR 不把 Cluster manager/tester、`pg`、Kubernetes client、listener 或 pool 带入 Edge/Standalone。
低配设备若后续需要同等产品入口,应复用短生命周期本机 Owner/SQLite ceremony 和 AI feature gate
不得为了 API 对称启动常驻 daemon。Cluster 可由 Job 横向处理不同请求,但每个 test 仍是唯一 intent、
零 provider retry;不能把副本数变成隐式 provider 重试。
## 被否决方案
1. **让 manager 直接测试**:合并身份管理、Secret read 和任意 provider egress 三种 authority。
2. **客户端直接传 URL/预算**:打开 SSRF、长连接、响应放大和费用绕过。
3. **已有 intent 自动重打 provider**:COMMIT 响应丢失时会产生重复外部副作用和费用。
4. **按 provider 建 workspace package**:只有少量协议/adapter 文件,没有新的独立交付或依赖反转价值。
5. **base NetworkPolicy 放行公网 443**:无法证明流量只去 allowlist hostname。
6. **给 tester `ql3` schema USAGE 以通过 readiness**:扩大实际 authority,并掩盖拒绝权限探针缺陷。
## 当前验证
- AI 全量 176 项:173 通过、3 条条件跳过、0 失败;Cluster Admin 全量 242 项:240 通过、
2 条条件跳过、0 失败;plan/execution PostgreSQL 定向 9/9
- manager service/transport/client/process、executor/process/CLI、Kustomize base/CNPG、manager/tester/
CloudNativePG/dependency audit 均通过;workspace 仍为 19,没有为 test connection 新增 package
- PostgreSQL 18.4 arm64 physical HA 实际使用 `ql3_ai_credential_tester` 单连接池,证明 plan/execution
exact replay、provider 调用恰好一次、Secret material 清零一次、completion COMMIT 已提交但响应
丢失后以同结果收敛、四类 durable fact 无 Secret、`remote_apply` 同步 WAL、timeline 1→2 晋升后
facts/readiness 完全一致;新增五个具体 gate 和总 `gates.passed=true`
- HA 运行后 `ql3-ha-*` 容器、网络、卷零残留,既有
`ql3-cnpg-evidence-control-plane` 容器保持运行且未被操作。
- 显式 opt-in 的真实三节点 K3s/arm64 纵切面使用锁定的 K3s `v1.34.3+k3s1`、CloudNativePG
`1.30.0` 与 PostgreSQL `18.4`,三个 PostgreSQL 实例分别预绑定到两个 worker 和唯一 control-plane
初始主库固定在 worker A,停止该 worker 后 primary 成功切换,worker 恢复后数据库回到 3/3 Ready
唯一 control-plane 始终在线;
- 纵切面真实执行 8 个 one-shot Job7 个 execution、1 个 exact replay),证明 projected Secret
material generation 1→2、旧 material 拒绝、新 Pod 重解析、Provider Pod/CIDR 轮换、旧 CIDR
fail-close、exact private `/32:8443`、DNS/CloudNativePG allow 与 Kubernetes API/公网 deny。CNI 在新
Pod 创建后存在短暂策略编程窗口,因此 tester 现在必须在 Secret/binding/audit 前同时证明 Provider
allow 与 Kubernetes API deny,不能以另一个预检 Pod 代替;
- 最终 content-free 报告为 `gates.passed=true`7 plan/execution/result、7 plan audit、5 次实际到达
credential-use 阶段的 use audit、4 reachable、3 unreachable、5 次 Provider `/models` 请求、零 replay
duplicate,并在 CloudNativePG failover 后完整存活;报告审计和部署审计均无 finding;
- Docker K3s 的本地 `imagePullPolicy: Never` 镜像会在节点重启或 containerd GC 后消失,测试夹具在
节点恢复后重新导入 exact 本地 admin image,再执行全节点收敛。这是离线测试制品恢复,不是生产
Provider 或数据库重试。成功运行后随机 K3s 容器、网络、卷和测试镜像零残留,受保护的既有
evidence control-plane 未被操作。
## 剩余生产证据与边界
本 ADR 的私有 Provider test-connection 接受门已经关闭,但该 Docker 夹具使用 privileged K3s、
fixture-only prebound hostPath、单个 Docker host 和唯一 Kubernetes control-plane;它不能冒充动态 CSI、
基础设施 STONITH、control-plane HA 或跨故障域证据。确定性私有 HTTPS Provider 也不是外部 SaaS。
公网 SaaS 仍必须选择并验证 CNI FQDN policy 或 egress proxy,不能复用私网 `/32` 结论;真实外部
OIDC/mTLS、KMS/Vault/HSM custody、首次 provision/active rotation/lost-secret recovery 继续由后续
ADR 和发布门负责。