feat(gateway): P0 凭证池稳定身份与统一快照 - #849
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
✅ 此 PR 已关联 issue,之前的提醒已自动标记为已解决。 |
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #849 +/- ##
==========================================
- Coverage 91.27% 91.16% -0.11%
==========================================
Files 193 195 +2
Lines 13204 13391 +187
==========================================
+ Hits 12052 12208 +156
- Misses 1152 1183 +31
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
本次审阅覆盖凭证池 ORM/导入、路由物化、Chat/Image/Video 网关以及 i2v Admit 的完整固定 SHA diff。当前实现仍有几处会导致数据库池配置不生效、请求错误地复用首个凭证、并发配额失效或泄露密钥的问题,建议修复后再合并。由于环境中未安装 uv/pytest,验证采用固定范围 diff、调用链检查与 compileall;git diff --check 通过。
Additional findings
backend/packages/app/src/windup_app/server/mq/i2v_admit.py:?: [P1] Enforce account and credential inflight limits: The newRoutableEdgecarriesaccount_inflight_maxandcredential_inflight_max, and account-scoped edges even expose a shared account Redis key, but acquisition still calls the globalinflight_max()and increments onlyedge.redis_inflight_key(). Thus a configured credential limit is ignored, and multiple credentials sharing one account can each admit up to the global limit instead of enforcing the account limit. This can exceed upstream capacity and defeats the quota fields introduced by the pool tables; acquisition needs to use the edge limit and, for account scope, atomically enforce the shared account set as well.backend/scripts/import_gateway_pool_from_env.py:?: [P1] Do not persist imported keys as plaintext: The importer writes the raw environment secret intoapi_key_ciphertextwith only aplaintext:prefix. The model contract explicitly says this column stores ciphertext and that keys must be decrypted only in memory; the prefix does not encrypt anything, so a database read exposes every upstream API key. Use the repository's encryption mechanism (and provide the matching decryptor to the runtime loader) rather than storing the secret verbatim.backend/packages/framework/src/windup_framework/gateway/pool_registry.py:?: [P1] Preserve the adapter for each stable credential:snapshot_from_settingsnow emits stable IDs such asprimary:<hash>, while the builders below still populate_route_adaptersusing the legacyprimary.key{i}IDs.lookup_adaptertherefore misses the per-key entry for every stable route and falls through to the default adapter, so a multi-key deployment sends all attempts through the first API key (and DB routes have the same problem). Build adapters keyed by the same stable credential IDs, or construct the adapter from the selected route instead of silently falling back.
There was a problem hiding this comment.
The review covers the new credential identity/pool registry, all gateway consumers, i2v admission migration, and the import/schema tooling. Three integration/security issues need correction before the database-backed pool can safely replace settings-based routing. Python compilation succeeds; the PR's advertised pytest command could not run here because uv and pytest are unavailable.
Additional findings
backend/scripts/import_gateway_pool_from_env.py:?: [P1] Do not persist API keys as plaintext ciphertext: The importer writesplaintext:{route.api_key}intoapi_key_ciphertext, exposing the full upstream secret in the database despite the model's contract that only ciphertext is stored. This is also not a usable encryption format for thedecrypt_api_keycallback expected bysnapshot_from_rows(). A dry-run is safe, but any real import permanently stores everyAI_ROUTE_*key in cleartext; use the repository's encryption mechanism (or fail closed when no encryption key/provider is configured) before inserting credentials.backend/packages/framework/src/windup_framework/gateway/pool_registry.py:?: [P1] Keep factory adapter keys aligned with stable route IDs: Database/settings snapshots now materialize each route withroute_id == credential_id(for example, the added assignment insnapshot_from_rows()), butbuild_chat_gateway(),build_image_gateway(), andbuild_video_gateway()still constructroute_adaptersusingroutes_from_settings(), whose keys areprimary.key0,primary.key1, etc. In the normal factory path,_adapter_for()therefore misses every stable credential key and falls back toself._adapter(the first adapter), so retries/fallbacks can send all credentials through the first API key. Build the adapter map frompool_routes()/stable IDs or make lookup explicitly map the legacy keys.
Admit 与 Gateway 共用稳定 credential_id,把 1024XEngineer#842 车道从 primary.key{i} 迁到物理钥匙,避免改 CSV 顺序把在途和冷却戴到另一把 key 上。 Part of 1024XEngineer#843 Closes 1024XEngineer#850
875bc88 to
51b4069
Compare
Summary
credential_id({endpoint}:{sha256(key)[:16]}),取代primary.key{i}下标身份get_pool_snapshot/pool_routes物化路由边windup:i2v:gate:inflight:cred:{credential_id},并兼容一轮 fix(i2v): 全站在途名额与 429 冷却,避免动作建单打满上游 #842 遗留primary.key{i}映射pool_models)与import_gateway_pool_from_env.py;无 DB 时行为与现网AI_ROUTE_*一致Closes #850
Part of #843
Test plan
uv run pytest tests/test_gateway_pool_ids.py tests/test_gateway_pool_models.py tests/test_i2v_admit.py tests/test_gateway_video.py tests/test_gateway_image.py tests/test_gateway_chat.py -qroute_id仍指向原物理凭证uv run python scripts/import_gateway_pool_from_env.py --dry-run后灌库