mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 03:18:09 +08:00
156 lines
8.2 KiB
Markdown
156 lines
8.2 KiB
Markdown
# ADR-0174:授权型 Model Price Catalog 管理 facade
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-07-27
|
||
- 关联:RFC D-49、D-73、D-84、D-85、D-156、D-162、D-163、D-164;ADR-0142、ADR-0143、ADR-0144、ADR-0145、ADR-0173
|
||
|
||
## 背景
|
||
|
||
ADR-0173 已把价格目录冻结为不可变 publication 和 append-only generation head,
|
||
但低层 repository 仍要求调用方直接填写 `publishedByUserId`/
|
||
`changedByUserId`。这足以证明数据来源字段,却不足以证明:
|
||
|
||
1. actor 是仍有效且最近完成强认证的稳定 User;
|
||
2. 一个受信平台 Policy 确实允许本次全局价格配置 mutation;
|
||
3. cluster 的发布者没有自行激活自己的价格;
|
||
4. rate-limit/quota 使用稳定幂等键;
|
||
5. 授权事实和 publication/head 在崩溃、并发与 COMMIT response loss 下同成同败。
|
||
|
||
价格目录是跨 Project 的基础设施配置。把它绑定到任意 Project 的
|
||
`project.manage` 会制造虚假的租户边界;把它放进 provider credential authority
|
||
又会把 Secret 生命周期与计价权威合并。
|
||
|
||
同时,`local-admin` 是默认本机产品闭包的一部分,`cluster-admin` 还承载通用集群
|
||
管理任务。让这两个包静态依赖可选 `@qinglong/ai`,会使禁用 AI 的路由设备和管理
|
||
镜像仍安装 AI,违反 D-156 的零成本关闭语义。
|
||
|
||
## 决策
|
||
|
||
### 1. 管理服务留在现有 AI 能力边界
|
||
|
||
新增 `@qinglong/ai/price-catalog-management`,不新增 workspace package、生产依赖、
|
||
进程、timer、watcher、缓存或数据库连接。它只组合:
|
||
|
||
- `ModelPriceCatalogAuthorizedAdministrationRepository`;
|
||
- 显式注入的 platform-level authorizer;
|
||
- 可选、调用方驱动且以 authorization ID 幂等的 quota port;
|
||
- profile 决策模式。
|
||
|
||
通用 `local-admin`/`cluster-admin` 不反向依赖 AI。后续产品 transport 必须只在 AI
|
||
显式启用的短生命周期 composition root 中惰性装配现有认证、私有 command-file 或
|
||
HTTPS transport port。
|
||
|
||
现有 `@qinglong/ai/profile` 同时提供管理 capability 的禁用优先 composition root,
|
||
不再拆分新包。禁用态不调用 authority loader;edge/standalone 固定
|
||
`human_confirmation`,cluster 固定 `separation_of_duty` 且缺 quota authority 时
|
||
启动失败关闭。停止时先撤销新 mutation admission,再等待在途 mutation drain,最后
|
||
释放 authority。
|
||
|
||
### 2. 身份与 Policy
|
||
|
||
管理 mutation 只接受稳定 `User`,assurance 必须是 `local_console`、
|
||
`multi_factor` 或 `hardware`,且数据库观察提交时距认证不超过五分钟并早于
|
||
principal expiry。User ID 从 principal 派生,HTTP/CLI body 不得覆盖。
|
||
|
||
authorizer 返回 domain-separated、digest-bound 的 allow/deny decision,固定绑定
|
||
platform policy revision 与有界 reasons。该 Policy 是平台级 authority,不伪装为
|
||
某个 Project 的角色绑定。
|
||
|
||
### 3. Profile 决策模式
|
||
|
||
- edge/standalone 使用 `human_confirmation`,允许同一强认证 Owner/User 发布并在
|
||
独立 mutation 中激活;
|
||
- cluster 使用 `separation_of_duty`。`activate` 的 User 必须不同于目标
|
||
publication 的 publisher;
|
||
- `deactivate` 和 `revoke` 是收敛风险的操作,不要求与发布者不同,但仍必须通过强
|
||
认证、Policy、quota 与 expected-generation/head fence。
|
||
|
||
职责分离在 service 预检查一次,并在 repository transaction 内从不可变 publication
|
||
authorization 再检查一次,不能由自定义调用方绕过。
|
||
|
||
### 4. 9006 授权事实
|
||
|
||
不修改 9001–9005。独立双方言 migration:
|
||
|
||
- `9006-ai-model-price-catalog-authorizations`;
|
||
- `pg-9006-ai-model-price-catalog-authorizations`。
|
||
|
||
新增单张 append-only authorization 表。每条事实固定绑定:
|
||
|
||
- authorization/request ID 和操作;
|
||
- provider/model/nullable revision;
|
||
- catalog command digest;
|
||
- publication digest 或 head digest(二选一外键);
|
||
- User、authentication ID/assurance/lifetime;
|
||
- Policy revision/decision digest/reasons;
|
||
- decision mode、数据库提交时间和 authorization digest。
|
||
|
||
`publishAuthorized`/`transitionAuthorized` 必须在原 publication/head 事务内插入授权
|
||
事实。catalog command digest、result digest、authorization command digest 均唯一;
|
||
精确重放返回同一事实,任一 ID 或语义漂移冲突。已有低层 raw mutation 如果没有
|
||
authorization,不得由新入口事后补写“授权”;旧 publication 若需启用,必须使用新
|
||
revision 经正式 ceremony 重新发布。激活还要求目标 publication 已有 publish
|
||
authorization。
|
||
|
||
PostgreSQL `ql3_admin` 只获得授权表 `SELECT/INSERT`;runtime、PUBLIC 与其它业务
|
||
角色无权,任何角色都没有 `UPDATE/DELETE`。runtime 解析价格仍只读取 9005 的 active
|
||
publication/head,不加载授权 JSON。
|
||
|
||
### 5. 失败语义
|
||
|
||
- 弱、过期或非 User principal:authentication error;
|
||
- Policy deny:authorization error;
|
||
- authorizer/quota 不可用:unavailable,且目录 mutation 为零;
|
||
- quota 超额:保留有界 retry-after;
|
||
- 同人 cluster 激活:separation-of-duty error;
|
||
- raw publication、缺失授权事实、stale generation/head、摘要漂移或并发输家:
|
||
conflict;
|
||
- 数据库在实际提交前再次验证 principal lifetime,进程时钟的预检查不能替代数据库
|
||
事实。
|
||
|
||
认证失败、Policy deny、quota deny 的 transport-level 审计仍由后续产品入口完成;
|
||
本 ADR 只把成功授权事实与业务 mutation 原子化,不能据此宣称 HTTP/CLI/UI 已开放。
|
||
|
||
## 拒绝方案
|
||
|
||
1. **把价格目录当成某个 Project 的配置**:共享价格会被虚假租户 fence 控制,拒绝。
|
||
2. **只信任 body 中的 User ID**:不能证明认证与 Policy,拒绝。
|
||
3. **先写通用 security audit,再写 catalog**:两个事务留下审计与 mutation 裂缝,
|
||
拒绝。
|
||
4. **在 9005 原表加列**:破坏已发布 checksum 和 migration 历史,拒绝。
|
||
5. **允许 raw publication 后补 authorization**:会把事后批准伪装成原子授权,拒绝。
|
||
6. **cluster 发布者自行激活**:价格配置缺少独立复核,拒绝。
|
||
7. **让 local-admin/cluster-admin 静态依赖 AI**:污染禁用闭包和低资源部署,拒绝。
|
||
8. **为管理 facade 新拆 package/service**:没有独立部署收益并增加运维成本,拒绝。
|
||
|
||
## 当前证据
|
||
|
||
- AI 全量 89 项:87 pass,2 条真实 PostgreSQL 条件 skip;新增 Profile 测试证明
|
||
edge 禁用零 loader、standalone 惰性人工确认装配、cluster 缺 quota 失败关闭,以及
|
||
cluster 固定职责分离;
|
||
- SQLite 已覆盖原子 publication/head/authorization、exact replay、authorization ID
|
||
漂移、raw publication 拒绝、双层职责分离、FK 与 rollback;
|
||
- PostgreSQL 18 migration/admin/runtime 三角色专项测试覆盖 authorized publish、
|
||
exact replay、双人 activation、授权读取、runtime deny 与 admin append-only ACL;
|
||
- 9001–9005 checksum 未变;9006 SQLite/PostgreSQL checksum 分别为
|
||
`3ee48d1468569c9dc1fa9f04031a48a220161762d48eeac4cd924e2dcd7abd21` 和
|
||
`486d46115e28e90604a47231fe95e3b1687649c063d93bf7ce267783f2a7165f`;
|
||
- 真实 PostgreSQL 专项目录测试 2/2 pass,Model Invocation 串行门另 1/1 pass。
|
||
- PostgreSQL 18.4 arm64 physical HA 门已证明 9001–9006 history/checksum、11 张
|
||
`ql3_ai` 表及 9006 runtime deny/admin append-only/其它角色 deny ACL 在 timeline
|
||
1→2 前后完全一致,`optionalAiFeatureSchemaSurvivesPromotion=true` 且总门通过;
|
||
- 22-importer dependency audit 覆盖 AI 20 个源码文件且 `findings=[]`,通用
|
||
local-admin/cluster-admin 仍不依赖 AI;
|
||
- disabled benchmark 在 edge/standalone/cluster 的 storage/provider/management
|
||
authority loader 均为 0,管理禁用启动最大 0.265 ms,模块加载 RSS 增量 425,984
|
||
bytes;默认 edge/standalone 产物仍为 4 MB 以下、478 files 且不含 AI,显式
|
||
edge-ai/standalone-ai 为约 4.54 MB、520 files,均通过各自门限。
|
||
|
||
## 后续门禁
|
||
|
||
1. 本机私有 command-file transport、local-console authentication 与失败审计;
|
||
2. Cluster TLS/identity assertion transport、平台 Policy repository、耐久 quota 与
|
||
deny/unavailable 审计;
|
||
3. transport body 中不得接受 principal/Policy/result digest;
|
||
4. COMMIT response loss 注入覆盖 authorization 与 catalog result 的精确收敛。
|