fix(access): verify subscription readiness with real user keys

When subscription access is requested with an explicit access_api_key, assign the subscription to the real target user, bind that user's API key to the subscription group, and probe readiness with the same key instead of falling back to a managed synthetic user.

Update the runtime/reconcile flows, adapter tests, and source-of-truth docs so subscription_ready now reflects user-visible host access rather than managed-key-only closure success.
This commit is contained in:
phamnazage-jpg
2026-06-01 09:55:11 +08:00
parent 702ae19a61
commit c588a95c7d
11 changed files with 260 additions and 24 deletions

View File

@@ -8,6 +8,37 @@
- 当前主目录 `artifacts/real-host-acceptance/` 已只保留最终证据;历史调试样本已迁到 `artifacts/real-host-acceptance-archive/`
- access ready 语义已经收口为:`/v1/models` 命中 `smoke_test_model`,且最小 `POST /v1/chat/completions` smoke 成功;不会再出现 models-only 假 ready
- 2026-06-01 已继续收掉 `subscription_ready` 的最后一个真实闭环缺口:
- 根因不是 provider、不是前端也不是宿主随机波动而是 CRM 旧实现会在 subscription closure 里把目标用户替换成 synthetic managed user再用 managed key 做 probe
- 这样会出现“closure 返回 `subscription_ready`,但目标用户自己的 `GET /api/v1/subscriptions/active` 仍为空,`/v1/models` 仍然 `403 INSUFFICIENT_BALANCE`”的假阳性
- 最新本机真实验收已确认修复后的语义:
- 页面级 artifact`artifacts/provider-admin-matrix/1780271169_provider_admin_actions/99-summary.json`
- batch 明细:`GET /api/import-batches/5`
- `access_closure.DetailsJSON` 已切成 `effective_probe_key_source=requested_probe_api_key`
- 目标用户 `GET /api/v1/subscriptions/active` 已返回 active subscription
- 目标用户 `GET /api/v1/groups/available` 已出现 `OpenAI 中转默认分组-subscription`
- 目标用户自己的 `local-user-api-key-20260531` 直探 `/v1/models``/v1/chat/completions` 均已回到 `HTTP 200`
- 2026-06-01 已把 `self_service` 的真实前置条件正式写死:
- `self_service` 不会自动为目标用户充值
- 如果目标用户 key 有效但余额不足,宿主会直接返回 `INSUFFICIENT_BALANCE`
- 所以 `self_service_ready` 的真实验收前提仍然必须包含“用户余额已满足最小调用成本”
- 2026-05-31 已继续把 `providers.html` 的页面内显式动作收口成独立 acceptance 入口:
- 新增脚本:`scripts/acceptance/verify_provider_admin_actions.sh`
- 覆盖范围:
- `GET /api/packs`
- `GET /api/hosts`
- `GET /api/packs/{pack_id}/providers`
- `POST /api/providers/{provider_id}/preview-import`
- `POST /api/providers/{provider_id}/import`
- `POST /api/provider-drafts`
- `PUT /api/provider-drafts/{draft_id}`
- `DELETE /api/provider-drafts/{draft_id}`
- `POST /api/provider-drafts/{draft_id}/publish`
- 同轮已补本地伪远端回归:
- `scripts/test/test_real_host_scripts.sh`
- 当前结论:
- `providers.html` 不再只有动作级审计矩阵,页面内显式动作已经有可重复执行的 acceptance 入口
- `rollback / reconcile / status / import-batches` 仍不属于这页当前显式 UI 能力
- `subscription` 主链路已通过 latest fresh-host 复验:
- MiniMax 53hk`artifacts/real-host-acceptance/20260521_191418_remote43_minimax_key_import/21-summary.json`
- DeepSeek 2166`artifacts/real-host-acceptance/20260521_201509_remote43_deepseek_key_import/21-summary.json`

View File

@@ -57,8 +57,9 @@
- 必须同时通过 `/v1/models``/v1/chat/completions`
4. `subscription``self_service` 的 probe key 语义不同
- `subscription` 最终 probe key 是宿主 managed key
- `self_service` 最终 probe key 是普通用户 gateway key
- `subscription` 在显式提供 `access_api_key` 时,最终 probe key 也必须是**目标用户自己的 gateway key**
- 只有在没有显式 `access_api_key` 的 fallback/运维场景,`subscription` 才允许走宿主 managed key
5. `self_service` 普通用户 key 的真实认证方式是 `Authorization: Bearer`
- 不是 `x-api-key`
@@ -170,6 +171,7 @@
2. 普通用户 key 可用
3. key 已绑定目标标准 group
4. 用户有可用余额
5. relay-manager 不会替用户自动充值;如果余额不足,宿主会直接返回 `INSUFFICIENT_BALANCE`
`subscription`
@@ -178,6 +180,7 @@
3. 目标 group 是 `subscription` 类型
4. 用户有 active subscription
5. key 已绑定该 subscription group
6. 如果本轮导入显式传了 `access_api_key`closure 与 completion smoke 必须用这把**真实用户 key** 验证,而不是 synthetic managed key
### 3.2 导入时最容易漏掉的三件事

View File

@@ -160,12 +160,18 @@
- `billing_model_source=channel_mapped`
2. subscription 场景的 gateway probe 语义必须保持:
- 最终 probe key 是宿主 managed key
- 不是外部原始 `access_api_key`
- closure artifact 必须把“请求传入的 key”和“实际探测使用的 key”分开表达
- `requested_probe_api_key` = 调用方传入原始 key
- `effective_probe_key_source=managed_subscription` = 实际 gateway probe 走宿主 managed key
- `probe_api_key` 仅继续保留给 `self_service` 向后兼容,不再用于 `subscription`
- 当请求显式提供 `requested_probe_api_key` 时:
- 最终 probe key 必须是**目标用户自己的 gateway key**
- 不能再用宿主 synthetic managed key 把 closure 伪装成 `ready`
- closure artifact 必须落 `requested_probe_api_key`
- `effective_probe_key_source=requested_probe_api_key`
- 仅当请求**没有**显式提供 `requested_probe_api_key` 时,才允许 fallback 到宿主 managed subscription key
- `effective_probe_key_source=managed_subscription`
- `probe_api_key` 仅继续保留给 `self_service` 向后兼容,不再用于 `subscription`
- 对于真实用户 subscription ready最终必须同时满足
- `GET /api/v1/subscriptions/active` 非空
- `GET /api/v1/groups/available` 可见目标 subscription group
- 目标用户自己的 key 直探 `/v1/models``/v1/chat/completions``HTTP 200`
3. 任何 live 结论都必须先确认:
- 在线 CRM 进程启动时间
@@ -208,6 +214,6 @@
- ❌ 把历史 review/task board 当当前 gate
- ❌ 把历史 PASS artifact 当当前 latest-head 真相
- ❌ 把 `/v1/models` 通过当成 completion 已通过
-把 subscription 场景原始 `access_api_key` 当成最终 probe key
- ❌ 把 `subscription` closure 里的 `requested_probe_api_key` 误读成实际 gateway probe key
-`requested_probe_api_key` 已提供时,仍然用 managed synthetic key 把 `subscription_ready` 判真
- ❌ 把旧 artifact 里的 `managed_subscription` 语义继续当成最新实现
- ❌ 把 harness 参数错误(`PACK_PATH`、容器目标、probe auth当成产品源码失败