8.2 KiB
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。这足以证明数据来源字段,却不足以证明:
- actor 是仍有效且最近完成强认证的稳定 User;
- 一个受信平台 Policy 确实允许本次全局价格配置 mutation;
- cluster 的发布者没有自行激活自己的价格;
- rate-limit/quota 使用稳定幂等键;
- 授权事实和 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 已开放。
拒绝方案
- 把价格目录当成某个 Project 的配置:共享价格会被虚假租户 fence 控制,拒绝。
- 只信任 body 中的 User ID:不能证明认证与 Policy,拒绝。
- 先写通用 security audit,再写 catalog:两个事务留下审计与 mutation 裂缝, 拒绝。
- 在 9005 原表加列:破坏已发布 checksum 和 migration 历史,拒绝。
- 允许 raw publication 后补 authorization:会把事后批准伪装成原子授权,拒绝。
- cluster 发布者自行激活:价格配置缺少独立复核,拒绝。
- 让 local-admin/cluster-admin 静态依赖 AI:污染禁用闭包和低资源部署,拒绝。
- 为管理 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,均通过各自门限。
后续门禁
- 本机私有 command-file transport、local-console authentication 与失败审计;
- Cluster TLS/identity assertion transport、平台 Policy repository、耐久 quota 与 deny/unavailable 审计;
- transport body 中不得接受 principal/Policy/result digest;
- COMMIT response loss 注入覆盖 authorization 与 catalog result 的精确收敛。