codexLB-up 1.25.0b10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (827) hide show
  1. app/__init__.py +10 -0
  2. app/admin_cli.py +494 -0
  3. app/cli.py +339 -0
  4. app/codex_sessions_retag.py +487 -0
  5. app/core/__init__.py +0 -0
  6. app/core/audit/__init__.py +0 -0
  7. app/core/audit/service.py +216 -0
  8. app/core/audit/types.py +94 -0
  9. app/core/auth/__init__.py +236 -0
  10. app/core/auth/api_key_cache.py +65 -0
  11. app/core/auth/dashboard_access.py +429 -0
  12. app/core/auth/dashboard_mode.py +171 -0
  13. app/core/auth/dashboard_session_ttl.py +74 -0
  14. app/core/auth/dashboard_users_cache.py +120 -0
  15. app/core/auth/dependencies.py +710 -0
  16. app/core/auth/external_identity.py +78 -0
  17. app/core/auth/guardian.py +374 -0
  18. app/core/auth/models.py +49 -0
  19. app/core/auth/providers/__init__.py +129 -0
  20. app/core/auth/providers/oidc.py +838 -0
  21. app/core/auth/providers/registry.py +118 -0
  22. app/core/auth/refresh.py +394 -0
  23. app/core/auth/step_up.py +117 -0
  24. app/core/auth/totp.py +81 -0
  25. app/core/balancer/__init__.py +71 -0
  26. app/core/balancer/logic.py +1612 -0
  27. app/core/balancer/rendezvous_hash.py +21 -0
  28. app/core/balancer/types.py +21 -0
  29. app/core/bootstrap.py +172 -0
  30. app/core/cache/invalidation.py +426 -0
  31. app/core/clients/__init__.py +0 -0
  32. app/core/clients/codex.py +996 -0
  33. app/core/clients/codex_version.py +187 -0
  34. app/core/clients/codex_version_snapshot.py +2 -0
  35. app/core/clients/files.py +534 -0
  36. app/core/clients/headers.py +29 -0
  37. app/core/clients/http.py +511 -0
  38. app/core/clients/model_fetcher.py +276 -0
  39. app/core/clients/native_egress.py +1304 -0
  40. app/core/clients/oauth.py +477 -0
  41. app/core/clients/proxy.py +6089 -0
  42. app/core/clients/proxy_websocket.py +1411 -0
  43. app/core/clients/rate_limit_reset_credits.py +412 -0
  44. app/core/clients/stream_errors.py +12 -0
  45. app/core/clients/thread_cache_identity.py +347 -0
  46. app/core/clients/usage.py +467 -0
  47. app/core/clock.py +170 -0
  48. app/core/config/__init__.py +0 -0
  49. app/core/config/background_jobs.py +73 -0
  50. app/core/config/context_window_overrides.py +166 -0
  51. app/core/config/dashboard_overrides.py +145 -0
  52. app/core/config/inheritable.py +47 -0
  53. app/core/config/key_fingerprint.py +115 -0
  54. app/core/config/settings.py +872 -0
  55. app/core/config/settings_cache.py +100 -0
  56. app/core/config/spool_retention.py +217 -0
  57. app/core/config/tiers.py +174 -0
  58. app/core/conversation_archive.py +568 -0
  59. app/core/crypto.py +37 -0
  60. app/core/draining.py +11 -0
  61. app/core/errors.py +365 -0
  62. app/core/exceptions.py +155 -0
  63. app/core/handlers/__init__.py +3 -0
  64. app/core/handlers/exceptions.py +478 -0
  65. app/core/http_protocol.py +176 -0
  66. app/core/http_protocol_httptools.py +204 -0
  67. app/core/ingress_limits.py +17 -0
  68. app/core/metadata_store.py +18 -0
  69. app/core/metrics/__init__.py +0 -0
  70. app/core/metrics/middleware.py +74 -0
  71. app/core/metrics/prometheus.py +604 -0
  72. app/core/middleware/__init__.py +25 -0
  73. app/core/middleware/api_firewall.py +111 -0
  74. app/core/middleware/app_version.py +37 -0
  75. app/core/middleware/dashboard_auth_proxy.py +55 -0
  76. app/core/middleware/dashboard_csrf.py +177 -0
  77. app/core/middleware/dashboard_gzip.py +41 -0
  78. app/core/middleware/dashboard_overrides.py +59 -0
  79. app/core/middleware/firewall_cache.py +91 -0
  80. app/core/middleware/inflight.py +126 -0
  81. app/core/middleware/multipart_content_encoding.py +129 -0
  82. app/core/middleware/path_rewrite.py +148 -0
  83. app/core/middleware/request_body_limit.py +196 -0
  84. app/core/middleware/request_decompression.py +209 -0
  85. app/core/middleware/request_id.py +72 -0
  86. app/core/middleware/required_capability_http.py +116 -0
  87. app/core/middleware/trusted_proxy_headers.py +36 -0
  88. app/core/multipart.py +237 -0
  89. app/core/multipart_fields.py +91 -0
  90. app/core/openai/__init__.py +0 -0
  91. app/core/openai/chat_requests.py +493 -0
  92. app/core/openai/chat_responses.py +931 -0
  93. app/core/openai/contracts.py +46 -0
  94. app/core/openai/exceptions.py +24 -0
  95. app/core/openai/host_models.py +14 -0
  96. app/core/openai/images.py +413 -0
  97. app/core/openai/message_coercion.py +376 -0
  98. app/core/openai/model_refresh_scheduler.py +423 -0
  99. app/core/openai/model_registry.py +997 -0
  100. app/core/openai/model_registry_store.py +548 -0
  101. app/core/openai/models.py +172 -0
  102. app/core/openai/parsing.py +97 -0
  103. app/core/openai/requests.py +1980 -0
  104. app/core/openai/strict_schema.py +199 -0
  105. app/core/openai/tool_call_safety.py +99 -0
  106. app/core/openai/v1_requests.py +187 -0
  107. app/core/plan_types.py +90 -0
  108. app/core/prestop.py +202 -0
  109. app/core/rate_limiter/__init__.py +0 -0
  110. app/core/rate_limiter/db_rate_limiter.py +215 -0
  111. app/core/request_locality.py +473 -0
  112. app/core/resilience/__init__.py +0 -0
  113. app/core/resilience/backpressure.py +57 -0
  114. app/core/resilience/bulkhead.py +202 -0
  115. app/core/resilience/circuit_breaker.py +150 -0
  116. app/core/resilience/degradation.py +55 -0
  117. app/core/resilience/loop_lag_monitor.py +60 -0
  118. app/core/resilience/memory_monitor.py +137 -0
  119. app/core/resilience/network_recovery.py +256 -0
  120. app/core/resilience/overload.py +98 -0
  121. app/core/resilience/toggles.py +114 -0
  122. app/core/retention/__init__.py +0 -0
  123. app/core/retention/job.py +240 -0
  124. app/core/retention/scheduler.py +69 -0
  125. app/core/runtime_logging.py +601 -0
  126. app/core/scheduling/__init__.py +0 -0
  127. app/core/scheduling/leader_election.py +993 -0
  128. app/core/scheduling/leader_election_handle.py +25 -0
  129. app/core/server.py +140 -0
  130. app/core/shutdown.py +326 -0
  131. app/core/socket_peer.py +22 -0
  132. app/core/startup.py +41 -0
  133. app/core/timeout_invariants.py +347 -0
  134. app/core/tracing/__init__.py +0 -0
  135. app/core/tracing/otel.py +207 -0
  136. app/core/types.py +6 -0
  137. app/core/upstream_proxy/__init__.py +13 -0
  138. app/core/upstream_proxy/cache.py +130 -0
  139. app/core/upstream_proxy/resolver.py +189 -0
  140. app/core/upstream_proxy/types.py +104 -0
  141. app/core/usage/__init__.py +407 -0
  142. app/core/usage/depletion.py +109 -0
  143. app/core/usage/live_hub.py +41 -0
  144. app/core/usage/live_snapshots.py +174 -0
  145. app/core/usage/logs.py +212 -0
  146. app/core/usage/metadata_scheduler.py +118 -0
  147. app/core/usage/models.py +54 -0
  148. app/core/usage/pricing.py +576 -0
  149. app/core/usage/pricing_catalog.py +241 -0
  150. app/core/usage/pricing_snapshot.json +821 -0
  151. app/core/usage/quota.py +133 -0
  152. app/core/usage/refresh_policy.py +21 -0
  153. app/core/usage/refresh_scheduler.py +713 -0
  154. app/core/usage/reset_credits_refresh_scheduler.py +435 -0
  155. app/core/usage/types.py +168 -0
  156. app/core/utils/__init__.py +0 -0
  157. app/core/utils/json_guards.py +18 -0
  158. app/core/utils/locks.py +33 -0
  159. app/core/utils/masking.py +20 -0
  160. app/core/utils/proxy_env.py +104 -0
  161. app/core/utils/request_id.py +65 -0
  162. app/core/utils/retry.py +77 -0
  163. app/core/utils/shared_future.py +172 -0
  164. app/core/utils/sse.py +286 -0
  165. app/core/utils/time.py +24 -0
  166. app/db/__init__.py +0 -0
  167. app/db/account_identity_lock.py +56 -0
  168. app/db/alembic/__init__.py +1 -0
  169. app/db/alembic/env.py +70 -0
  170. app/db/alembic/revision_ids.py +43 -0
  171. app/db/alembic/versions/20260213_000000_base_schema.py +248 -0
  172. app/db/alembic/versions/20260213_000100_normalize_account_plan_types.py +45 -0
  173. app/db/alembic/versions/20260213_000200_add_request_logs_reasoning_effort.py +45 -0
  174. app/db/alembic/versions/20260213_000300_add_accounts_reset_at.py +45 -0
  175. app/db/alembic/versions/20260213_000400_add_accounts_chatgpt_account_id.py +45 -0
  176. app/db/alembic/versions/20260213_000500_add_dashboard_settings.py +70 -0
  177. app/db/alembic/versions/20260213_000600_add_dashboard_settings_totp.py +64 -0
  178. app/db/alembic/versions/20260213_000700_add_dashboard_settings_password.py +59 -0
  179. app/db/alembic/versions/20260213_000800_add_api_keys.py +87 -0
  180. app/db/alembic/versions/20260214_000000_add_api_key_limits.py +143 -0
  181. app/db/alembic/versions/20260215_000000_add_idx_logs_requested_at.py +38 -0
  182. app/db/alembic/versions/20260218_000000_add_api_key_usage_reservations.py +117 -0
  183. app/db/alembic/versions/20260218_000100_add_import_without_overwrite_and_drop_accounts_email_unique.py +198 -0
  184. app/db/alembic/versions/20260225_000000_add_dashboard_settings_routing_strategy.py +53 -0
  185. app/db/alembic/versions/20260228_020000_align_api_key_limit_enum_types.py +104 -0
  186. app/db/alembic/versions/20260228_030000_add_api_firewall_allowlist.py +42 -0
  187. app/db/alembic/versions/20260306_000000_add_request_logs_service_tier.py +45 -0
  188. app/db/alembic/versions/20260307_000000_add_api_key_enforcement_fields.py +56 -0
  189. app/db/alembic/versions/20260308_000000_add_sqlite_performance_indexes.py +41 -0
  190. app/db/alembic/versions/20260309_000000_add_additional_usage_history.py +71 -0
  191. app/db/alembic/versions/20260309_000000_request_logs_nullable_account_id.py +28 -0
  192. app/db/alembic/versions/20260310_000000_add_request_logs_transport.py +45 -0
  193. app/db/alembic/versions/20260310_000000_fix_postgresql_enum_value_casing.py +79 -0
  194. app/db/alembic/versions/20260310_120000_add_sticky_session_kinds_and_affinity_ttl.py +110 -0
  195. app/db/alembic/versions/20260312_000000_add_additional_usage_quota_key.py +193 -0
  196. app/db/alembic/versions/20260312_000000_split_sticky_sessions_primary_key_by_kind.py +96 -0
  197. app/db/alembic/versions/20260312_010000_merge_additional_usage_and_sticky_session_heads.py +25 -0
  198. app/db/alembic/versions/20260312_020000_merge_request_logs_transport_and_feature_heads.py +25 -0
  199. app/db/alembic/versions/20260312_020000_merge_request_logs_transport_and_sticky_heads.py +25 -0
  200. app/db/alembic/versions/20260312_120000_add_dashboard_upstream_stream_transport.py +62 -0
  201. app/db/alembic/versions/20260313_024500_merge_request_log_transport_heads.py +27 -0
  202. app/db/alembic/versions/20260319_100937_increase_prompt_cache_ttl_to_1800s.py +73 -0
  203. app/db/alembic/versions/20260319_111500_merge_prompt_cache_ttl_and_upstream_transport_heads.py +25 -0
  204. app/db/alembic/versions/20260319_183000_normalize_sqlite_account_status_casing.py +48 -0
  205. app/db/alembic/versions/20260320_000000_add_request_log_requested_actual_tiers.py +51 -0
  206. app/db/alembic/versions/20260321_190000_tighten_dashboard_db_indexes.py +64 -0
  207. app/db/alembic/versions/20260321_210000_merge_request_log_tiers_and_dashboard_index_heads.py +25 -0
  208. app/db/alembic/versions/20260325_000000_add_request_log_cost.py +193 -0
  209. app/db/alembic/versions/20260328_000000_add_rate_limit_attempts.py +48 -0
  210. app/db/alembic/versions/20260328_120000_add_request_log_ttft.py +37 -0
  211. app/db/alembic/versions/20260328_130000_add_audit_logs_table.py +45 -0
  212. app/db/alembic/versions/20260328_140000_add_scheduler_leader_table.py +39 -0
  213. app/db/alembic/versions/20260330_000000_add_cache_locality_settings.py +69 -0
  214. app/db/alembic/versions/20260330_010000_merge_scheduler_leader_and_cache_locality_heads.py +25 -0
  215. app/db/alembic/versions/20260330_020000_add_bridge_ring_members.py +46 -0
  216. app/db/alembic/versions/20260401_000000_add_api_key_enforced_service_tier.py +52 -0
  217. app/db/alembic/versions/20260401_000000_add_cache_invalidation.py +41 -0
  218. app/db/alembic/versions/20260401_020000_merge_cache_invalidation_and_api_key_service_tier_heads.py +22 -0
  219. app/db/alembic/versions/20260402_000000_switch_dashboard_routing_default_to_capacity_weighted.py +66 -0
  220. app/db/alembic/versions/20260403_000000_add_credit_api_key_limit_values.py +44 -0
  221. app/db/alembic/versions/20260406_000000_add_api_key_account_assignments.py +57 -0
  222. app/db/alembic/versions/20260406_010000_add_api_key_assignment_scope_flag.py +75 -0
  223. app/db/alembic/versions/20260407_000000_add_bridge_gateway_safe_mode.py +51 -0
  224. app/db/alembic/versions/20260407_010000_merge_api_key_assignment_and_bridge_gateway_heads.py +25 -0
  225. app/db/alembic/versions/20260408_000000_switch_import_without_overwrite_default_to_true.py +73 -0
  226. app/db/alembic/versions/20260408_010000_merge_import_without_overwrite_and_assignment_heads.py +25 -0
  227. app/db/alembic/versions/20260409_000000_add_http_bridge_sessions.py +130 -0
  228. app/db/alembic/versions/20260409_000000_switch_sticky_threads_and_prefer_earlier_reset_defaults_to_true.py +89 -0
  229. app/db/alembic/versions/20260409_010000_add_dashboard_settings_bootstrap_token.py +57 -0
  230. app/db/alembic/versions/20260409_010000_merge_http_bridge_and_import_without_overwrite_heads.py +25 -0
  231. app/db/alembic/versions/20260409_020000_fix_http_bridge_last_seen_index.py +46 -0
  232. app/db/alembic/versions/20260410_000000_backfill_pristine_dashboard_settings_defaults.py +77 -0
  233. app/db/alembic/versions/20260410_030000_restore_import_without_overwrite_default_true.py +73 -0
  234. app/db/alembic/versions/20260410_040000_merge_dashboard_defaults_and_import_default_heads.py +25 -0
  235. app/db/alembic/versions/20260413_000000_add_accounts_blocked_at.py +44 -0
  236. app/db/alembic/versions/20260415_160000_add_automations_tables.py +189 -0
  237. app/db/alembic/versions/20260415_160000_add_request_logs_response_lookup_index.py +67 -0
  238. app/db/alembic/versions/20260417_000000_add_request_log_plan_type.py +44 -0
  239. app/db/alembic/versions/20260418_190000_add_automation_runs_cycle_key.py +87 -0
  240. app/db/alembic/versions/20260419_000000_add_automation_run_cycle_metadata.py +56 -0
  241. app/db/alembic/versions/20260419_020000_add_automation_run_cycles_snapshot_tables.py +342 -0
  242. app/db/alembic/versions/20260421_000000_add_dashboard_session_ttl_setting.py +51 -0
  243. app/db/alembic/versions/20260421_120000_merge_request_log_lookup_and_plan_type_heads.py +25 -0
  244. app/db/alembic/versions/20260421_130000_merge_automation_and_request_log_heads.py +25 -0
  245. app/db/alembic/versions/20260422_103000_normalize_legacy_automation_run_cycle_keys.py +648 -0
  246. app/db/alembic/versions/20260423_120000_add_api_key_limit_reset_at_index.py +43 -0
  247. app/db/alembic/versions/20260424_000000_merge_dashboard_session_ttl_and_request_log_heads.py +25 -0
  248. app/db/alembic/versions/20260426_000000_add_dashboard_relative_availability_settings.py +68 -0
  249. app/db/alembic/versions/20260427_130000_add_warmup_model_and_request_kind.py +75 -0
  250. app/db/alembic/versions/20260506_000000_add_account_routing_policy.py +37 -0
  251. app/db/alembic/versions/20260509_000000_add_api_key_traffic_class.py +37 -0
  252. app/db/alembic/versions/20260509_010000_add_additional_quota_routing_policies.py +37 -0
  253. app/db/alembic/versions/20260513_000000_add_accounts_alias.py +44 -0
  254. app/db/alembic/versions/20260513_000000_add_api_key_apply_to_codex_model.py +59 -0
  255. app/db/alembic/versions/20260514_000000_add_request_logs_api_key_time_index.py +43 -0
  256. app/db/alembic/versions/20260515_000000_add_prefer_earlier_reset_window.py +47 -0
  257. app/db/alembic/versions/20260515_000000_soft_delete_request_logs_on_account_delete.py +83 -0
  258. app/db/alembic/versions/20260515_020000_add_split_sticky_budget_thresholds.py +62 -0
  259. app/db/alembic/versions/20260516_000000_add_sqlite_hot_path_indexes.py +75 -0
  260. app/db/alembic/versions/20260517_000000_merge_sqlite_hot_path_and_request_log_delete_heads.py +24 -0
  261. app/db/alembic/versions/20260518_000000_add_http_bridge_durable_input_prefix.py +44 -0
  262. app/db/alembic/versions/20260518_010000_merge_http_bridge_and_sqlite_recovery_heads.py +25 -0
  263. app/db/alembic/versions/20260518_120000_add_dashboard_guest_access.py +57 -0
  264. app/db/alembic/versions/20260520_000000_merge_api_key_and_http_bridge_heads.py +26 -0
  265. app/db/alembic/versions/20260520_010000_add_request_logs_api_key_account_index.py +45 -0
  266. app/db/alembic/versions/20260520_010000_merge_automation_and_api_key_heads.py +26 -0
  267. app/db/alembic/versions/20260520_030000_add_quota_planner.py +194 -0
  268. app/db/alembic/versions/20260520_040000_merge_quota_planner_and_request_log_index_heads.py +24 -0
  269. app/db/alembic/versions/20260521_000000_add_account_security_work_authorized.py +46 -0
  270. app/db/alembic/versions/20260521_000000_merge_dashboard_guest_and_api_key_heads.py +26 -0
  271. app/db/alembic/versions/20260521_000000_merge_prefer_reset_window_head.py +26 -0
  272. app/db/alembic/versions/20260521_000000_merge_warmup_and_request_log_index_heads.py +25 -0
  273. app/db/alembic/versions/20260522_000000_add_limit_warmup_trigger.py +195 -0
  274. app/db/alembic/versions/20260524_000000_merge_accounts_alias_and_reset_window_heads.py +24 -0
  275. app/db/alembic/versions/20260525_000000_add_usage_raw_window_latest_index.py +31 -0
  276. app/db/alembic/versions/20260526_000000_add_request_log_failure_metadata.py +100 -0
  277. app/db/alembic/versions/20260531_000000_add_account_workspace_identity.py +39 -0
  278. app/db/alembic/versions/20260531_000000_add_single_account_routing.py +42 -0
  279. app/db/alembic/versions/20260601_000000_merge_relative_availability_and_usage_raw_heads.py +24 -0
  280. app/db/alembic/versions/20260601_010000_add_api_key_usage_sections.py +58 -0
  281. app/db/alembic/versions/20260601_010000_add_routing_policy_persistence.py +67 -0
  282. app/db/alembic/versions/20260601_010000_merge_security_work_and_relative_availability_heads.py +25 -0
  283. app/db/alembic/versions/20260601_010000_merge_split_sticky_and_relative_usage_heads.py +25 -0
  284. app/db/alembic/versions/20260601_010000_merge_warmup_and_relative_availability_heads.py +25 -0
  285. app/db/alembic/versions/20260601_020000_merge_additional_quota_routing_and_relative_availability_heads.py +25 -0
  286. app/db/alembic/versions/20260601_020000_merge_quota_planner_and_relative_availability_heads.py +25 -0
  287. app/db/alembic/versions/20260601_020000_merge_reset_window_and_relative_availability_heads.py +25 -0
  288. app/db/alembic/versions/20260601_020000_merge_routing_policy_and_split_threshold_heads.py +24 -0
  289. app/db/alembic/versions/20260601_020000_merge_warmup_and_request_log_failure_heads.py +25 -0
  290. app/db/alembic/versions/20260601_030000_merge_single_account_and_routing_quota_heads.py +29 -0
  291. app/db/alembic/versions/20260601_040000_merge_security_work_and_routing_consolidation_heads.py +25 -0
  292. app/db/alembic/versions/20260601_050000_merge_quota_planner_and_routing_consolidation_heads.py +25 -0
  293. app/db/alembic/versions/20260602_050000_add_upstream_proxy_routing.py +164 -0
  294. app/db/alembic/versions/20260602_060000_merge_account_workspace_and_failure_heads.py +25 -0
  295. app/db/alembic/versions/20260602_070000_add_request_log_useragent_fields.py +86 -0
  296. app/db/alembic/versions/20260602_070000_merge_quota_planner_and_account_workspace_heads.py +25 -0
  297. app/db/alembic/versions/20260602_080000_merge_upstream_proxy_and_quota_planner_heads.py +25 -0
  298. app/db/alembic/versions/20260602_080000_merge_useragent_and_upstream_proxy_heads.py +24 -0
  299. app/db/alembic/versions/20260603_000000_add_weekly_pace_working_days.py +52 -0
  300. app/db/alembic/versions/20260603_000000_free_account_monthly_window.py +60 -0
  301. app/db/alembic/versions/20260604_000000_add_reauth_required_account_status.py +159 -0
  302. app/db/alembic/versions/20260604_010000_merge_automations_and_reauth_status_heads.py +25 -0
  303. app/db/alembic/versions/20260604_010000_merge_dashboard_guest_and_reauth_status_heads.py +25 -0
  304. app/db/alembic/versions/20260604_010000_merge_useragent_and_reauth_heads.py +24 -0
  305. app/db/alembic/versions/20260607_000000_merge_weekly_monthly_useragent_heads.py +28 -0
  306. app/db/alembic/versions/20260608_000000_add_hide_upstream_quota_from_api_keys.py +50 -0
  307. app/db/alembic/versions/20260611_000000_add_request_log_archive_request_id.py +47 -0
  308. app/db/alembic/versions/20260611_000000_merge_automations_and_weekly_monthly_useragent_heads.py +26 -0
  309. app/db/alembic/versions/20260611_000000_merge_dashboard_guest_and_weekly_useragent_heads.py +26 -0
  310. app/db/alembic/versions/20260611_010000_add_request_log_client_ip.py +57 -0
  311. app/db/alembic/versions/20260611_020000_merge_request_log_archive_and_client_ip_heads.py +26 -0
  312. app/db/alembic/versions/20260611_030000_merge_dashboard_guest_and_request_log_client_ip_heads.py +26 -0
  313. app/db/alembic/versions/20260613_000000_add_accounts_codex_installation_id.py +57 -0
  314. app/db/alembic/versions/20260613_010000_merge_installation_id_and_dashboard_weekly_heads.py +26 -0
  315. app/db/alembic/versions/20260615_000000_merge_automations_and_dashboard_guest_heads.py +26 -0
  316. app/db/alembic/versions/20260623_000000_add_limit_warmup_exhausted_threshold.py +51 -0
  317. app/db/alembic/versions/20260626_000000_add_http_downstream_transport_policy.py +46 -0
  318. app/db/alembic/versions/20260626_010000_add_request_logs_upstream_transport.py +31 -0
  319. app/db/alembic/versions/20260629_000000_add_dashboard_query_hot_path_indexes.py +53 -0
  320. app/db/alembic/versions/20260630_000000_add_limit_warmup_staggered_idle.py +47 -0
  321. app/db/alembic/versions/20260630_000000_merge_request_log_client_ip_and_dashboard_hot_path_heads.py +26 -0
  322. app/db/alembic/versions/20260630_010000_merge_warmup_and_request_log_dashboard_heads.py +26 -0
  323. app/db/alembic/versions/20260630_020000_merge_automations_and_main_heads.py +26 -0
  324. app/db/alembic/versions/20260630_020000_merge_warmup_threshold_and_main_heads.py +26 -0
  325. app/db/alembic/versions/20260630_030000_add_automation_run_model_snapshot.py +59 -0
  326. app/db/alembic/versions/20260630_040000_merge_automation_snapshot_and_warmup_threshold_heads.py +26 -0
  327. app/db/alembic/versions/20260630_050000_add_automation_run_prompt_snapshot.py +52 -0
  328. app/db/alembic/versions/20260701_000000_add_weekly_pace_smoothing_minutes.py +51 -0
  329. app/db/alembic/versions/20260702_000000_add_model_sources.py +176 -0
  330. app/db/alembic/versions/20260705_000000_add_model_source_audio_transcriptions.py +50 -0
  331. app/db/alembic/versions/20260705_000000_harden_dashboard_session_ttl.py +62 -0
  332. app/db/alembic/versions/20260706_000000_add_model_source_audio_per_minute.py +43 -0
  333. app/db/alembic/versions/20260707_000000_merge_model_sources_and_session_ttl.py +24 -0
  334. app/db/alembic/versions/20260707_010000_merge_automations_and_model_sources_heads.py +24 -0
  335. app/db/alembic/versions/20260707_020000_add_automation_job_account_scope.py +125 -0
  336. app/db/alembic/versions/20260709_000000_add_ttft_phase_observability.py +66 -0
  337. app/db/alembic/versions/20260711_000000_add_dashboard_prohibit_fast_mode.py +42 -0
  338. app/db/alembic/versions/20260711_010000_add_account_chatgpt_user_id.py +40 -0
  339. app/db/alembic/versions/20260711_020000_add_dashboard_account_concurrency_caps.py +71 -0
  340. app/db/alembic/versions/20260711_030000_add_limit_warmup_idle_threshold.py +51 -0
  341. app/db/alembic/versions/20260712_010000_add_account_usage_rollups.py +56 -0
  342. app/db/alembic/versions/20260712_020000_add_api_key_usage_rollups.py +53 -0
  343. app/db/alembic/versions/20260713_020000_add_model_registry_snapshot.py +42 -0
  344. app/db/alembic/versions/20260713_040000_add_account_refresh_claims.py +42 -0
  345. app/db/alembic/versions/20260713_040000_add_replica_guardrails.py +64 -0
  346. app/db/alembic/versions/20260713_070000_add_reset_credit_redeem_tables.py +58 -0
  347. app/db/alembic/versions/20260714_000000_add_oauth_flow_states.py +63 -0
  348. app/db/alembic/versions/20260715_000000_add_request_log_queue_latency.py +44 -0
  349. app/db/alembic/versions/20260716_000000_add_oauth_device_flow_slots.py +41 -0
  350. app/db/alembic/versions/20260716_010000_add_dashboard_retention_settings.py +48 -0
  351. app/db/alembic/versions/20260716_010000_add_reset_credit_display_settings.py +73 -0
  352. app/db/alembic/versions/20260717_000000_add_http_bridge_retry_circuits.py +47 -0
  353. app/db/alembic/versions/20260717_000000_merge_retention_and_reset_credit_display_heads.py +26 -0
  354. app/db/alembic/versions/20260717_000000_optimize_dashboard_hot_path_indexes.py +162 -0
  355. app/db/alembic/versions/20260717_000001_merge_retry_circuits_and_dashboard_indexes.py +22 -0
  356. app/db/alembic/versions/20260720_000000_add_request_log_conversation_id.py +57 -0
  357. app/db/alembic/versions/20260722_000000_backfill_request_log_useragent_families.py +40 -0
  358. app/db/alembic/versions/20260724_000000_add_request_usage_time_rollups.py +176 -0
  359. app/db/alembic/versions/20260724_000000_merge_request_log_schema_heads.py +33 -0
  360. app/db/alembic/versions/20260725_000000_add_http_bridge_pending_tool_calls.py +43 -0
  361. app/db/alembic/versions/20260726_000000_add_account_plan_downgrade_observations.py +44 -0
  362. app/db/alembic/versions/20260726_000000_repair_request_usage_rollups_after_merge.py +37 -0
  363. app/db/alembic/versions/20260727_000000_add_sticky_session_continuity_abandoned_at.py +45 -0
  364. app/db/alembic/versions/20260728_000000_merge_pending_tool_calls_and_rollup_repair_heads.py +31 -0
  365. app/db/alembic/versions/20260730_000000_add_api_key_fair_share_threshold.py +44 -0
  366. app/db/alembic/versions/20260730_000000_add_http_bridge_recovery_attempts.py +60 -0
  367. app/db/alembic/versions/20260731_000000_add_capability_lineage_markers.py +38 -0
  368. app/db/alembic/versions/20260803_000000_merge_http_bridge_recovery_and_capability_lineage_heads.py +30 -0
  369. app/db/alembic/versions/20260804_000000_add_http_bridge_operations.py +63 -0
  370. app/db/alembic/versions/20260804_000001_add_global_http_bridge_operation_fingerprint.py +53 -0
  371. app/db/alembic/versions/20260804_230000_add_request_log_connection_request_kind.py +43 -0
  372. app/db/alembic/versions/20260805_000000_add_http_bridge_operation_spool.py +75 -0
  373. app/db/alembic/versions/20260805_000001_finalize_http_bridge_operation_spool.py +65 -0
  374. app/db/alembic/versions/20260806_000000_add_additional_usage_alias_probe_indexes.py +84 -0
  375. app/db/alembic/versions/20260806_000000_add_anonymous_telemetry.py +49 -0
  376. app/db/alembic/versions/20260806_010000_add_conversation_presence_rollup.py +72 -0
  377. app/db/alembic/versions/20260806_020000_add_usage_history_bulk_covering_indexes.py +99 -0
  378. app/db/alembic/versions/20260806_030000_add_api_key_allowed_reasoning_efforts.py +65 -0
  379. app/db/alembic/versions/20260806_120000_add_http_bridge_owner_process_epoch.py +57 -0
  380. app/db/alembic/versions/20260807_000000_merge_http_bridge_operation_spool_and_latest_main.py +24 -0
  381. app/db/alembic/versions/20260808_000000_tune_usage_history_autovacuum.py +54 -0
  382. app/db/alembic/versions/20260810_000000_add_http_bridge_recovery_dispatch_count.py +44 -0
  383. app/db/alembic/versions/20260811_000000_add_hourly_rollup_cancelled_count.py +92 -0
  384. app/db/alembic/versions/20260812_000000_merge_recovery_dispatch_and_hourly_cancelled_heads.py +25 -0
  385. app/db/alembic/versions/20260812_120000_add_sticky_abandonment_scope.py +43 -0
  386. app/db/alembic/versions/20260813_000000_add_file_account_pins.py +40 -0
  387. app/db/alembic/versions/20260816_000000_add_account_pending_deletion.py +81 -0
  388. app/db/alembic/versions/20260816_000000_add_model_source_embeddings.py +50 -0
  389. app/db/alembic/versions/20260821_000000_add_retry_circuit_admission_generation.py +49 -0
  390. app/db/alembic/versions/20260826_000000_add_http_bridge_event_chunks.py +107 -0
  391. app/db/alembic/versions/20260828_000000_add_accounts_chatgpt_identity_index.py +55 -0
  392. app/db/alembic/versions/20260830_000000_add_quota_warmup_claim_expiry.py +83 -0
  393. app/db/alembic/versions/20260908_000000_add_guest_session_generation.py +37 -0
  394. app/db/alembic/versions/20260908_000000_add_subscription_overflow.py +140 -0
  395. app/db/alembic/versions/20260908_000000_replace_upstream_stream_transport_default_sentinel.py +50 -0
  396. app/db/alembic/versions/20260908_020000_merge_overflow_transport_heads.py +27 -0
  397. app/db/alembic/versions/20260909_000000_add_dashboard_roles.py +72 -0
  398. app/db/alembic/versions/20260909_010000_add_dashboard_users.py +183 -0
  399. app/db/alembic/versions/20260909_020000_reproject_compat_admin_credentials.py +105 -0
  400. app/db/alembic/versions/20260909_030000_add_audit_actor_columns.py +103 -0
  401. app/db/alembic/versions/20260909_030000_dashboard_resilience_toggle_settings.py +63 -0
  402. app/db/alembic/versions/20260909_040000_add_dashboard_user_invites.py +54 -0
  403. app/db/alembic/versions/20260909_040000_dashboard_timeout_settings.py +68 -0
  404. app/db/alembic/versions/20260909_050000_dashboard_routing_overload_settings.py +65 -0
  405. app/db/alembic/versions/20260909_060000_add_report_rollup.py +74 -0
  406. app/db/alembic/versions/20260909_070000_automation_run_claim_budget.py +50 -0
  407. app/db/alembic/versions/20260909_080000_dashboard_stream_bridge_budgets.py +62 -0
  408. app/db/alembic/versions/20260909_090000_dashboard_background_job_toggles.py +63 -0
  409. app/db/alembic/versions/20260909_100000_dashboard_codex_prewarm.py +49 -0
  410. app/db/alembic/versions/20260909_110000_model_context_window_overrides.py +45 -0
  411. app/db/alembic/versions/20260909_120000_dashboard_conversation_archive.py +48 -0
  412. app/db/alembic/versions/20260909_130000_add_request_logs_live_facet_indexes.py +96 -0
  413. app/db/alembic/versions/20260910_000000_add_totp_required_for_admin_role.py +44 -0
  414. app/db/alembic/versions/20260910_000000_request_logs_missing_cost_index.py +52 -0
  415. app/db/alembic/versions/20260910_010000_add_dashboard_auth_providers.py +134 -0
  416. app/db/alembic/versions/20260910_010000_dashboard_spool_retention.py +52 -0
  417. app/db/alembic/versions/20260910_020000_add_dashboard_role_mappings.py +74 -0
  418. app/db/alembic/versions/20260910_143000_request_log_affinity.py +28 -0
  419. app/db/alembic/versions/20260910_180000_merge_affinity_guest_heads.py +17 -0
  420. app/db/alembic/versions/20260910_200000_merge_affinity_identity_heads.py +17 -0
  421. app/db/alembic/versions/20260910_220000_merge_affinity_invite_heads.py +17 -0
  422. app/db/alembic/versions/20260911_000000_model_source_pins_kind_expires_index.py +43 -0
  423. app/db/alembic/versions/20260911_010000_merge_pin_index_and_affinity_heads.py +17 -0
  424. app/db/alembic/versions/20260911_020000_add_http_bridge_terminal_append_phase.py +54 -0
  425. app/db/alembic/versions/20260911_030000_add_local_login_policy.py +48 -0
  426. app/db/alembic/versions/20260911_040000_add_thread_cache_identity_mode.py +48 -0
  427. app/db/alembic/versions/20260911_060000_add_bridge_session_continuity_abandonment.py +40 -0
  428. app/db/alembic/versions/20260912_000000_merge_thread_cache_and_bridge_retirement_heads.py +22 -0
  429. app/db/alembic/versions/20260912_010000_drop_legacy_dashboard_credentials.py +140 -0
  430. app/db/alembic/versions/20260913_000000_add_oidc_provider_flow.py +134 -0
  431. app/db/alembic/versions/20260914_000000_add_scim_tokens.py +78 -0
  432. app/db/alembic/versions/20260914_000000_drop_subscription_overflow_schema.py +132 -0
  433. app/db/alembic/versions/20260918_000000_merge_scim_and_overflow_heads.py +17 -0
  434. app/db/alembic/versions/__init__.py +1 -0
  435. app/db/backup.py +64 -0
  436. app/db/migrate.py +1249 -0
  437. app/db/migration_lock.py +160 -0
  438. app/db/migration_url.py +18 -0
  439. app/db/models.py +3100 -0
  440. app/db/recover.py +244 -0
  441. app/db/session.py +1295 -0
  442. app/db/snapshot.py +46 -0
  443. app/db/sqlite_lock_retry.py +190 -0
  444. app/db/sqlite_utils.py +549 -0
  445. app/dependencies.py +462 -0
  446. app/main.py +1240 -0
  447. app/modules/__init__.py +0 -0
  448. app/modules/accounts/__init__.py +0 -0
  449. app/modules/accounts/api.py +426 -0
  450. app/modules/accounts/auth_manager.py +1439 -0
  451. app/modules/accounts/background_repository.py +160 -0
  452. app/modules/accounts/deletion.py +540 -0
  453. app/modules/accounts/mappers.py +654 -0
  454. app/modules/accounts/refresh_claims.py +352 -0
  455. app/modules/accounts/repository.py +1679 -0
  456. app/modules/accounts/schemas.py +308 -0
  457. app/modules/accounts/service.py +809 -0
  458. app/modules/accounts/usage_rollup.py +486 -0
  459. app/modules/accounts/usage_rollup_scheduler.py +75 -0
  460. app/modules/accounts/usage_time_rollup.py +1037 -0
  461. app/modules/accounts/usage_time_rollup_read.py +522 -0
  462. app/modules/api_keys/__init__.py +1 -0
  463. app/modules/api_keys/api.py +344 -0
  464. app/modules/api_keys/last_used_coalescer.py +216 -0
  465. app/modules/api_keys/limit_windows.py +41 -0
  466. app/modules/api_keys/repository.py +1200 -0
  467. app/modules/api_keys/reset_scheduler.py +85 -0
  468. app/modules/api_keys/schemas.py +135 -0
  469. app/modules/api_keys/service.py +2123 -0
  470. app/modules/audit/__init__.py +0 -0
  471. app/modules/audit/api.py +88 -0
  472. app/modules/audit/repository.py +74 -0
  473. app/modules/audit/schemas.py +33 -0
  474. app/modules/audit/service.py +71 -0
  475. app/modules/auth_providers/__init__.py +0 -0
  476. app/modules/auth_providers/api.py +127 -0
  477. app/modules/auth_providers/config.py +343 -0
  478. app/modules/auth_providers/repository.py +101 -0
  479. app/modules/auth_providers/schemas.py +88 -0
  480. app/modules/auth_providers/seed.py +105 -0
  481. app/modules/auth_providers/service.py +289 -0
  482. app/modules/automations/__init__.py +1 -0
  483. app/modules/automations/api.py +369 -0
  484. app/modules/automations/repository.py +1896 -0
  485. app/modules/automations/scheduler.py +107 -0
  486. app/modules/automations/schemas.py +158 -0
  487. app/modules/automations/service.py +2548 -0
  488. app/modules/cache_isolation_probe/__init__.py +0 -0
  489. app/modules/cache_isolation_probe/api.py +188 -0
  490. app/modules/cache_isolation_probe/prefix.py +144 -0
  491. app/modules/cache_isolation_probe/schemas.py +94 -0
  492. app/modules/cache_isolation_probe/sender.py +229 -0
  493. app/modules/cache_isolation_probe/service.py +525 -0
  494. app/modules/conversation_archive/__init__.py +1 -0
  495. app/modules/conversation_archive/api.py +101 -0
  496. app/modules/conversation_archive/schemas.py +28 -0
  497. app/modules/conversation_archive/service.py +165 -0
  498. app/modules/dashboard/__init__.py +1 -0
  499. app/modules/dashboard/api.py +106 -0
  500. app/modules/dashboard/builders.py +107 -0
  501. app/modules/dashboard/repository.py +304 -0
  502. app/modules/dashboard/schemas.py +148 -0
  503. app/modules/dashboard/service.py +534 -0
  504. app/modules/dashboard/timeframes.py +19 -0
  505. app/modules/dashboard/weekly_pace.py +617 -0
  506. app/modules/dashboard_auth/__init__.py +1 -0
  507. app/modules/dashboard_auth/api.py +1344 -0
  508. app/modules/dashboard_auth/oidc_api.py +753 -0
  509. app/modules/dashboard_auth/oidc_flows.py +330 -0
  510. app/modules/dashboard_auth/repository.py +452 -0
  511. app/modules/dashboard_auth/schemas.py +232 -0
  512. app/modules/dashboard_auth/service.py +1384 -0
  513. app/modules/dashboard_roles/__init__.py +0 -0
  514. app/modules/dashboard_roles/api.py +64 -0
  515. app/modules/dashboard_roles/repository.py +54 -0
  516. app/modules/dashboard_roles/schemas.py +29 -0
  517. app/modules/dashboard_roles/seed.py +80 -0
  518. app/modules/dashboard_roles/service.py +142 -0
  519. app/modules/dashboard_users/__init__.py +0 -0
  520. app/modules/dashboard_users/api.py +278 -0
  521. app/modules/dashboard_users/break_glass.py +169 -0
  522. app/modules/dashboard_users/credentials.py +18 -0
  523. app/modules/dashboard_users/identity_resolver.py +665 -0
  524. app/modules/dashboard_users/repository.py +725 -0
  525. app/modules/dashboard_users/schemas.py +117 -0
  526. app/modules/dashboard_users/service.py +1054 -0
  527. app/modules/firewall/__init__.py +1 -0
  528. app/modules/firewall/api.py +78 -0
  529. app/modules/firewall/repository.py +51 -0
  530. app/modules/firewall/schemas.py +28 -0
  531. app/modules/firewall/service.py +93 -0
  532. app/modules/fleet/__init__.py +1 -0
  533. app/modules/fleet/api.py +177 -0
  534. app/modules/fleet/mappers.py +56 -0
  535. app/modules/fleet/observability.py +372 -0
  536. app/modules/fleet/schemas.py +119 -0
  537. app/modules/health/__init__.py +0 -0
  538. app/modules/health/api.py +246 -0
  539. app/modules/health/schemas.py +27 -0
  540. app/modules/limit_warmup/__init__.py +1 -0
  541. app/modules/limit_warmup/repository.py +159 -0
  542. app/modules/limit_warmup/service.py +1001 -0
  543. app/modules/model_sources/__init__.py +1 -0
  544. app/modules/model_sources/api.py +99 -0
  545. app/modules/model_sources/catalog.py +307 -0
  546. app/modules/model_sources/forwarding.py +1435 -0
  547. app/modules/model_sources/projection.py +55 -0
  548. app/modules/model_sources/repository.py +201 -0
  549. app/modules/model_sources/schemas.py +79 -0
  550. app/modules/model_sources/selection.py +166 -0
  551. app/modules/model_sources/service.py +236 -0
  552. app/modules/oauth/__init__.py +0 -0
  553. app/modules/oauth/api.py +101 -0
  554. app/modules/oauth/repository.py +238 -0
  555. app/modules/oauth/schemas.py +49 -0
  556. app/modules/oauth/service.py +1196 -0
  557. app/modules/oauth/templates/oauth_success.html +122 -0
  558. app/modules/proxy/__init__.py +0 -0
  559. app/modules/proxy/_load_balancer/__init__.py +1 -0
  560. app/modules/proxy/_load_balancer/error_rate.py +116 -0
  561. app/modules/proxy/_load_balancer/latency_cohort.py +154 -0
  562. app/modules/proxy/_load_balancer/model_eligibility.py +250 -0
  563. app/modules/proxy/_load_balancer/opportunistic_admission.py +350 -0
  564. app/modules/proxy/_load_balancer/overload_backoff.py +394 -0
  565. app/modules/proxy/_load_balancer/sticky_selection.py +2417 -0
  566. app/modules/proxy/_load_balancer/throughput_cohort.py +233 -0
  567. app/modules/proxy/_load_balancer/ttft_cohort.py +214 -0
  568. app/modules/proxy/_load_balancer/tunables.py +97 -0
  569. app/modules/proxy/_load_balancer/types.py +96 -0
  570. app/modules/proxy/_load_balancer/unbound_selection.py +534 -0
  571. app/modules/proxy/_service/__init__.py +6 -0
  572. app/modules/proxy/_service/api_key_usage.py +745 -0
  573. app/modules/proxy/_service/clock_budget.py +38 -0
  574. app/modules/proxy/_service/codex_control.py +611 -0
  575. app/modules/proxy/_service/compact.py +2211 -0
  576. app/modules/proxy/_service/file_ops.py +749 -0
  577. app/modules/proxy/_service/http_bridge/__init__.py +3 -0
  578. app/modules/proxy/_service/http_bridge/accepted_replay.py +331 -0
  579. app/modules/proxy/_service/http_bridge/account_sessions.py +57 -0
  580. app/modules/proxy/_service/http_bridge/activity.py +178 -0
  581. app/modules/proxy/_service/http_bridge/helpers.py +3959 -0
  582. app/modules/proxy/_service/http_bridge/mixin.py +2435 -0
  583. app/modules/proxy/_service/http_bridge/owner_forwarding.py +609 -0
  584. app/modules/proxy/_service/http_bridge/protocol.py +167 -0
  585. app/modules/proxy/_service/http_bridge/proxy_failover.py +58 -0
  586. app/modules/proxy/_service/http_bridge/quarantine.py +443 -0
  587. app/modules/proxy/_service/http_bridge/request_submit.py +4582 -0
  588. app/modules/proxy/_service/http_bridge/retry_circuit.py +2442 -0
  589. app/modules/proxy/_service/http_bridge/service_stubs.py +492 -0
  590. app/modules/proxy/_service/http_bridge/session_registry.py +778 -0
  591. app/modules/proxy/_service/http_bridge/streaming.py +5328 -0
  592. app/modules/proxy/_service/http_bridge/upstream_events.py +4538 -0
  593. app/modules/proxy/_service/observability.py +410 -0
  594. app/modules/proxy/_service/rate_limit.py +361 -0
  595. app/modules/proxy/_service/realtime_live.py +655 -0
  596. app/modules/proxy/_service/refresh.py +154 -0
  597. app/modules/proxy/_service/request_log.py +585 -0
  598. app/modules/proxy/_service/response_create.py +930 -0
  599. app/modules/proxy/_service/streaming/__init__.py +3 -0
  600. app/modules/proxy/_service/streaming/helpers.py +1317 -0
  601. app/modules/proxy/_service/streaming/mixin.py +1100 -0
  602. app/modules/proxy/_service/streaming/protocol.py +32 -0
  603. app/modules/proxy/_service/streaming/retry.py +3762 -0
  604. app/modules/proxy/_service/support.py +2122 -0
  605. app/modules/proxy/_service/transcribe.py +434 -0
  606. app/modules/proxy/_service/warmup.py +483 -0
  607. app/modules/proxy/_service/websocket/__init__.py +3 -0
  608. app/modules/proxy/_service/websocket/helpers.py +2360 -0
  609. app/modules/proxy/_service/websocket/mixin.py +7285 -0
  610. app/modules/proxy/_service/websocket/protocol.py +74 -0
  611. app/modules/proxy/account_cache.py +229 -0
  612. app/modules/proxy/account_eligibility.py +87 -0
  613. app/modules/proxy/additional_model_limits.py +40 -0
  614. app/modules/proxy/affinity.py +830 -0
  615. app/modules/proxy/affinity_observation.py +101 -0
  616. app/modules/proxy/api.py +10254 -0
  617. app/modules/proxy/api_key_usage.py +113 -0
  618. app/modules/proxy/cap_partitioning.py +280 -0
  619. app/modules/proxy/capability_lineage.py +65 -0
  620. app/modules/proxy/capability_lineage_repository.py +82 -0
  621. app/modules/proxy/capability_routing.py +253 -0
  622. app/modules/proxy/continuity.py +112 -0
  623. app/modules/proxy/downstream_delivery.py +277 -0
  624. app/modules/proxy/durable_bridge_coordinator.py +1217 -0
  625. app/modules/proxy/durable_bridge_repository.py +4516 -0
  626. app/modules/proxy/durable_bridge_runtime.py +9 -0
  627. app/modules/proxy/durable_bridge_transcript_codec.py +122 -0
  628. app/modules/proxy/fair_share.py +131 -0
  629. app/modules/proxy/file_pin_repository.py +210 -0
  630. app/modules/proxy/helpers.py +439 -0
  631. app/modules/proxy/http_bridge_event_batcher.py +613 -0
  632. app/modules/proxy/http_bridge_forwarding.py +797 -0
  633. app/modules/proxy/http_continuation.py +76 -0
  634. app/modules/proxy/images_observability.py +62 -0
  635. app/modules/proxy/images_service.py +1054 -0
  636. app/modules/proxy/load_balancer.py +3016 -0
  637. app/modules/proxy/rate_limit_cache.py +46 -0
  638. app/modules/proxy/replay_safety.py +1063 -0
  639. app/modules/proxy/repo_bundle.py +31 -0
  640. app/modules/proxy/request_policy.py +1054 -0
  641. app/modules/proxy/ring_membership.py +251 -0
  642. app/modules/proxy/schemas.py +345 -0
  643. app/modules/proxy/selection_errors.py +41 -0
  644. app/modules/proxy/service.py +2600 -0
  645. app/modules/proxy/source_admission.py +125 -0
  646. app/modules/proxy/source_dispatch.py +999 -0
  647. app/modules/proxy/sticky_repository.py +831 -0
  648. app/modules/proxy/thread_anchors.py +491 -0
  649. app/modules/proxy/tool_call_dedupe.py +802 -0
  650. app/modules/proxy/types.py +54 -0
  651. app/modules/proxy/work_admission.py +121 -0
  652. app/modules/quota_planner/__init__.py +1 -0
  653. app/modules/quota_planner/api.py +276 -0
  654. app/modules/quota_planner/logic.py +743 -0
  655. app/modules/quota_planner/repository.py +722 -0
  656. app/modules/quota_planner/scheduler.py +212 -0
  657. app/modules/quota_planner/schemas.py +102 -0
  658. app/modules/quota_planner/warmup.py +693 -0
  659. app/modules/rate_limit_reset_credits/__init__.py +0 -0
  660. app/modules/rate_limit_reset_credits/api.py +601 -0
  661. app/modules/rate_limit_reset_credits/redeem_coordination.py +274 -0
  662. app/modules/rate_limit_reset_credits/store.py +114 -0
  663. app/modules/reports/__init__.py +1 -0
  664. app/modules/reports/api.py +134 -0
  665. app/modules/reports/cache.py +74 -0
  666. app/modules/reports/filters.py +27 -0
  667. app/modules/reports/repository.py +525 -0
  668. app/modules/reports/rollup.py +150 -0
  669. app/modules/reports/rollup_read.py +148 -0
  670. app/modules/reports/schemas.py +123 -0
  671. app/modules/reports/service.py +287 -0
  672. app/modules/reports/thread_identity.py +256 -0
  673. app/modules/request_logs/__init__.py +0 -0
  674. app/modules/request_logs/api.py +224 -0
  675. app/modules/request_logs/cost_backfill.py +154 -0
  676. app/modules/request_logs/mappers.py +97 -0
  677. app/modules/request_logs/repository.py +1765 -0
  678. app/modules/request_logs/schemas.py +145 -0
  679. app/modules/request_logs/service.py +348 -0
  680. app/modules/role_mappings/__init__.py +0 -0
  681. app/modules/role_mappings/api.py +206 -0
  682. app/modules/role_mappings/matching.py +55 -0
  683. app/modules/role_mappings/repository.py +106 -0
  684. app/modules/role_mappings/schemas.py +67 -0
  685. app/modules/role_mappings/service.py +334 -0
  686. app/modules/runtime/__init__.py +1 -0
  687. app/modules/runtime/api.py +18 -0
  688. app/modules/runtime/schemas.py +14 -0
  689. app/modules/runtime/service.py +218 -0
  690. app/modules/scim/__init__.py +1 -0
  691. app/modules/scim/api.py +194 -0
  692. app/modules/scim/dependencies.py +112 -0
  693. app/modules/scim/errors.py +77 -0
  694. app/modules/scim/management_api.py +156 -0
  695. app/modules/scim/repository.py +212 -0
  696. app/modules/scim/schemas.py +145 -0
  697. app/modules/scim/service.py +786 -0
  698. app/modules/scim/tokens.py +43 -0
  699. app/modules/settings/__init__.py +1 -0
  700. app/modules/settings/api.py +1677 -0
  701. app/modules/settings/repository.py +606 -0
  702. app/modules/settings/schemas.py +496 -0
  703. app/modules/settings/service.py +769 -0
  704. app/modules/shared/__init__.py +0 -0
  705. app/modules/shared/schemas.py +22 -0
  706. app/modules/sticky_sessions/__init__.py +1 -0
  707. app/modules/sticky_sessions/api.py +118 -0
  708. app/modules/sticky_sessions/cleanup_scheduler.py +487 -0
  709. app/modules/sticky_sessions/schemas.py +78 -0
  710. app/modules/sticky_sessions/service.py +196 -0
  711. app/modules/telemetry/__init__.py +5 -0
  712. app/modules/telemetry/api.py +115 -0
  713. app/modules/telemetry/clients.py +62 -0
  714. app/modules/telemetry/consent.py +115 -0
  715. app/modules/telemetry/scheduler.py +85 -0
  716. app/modules/telemetry/schemas.py +163 -0
  717. app/modules/telemetry/sender.py +221 -0
  718. app/modules/telemetry/snapshot.py +490 -0
  719. app/modules/usage/__init__.py +0 -0
  720. app/modules/usage/additional_quota_keys.py +379 -0
  721. app/modules/usage/api.py +45 -0
  722. app/modules/usage/background_repository.py +147 -0
  723. app/modules/usage/builders.py +559 -0
  724. app/modules/usage/depletion_service.py +277 -0
  725. app/modules/usage/live_ingest.py +356 -0
  726. app/modules/usage/mappers.py +29 -0
  727. app/modules/usage/plan_downgrade_observations.py +488 -0
  728. app/modules/usage/repository.py +1890 -0
  729. app/modules/usage/schemas.py +68 -0
  730. app/modules/usage/service.py +102 -0
  731. app/modules/usage/updater.py +1488 -0
  732. app/static/assets/Trans-Cj-J9mVA.js +1 -0
  733. app/static/assets/access-page-DHzZiXZi.js +1 -0
  734. app/static/assets/account-cost-donut-DGhfA4U8.js +1 -0
  735. app/static/assets/account-identifiers-CxbdLshZ.js +1 -0
  736. app/static/assets/account-status-DIsU5sUS.js +1 -0
  737. app/static/assets/account-trend-chart-BptlfwwQ.js +1 -0
  738. app/static/assets/accounts-page-Bbai8ift.js +3 -0
  739. app/static/assets/api-Dwa7XxPe.js +1 -0
  740. app/static/assets/api-client-dugX1Pti.js +55 -0
  741. app/static/assets/api-key-create-dialog-DnRm0oYN.js +1 -0
  742. app/static/assets/api-key-created-dialog-B6jkDG9f.js +1 -0
  743. app/static/assets/api-key-edit-dialog-Ck3Bnc3V.js +1 -0
  744. app/static/assets/api-trend-chart-BchCw7Hj.js +1 -0
  745. app/static/assets/apis-page-BvXz9c6i.js +2 -0
  746. app/static/assets/arrow-up-Dqh_KzJo.js +1 -0
  747. app/static/assets/arrow-up-down-LBRgVDGY.js +1 -0
  748. app/static/assets/automations-page-DQgNAJOR.js +3 -0
  749. app/static/assets/badge-DqMb9es0.js +1 -0
  750. app/static/assets/blur-email-HEvKagG2.js +1 -0
  751. app/static/assets/button-BjVmmiGW.js +1 -0
  752. app/static/assets/checkbox-apsl9vzk.js +1 -0
  753. app/static/assets/chevron-left-1d_pHVa5.js +1 -0
  754. app/static/assets/circle-alert-BS0xd_o1.js +1 -0
  755. app/static/assets/clsx-DB0hHKMi.js +1 -0
  756. app/static/assets/colors-BBy8GEtr.js +1 -0
  757. app/static/assets/copy-button-D7nAvOKR.js +1 -0
  758. app/static/assets/cost-per-day-chart-YdqlkZVM.js +1 -0
  759. app/static/assets/createLucideIcon-DBLzLNh2.js +1 -0
  760. app/static/assets/dashboard-page-CSdhBy0f.js +2 -0
  761. app/static/assets/distribution-metric-format-yZTy1ZEu.js +1 -0
  762. app/static/assets/donut-chart-Bxc69cra.js +1 -0
  763. app/static/assets/download-kQteIcsB.js +1 -0
  764. app/static/assets/ellipsis-B_5OgIre.js +1 -0
  765. app/static/assets/empty-state-JcA7tc8m.js +1 -0
  766. app/static/assets/es2015-bMH8YKZT.js +41 -0
  767. app/static/assets/es6-B264ni6W.js +69 -0
  768. app/static/assets/external-link-UVAOgMQ0.js +1 -0
  769. app/static/assets/i18n-BWqZ9Ua2.js +1 -0
  770. app/static/assets/inbox-B_RM2s1J.js +1 -0
  771. app/static/assets/index-DIpd264Z.js +23 -0
  772. app/static/assets/index-DMeLKRJx.css +2 -0
  773. app/static/assets/inherit-badge-QhTYVaU6.js +1 -0
  774. app/static/assets/invite-dialog-0CYh0GiQ.js +1 -0
  775. app/static/assets/iso-Bpe5Ihbb.js +1 -0
  776. app/static/assets/label-CGaLmLNf.js +1 -0
  777. app/static/assets/lazy-recharts-Pv8p9ArM.js +2 -0
  778. app/static/assets/loading-overlay-CzKQfHLI.js +1 -0
  779. app/static/assets/mini-quota-bar-cGT_t8Ae.js +1 -0
  780. app/static/assets/model-distribution-donut-BKjAfT48.js +1 -0
  781. app/static/assets/model-source-multi-select-D2d06Vc7.js +1 -0
  782. app/static/assets/multi-select-filter-KIA5qU6Z.js +1 -0
  783. app/static/assets/oauth-dialog-XeaGGDjS.js +1 -0
  784. app/static/assets/pagination-controls-D1Ypd0RU.js +1 -0
  785. app/static/assets/payload-CIYnGZz9.js +1 -0
  786. app/static/assets/queue-wait-chart-VoV7Yg9L.js +1 -0
  787. app/static/assets/report-chart-card-BXDliwcc.js +1 -0
  788. app/static/assets/reports-page-CGCJeMHG.js +3 -0
  789. app/static/assets/reset-credit-confirm-dialog-QOifTnVB.js +1 -0
  790. app/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  791. app/static/assets/search-HfhqwERt.js +1 -0
  792. app/static/assets/select-Cl33bz-g.js +1 -0
  793. app/static/assets/settings-page-mZj01HYl.js +2 -0
  794. app/static/assets/shim-CZwyM4Q7.js +1 -0
  795. app/static/assets/skeleton-C_EUc4Rp.js +1 -0
  796. app/static/assets/sparkline-chart-DdNNsddM.js +1 -0
  797. app/static/assets/status-badge-CHHA5X6c.js +1 -0
  798. app/static/assets/switch-CzxgokcM.js +1 -0
  799. app/static/assets/table-DXaNSQ0G.js +1 -0
  800. app/static/assets/time-to-first-token-chart--68L0u_v.js +1 -0
  801. app/static/assets/tokens-per-day-chart-Cs82BFuE.js +1 -0
  802. app/static/assets/tokens-per-second-chart-AD2dfOn6.js +1 -0
  803. app/static/assets/totp-settings-CRHtxQ7j.js +1 -0
  804. app/static/assets/trash-D3KyWr1h.js +1 -0
  805. app/static/assets/use-account-quota-display-BPmrTBTC.js +1 -0
  806. app/static/assets/use-accounts-PWlVGw3z.js +1 -0
  807. app/static/assets/use-auth-CuDlW3iC.js +1 -0
  808. app/static/assets/use-chart-colors-DIu6tClI.js +1 -0
  809. app/static/assets/use-model-sources-Bul24JBu.js +1 -0
  810. app/static/assets/use-models-DUCPUuZg.js +1 -0
  811. app/static/assets/use-reduced-motion-hAm5r6_d.js +1 -0
  812. app/static/assets/useTranslation--56nHhlH.js +1 -0
  813. app/static/assets/useragent-distribution-donut-Dup6sf_n.js +1 -0
  814. app/static/assets/users-uIC3TIlE.js +1 -0
  815. app/static/assets/vendor-query-_bHZuxEx.js +1 -0
  816. app/static/assets/vendor-react-BVL0YZPW.js +9 -0
  817. app/static/favicon.svg +10 -0
  818. app/static/fonts/GeistSans-Variable.woff2 +0 -0
  819. app/static/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
  820. app/static/fonts/jetbrains-mono-latin.woff2 +0 -0
  821. app/static/index.html +29 -0
  822. codexlb_up-1.25.0b10.dist-info/METADATA +124 -0
  823. codexlb_up-1.25.0b10.dist-info/RECORD +827 -0
  824. codexlb_up-1.25.0b10.dist-info/WHEEL +4 -0
  825. codexlb_up-1.25.0b10.dist-info/entry_points.txt +3 -0
  826. codexlb_up-1.25.0b10.dist-info/licenses/LICENSE +21 -0
  827. config/additional_quota_registry.json +11 -0
@@ -0,0 +1,48 @@
1
+ """Add dashboard_settings.local_login_policy (PLAN §4.6: who may use the local password form).
2
+
3
+ Revision ID: 20260911_030000_add_local_login_policy
4
+ Revises: 20260911_020000_add_http_bridge_terminal_append_phase
5
+ Create Date: 2026-09-10
6
+
7
+ String, NOT NULL, server default ``enabled``: an upgraded install keeps
8
+ today's behaviour (every active account holding a password may sign in) until
9
+ an operator tightens the policy from the Settings page. No data backfill, and
10
+ deliberately no environment variable.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import sqlalchemy as sa
16
+ from alembic import op
17
+ from sqlalchemy.engine import Connection
18
+
19
+ revision = "20260911_030000_add_local_login_policy"
20
+ down_revision = "20260911_020000_add_http_bridge_terminal_append_phase"
21
+ branch_labels = None
22
+ depends_on = None
23
+
24
+ _TABLE = "dashboard_settings"
25
+ _COLUMN = "local_login_policy"
26
+ _DEFAULT = "enabled"
27
+
28
+
29
+ def _columns(connection: Connection) -> set[str]:
30
+ if not sa.inspect(connection).has_table(_TABLE):
31
+ return set()
32
+ return {column["name"] for column in sa.inspect(connection).get_columns(_TABLE)}
33
+
34
+
35
+ def upgrade() -> None:
36
+ columns = _columns(op.get_bind())
37
+ if columns and _COLUMN not in columns:
38
+ with op.batch_alter_table(_TABLE) as batch_op:
39
+ batch_op.add_column(
40
+ sa.Column(_COLUMN, sa.String(length=32), server_default=sa.text(f"'{_DEFAULT}'"), nullable=False)
41
+ )
42
+
43
+
44
+ def downgrade() -> None:
45
+ columns = _columns(op.get_bind())
46
+ if columns and _COLUMN in columns:
47
+ with op.batch_alter_table(_TABLE) as batch_op:
48
+ batch_op.drop_column(_COLUMN)
@@ -0,0 +1,48 @@
1
+ """Add the thread cache identity mode setting and its per-API-key override.
2
+
3
+ Revision ID: 20260911_010000_add_thread_cache_identity_mode
4
+ Revises: 20260911_010000_merge_pin_index_and_affinity_heads
5
+ Create Date: 2026-09-11 01:00:00.000000
6
+
7
+ Both columns are nullable and are created without a server default, so an
8
+ upgrade introduces no decision: NULL on ``dashboard_settings`` inherits the
9
+ environment value and then the ``shared`` code default, and NULL on
10
+ ``api_keys`` means "follow the fleet". ``shared`` reproduces today's outbound
11
+ bytes exactly, so an upgraded install behaves identically until an operator
12
+ sets a value.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import sqlalchemy as sa
18
+ from alembic import op
19
+
20
+ revision = "20260911_040000_add_thread_cache_identity_mode"
21
+ down_revision = "20260911_030000_add_local_login_policy"
22
+ branch_labels = None
23
+ depends_on = None
24
+
25
+
26
+ def _has_column(table_name: str, column_name: str) -> bool:
27
+ inspector = sa.inspect(op.get_bind())
28
+ return column_name in {column["name"] for column in inspector.get_columns(table_name)}
29
+
30
+
31
+ def upgrade() -> None:
32
+ if not _has_column("dashboard_settings", "thread_cache_identity_mode"):
33
+ op.add_column(
34
+ "dashboard_settings",
35
+ sa.Column("thread_cache_identity_mode", sa.String(), nullable=True),
36
+ )
37
+ if not _has_column("api_keys", "thread_cache_identity_override"):
38
+ op.add_column(
39
+ "api_keys",
40
+ sa.Column("thread_cache_identity_override", sa.String(), nullable=True),
41
+ )
42
+
43
+
44
+ def downgrade() -> None:
45
+ if _has_column("api_keys", "thread_cache_identity_override"):
46
+ op.drop_column("api_keys", "thread_cache_identity_override")
47
+ if _has_column("dashboard_settings", "thread_cache_identity_mode"):
48
+ op.drop_column("dashboard_settings", "thread_cache_identity_mode")
@@ -0,0 +1,40 @@
1
+ """Add continuity-abandonment markers to durable HTTP bridge sessions.
2
+
3
+ Revision ID: 20260911_060000_add_bridge_session_continuity_abandonment
4
+ Revises: 20260911_030000_add_local_login_policy
5
+ Create Date: 2026-09-11
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sqlalchemy as sa
11
+ from alembic import op
12
+
13
+ revision = "20260911_060000_add_bridge_session_continuity_abandonment"
14
+ down_revision = "20260911_030000_add_local_login_policy"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ # Mirrors sticky_sessions' pair exactly: a scope-only marker is the new
21
+ # reader's tombstone, while a timestamp with NULL scope abandons globally.
22
+ # Both stay NULL on every existing row, so a rolling deploy keeps treating
23
+ # account_id as hard ownership until a writer retires a specific owner.
24
+ bind = op.get_bind()
25
+ columns = {column["name"] for column in sa.inspect(bind).get_columns("http_bridge_sessions")}
26
+ with op.batch_alter_table("http_bridge_sessions") as batch_op:
27
+ if "continuity_abandoned_at" not in columns:
28
+ batch_op.add_column(sa.Column("continuity_abandoned_at", sa.DateTime(timezone=True), nullable=True))
29
+ if "continuity_abandonment_scope" not in columns:
30
+ batch_op.add_column(sa.Column("continuity_abandonment_scope", sa.String(32), nullable=True))
31
+
32
+
33
+ def downgrade() -> None:
34
+ bind = op.get_bind()
35
+ columns = {column["name"] for column in sa.inspect(bind).get_columns("http_bridge_sessions")}
36
+ with op.batch_alter_table("http_bridge_sessions") as batch_op:
37
+ if "continuity_abandonment_scope" in columns:
38
+ batch_op.drop_column("continuity_abandonment_scope")
39
+ if "continuity_abandoned_at" in columns:
40
+ batch_op.drop_column("continuity_abandoned_at")
@@ -0,0 +1,22 @@
1
+ """Merge the thread-cache identity and bridge owner-retirement histories.
2
+
3
+ Both revisions chained onto ``20260911_030000_add_local_login_policy`` and
4
+ merged within minutes of each other, forking the graph. Neither is wrong on
5
+ its own, so converge them rather than re-chaining either.
6
+ """
7
+
8
+ revision = "20260912_000000_merge_thread_cache_and_bridge_retirement_heads"
9
+ down_revision = (
10
+ "20260911_040000_add_thread_cache_identity_mode",
11
+ "20260911_060000_add_bridge_session_continuity_abandonment",
12
+ )
13
+ branch_labels = None
14
+ depends_on = None
15
+
16
+
17
+ def upgrade() -> None:
18
+ pass
19
+
20
+
21
+ def downgrade() -> None:
22
+ pass
@@ -0,0 +1,140 @@
1
+ """Drop the legacy dashboard credential columns from ``dashboard_settings``.
2
+
3
+ Revision ID: 20260912_010000_drop_legacy_dashboard_credentials
4
+ Revises: 20260912_000000_merge_thread_cache_and_bridge_retirement_heads
5
+ Create Date: 2026-09-12
6
+
7
+ ``dashboard_users`` has been the only authority for dashboard credentials since
8
+ ``20260909_020000_reproject_compat_admin_credentials`` copied the legacy values
9
+ onto the account row; the three columns below were kept one release longer as a
10
+ write-only projection so a replica of the previous release kept working. That
11
+ projection is removed in this release, so the columns are now an unowned copy of
12
+ a live password hash and an encrypted TOTP secret that no code path clears or
13
+ rotates -- which is why they go in the same change.
14
+
15
+ **This upgrade is not rolling-safe.** Every replica of an earlier release maps
16
+ these columns and loads the settings row as a whole entity, so its settings
17
+ reads (and, on release N, its credential mirror) fail the moment the columns are
18
+ gone. Old replicas must be stopped before this runs, and ``run_upgrade`` says so
19
+ out loud once. It refuses nothing: this revision descends from the reprojection,
20
+ so a database stamped at any older revision re-projects and only then drops, in
21
+ one command.
22
+
23
+ The downgrade re-creates the columns and re-projects them from the account row
24
+ carrying the deterministic compat id -- by id, because after this release the
25
+ bootstrap account's *name* is no longer a stable way to find it.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import sqlalchemy as sa
31
+ from alembic import op
32
+ from sqlalchemy.engine import Connection
33
+
34
+ revision = "20260912_010000_drop_legacy_dashboard_credentials"
35
+ down_revision = "20260912_000000_merge_thread_cache_and_bridge_retirement_heads"
36
+ branch_labels = None
37
+ depends_on = None
38
+
39
+ _TABLE = "dashboard_settings"
40
+ _USERS_TABLE = "dashboard_users"
41
+ _SENTINELS_TABLE = "runtime_sentinels"
42
+
43
+ #: Durable "the legacy credential layer is retired" marker, read by
44
+ #: ``app.db.migrate.run_upgrade`` (which freezes the same literal, because
45
+ #: neither side may import the other).
46
+ #:
47
+ #: The ledger cannot carry this, because the ledger is the thing that goes
48
+ #: wrong: a database whose ledger was lost or rewound has the chain replayed
49
+ #: over it, which re-creates these three columns *empty* a few revisions before
50
+ #: dropping them again -- and the one-shot re-projection in between reads that
51
+ #: emptiness as "the password was removed" and clears the account row, which by
52
+ #: then holds the only credential the install has. A guard inside that
53
+ #: re-projection cannot help: it is a published revision, so an install that
54
+ #: already applied it never applies it again, and editing it would give one
55
+ #: revision id two histories. ``run_upgrade`` sees the marker first and stamps
56
+ #: the ledger at this revision instead of replaying over the schema. Only this
57
+ #: revision writes the marker and only its downgrade removes it, so the marker
58
+ #: means these columns are gone whatever the ledger claims.
59
+ _RETIRED_SENTINEL = "dashboard_legacy_credentials_retired"
60
+
61
+ #: Frozen copy of the runtime identifier (see 20260909_010000): a migration must
62
+ #: not import transient application modules.
63
+ COMPAT_ADMIN_USER_ID = "7a4fc02d-216e-5be7-b974-bc437f23df60"
64
+
65
+ #: The three the projection wrote, and the only three that go. The guest
66
+ #: credential, both bootstrap-token columns, both TOTP requirement flags and
67
+ #: ``local_login_policy`` are live and stay.
68
+ _DROPPED_COLUMN_NAMES: tuple[str, ...] = (
69
+ "password_hash",
70
+ "totp_secret_encrypted",
71
+ "totp_last_verified_step",
72
+ )
73
+
74
+
75
+ def _columns(connection: Connection, table: str) -> set[str]:
76
+ inspector = sa.inspect(connection)
77
+ if not inspector.has_table(table):
78
+ return set()
79
+ return {column["name"] for column in inspector.get_columns(table)}
80
+
81
+
82
+ def _set_retired_marker(bind: Connection, *, retired: bool) -> None:
83
+ if not sa.inspect(bind).has_table(_SENTINELS_TABLE):
84
+ return
85
+ bind.execute(sa.text(f"DELETE FROM {_SENTINELS_TABLE} WHERE name = :name"), {"name": _RETIRED_SENTINEL})
86
+ if retired:
87
+ bind.execute(
88
+ sa.text(f"INSERT INTO {_SENTINELS_TABLE} (name, value) VALUES (:name, :value)"),
89
+ {"name": _RETIRED_SENTINEL, "value": revision},
90
+ )
91
+
92
+
93
+ def upgrade() -> None:
94
+ bind = op.get_bind()
95
+ present = _columns(bind, _TABLE)
96
+ doomed = [name for name in _DROPPED_COLUMN_NAMES if name in present]
97
+ if doomed:
98
+ with op.batch_alter_table(_TABLE) as batch_op:
99
+ for name in doomed:
100
+ batch_op.drop_column(name)
101
+ _set_retired_marker(bind, retired=True)
102
+
103
+
104
+ def downgrade() -> None:
105
+ bind = op.get_bind()
106
+ _set_retired_marker(bind, retired=False)
107
+ present = _columns(bind, _TABLE)
108
+ if present and not {*_DROPPED_COLUMN_NAMES} <= present:
109
+ with op.batch_alter_table(_TABLE) as batch_op:
110
+ if "password_hash" not in present:
111
+ batch_op.add_column(sa.Column("password_hash", sa.Text(), nullable=True))
112
+ if "totp_secret_encrypted" not in present:
113
+ batch_op.add_column(sa.Column("totp_secret_encrypted", sa.LargeBinary(), nullable=True))
114
+ if "totp_last_verified_step" not in present:
115
+ batch_op.add_column(sa.Column("totp_last_verified_step", sa.Integer(), nullable=True))
116
+
117
+ if not _columns(bind, _TABLE) or not _columns(bind, _USERS_TABLE):
118
+ return
119
+ account = bind.execute(
120
+ sa.text(
121
+ f"SELECT password_hash, totp_secret_encrypted, totp_last_verified_step FROM {_USERS_TABLE} WHERE id = :id"
122
+ ),
123
+ {"id": COMPAT_ADMIN_USER_ID},
124
+ ).first()
125
+ if account is None:
126
+ # Nothing deterministic to project from (the account was deleted, or
127
+ # this install never bootstrapped one): leave the columns NULL.
128
+ return
129
+ bind.execute(
130
+ sa.text(
131
+ f"UPDATE {_TABLE} SET password_hash = :password_hash, "
132
+ "totp_secret_encrypted = :totp_secret_encrypted, "
133
+ "totp_last_verified_step = :totp_last_verified_step WHERE id = 1"
134
+ ),
135
+ {
136
+ "password_hash": account[0],
137
+ "totp_secret_encrypted": account[1],
138
+ "totp_last_verified_step": account[2],
139
+ },
140
+ )
@@ -0,0 +1,134 @@
1
+ """Add the OIDC login-flow table, the provider's test-login proof and its seeded row.
2
+
3
+ Revision ID: 20260913_000000_add_oidc_provider_flow
4
+ Revises: 20260912_010000_drop_legacy_dashboard_credentials
5
+ Create Date: 2026-09-13
6
+
7
+ The connection settings themselves need no column: ``config_encrypted`` was
8
+ reserved for them when the providers table landed. What is new is durable
9
+ *flow* state -- the callback lands on whichever replica the load balancer picks,
10
+ so the state, the nonce hash and the PKCE verifier cannot live in memory -- and
11
+ the two columns that record an admin's pre-flight test login.
12
+
13
+ The ``oidc/default`` row is seeded disabled with no role for an unmatched
14
+ identity, through the same insert-ignore helper the other built-in rows use, so
15
+ an install that never connects an identity provider is unchanged and a
16
+ connected one denies unknown identities until an operator says otherwise.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import sqlalchemy as sa
22
+ from alembic import op
23
+
24
+ from app.modules.auth_providers.seed import seed_default_auth_providers
25
+
26
+ revision = "20260913_000000_add_oidc_provider_flow"
27
+ down_revision = "20260912_010000_drop_legacy_dashboard_credentials"
28
+ branch_labels = None
29
+ depends_on = None
30
+
31
+ _PROVIDERS = "dashboard_auth_providers"
32
+ _FLOWS = "dashboard_oidc_login_flows"
33
+ _FLOWS_INDEX = "idx_dashboard_oidc_login_flows_expires_at"
34
+ _PROOF_COLUMNS: tuple[str, ...] = ("test_login_user_id", "test_login_verified_at")
35
+ _PROOF_FK = "fk_dashboard_auth_providers_test_login_user_id"
36
+
37
+
38
+ def _provider_columns() -> tuple[sa.Column, ...]:
39
+ """Fresh Column objects per call: a Column may be bound to one table only."""
40
+
41
+ return (
42
+ sa.Column("id", sa.String(length=36), primary_key=True),
43
+ sa.Column("kind", sa.String(length=32), nullable=False),
44
+ sa.Column("provider_key", sa.String(length=64), nullable=False),
45
+ sa.Column("enabled", sa.Boolean(), server_default=sa.true(), nullable=False),
46
+ sa.Column("label", sa.String(length=64), nullable=False),
47
+ sa.Column("config_encrypted", sa.LargeBinary(), nullable=True),
48
+ sa.Column("unknown_identity_role_id", sa.String(length=36), nullable=True),
49
+ sa.Column("no_match_role_id", sa.String(length=36), nullable=True),
50
+ sa.Column("link_by_email", sa.Boolean(), server_default=sa.false(), nullable=False),
51
+ sa.Column("skip_role_sync", sa.Boolean(), server_default=sa.false(), nullable=False),
52
+ sa.Column("idp_mfa_enforced", sa.Boolean(), server_default=sa.false(), nullable=False),
53
+ sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
54
+ sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
55
+ )
56
+
57
+
58
+ def _seed_table() -> sa.Table:
59
+ """Revision-pinned column list for the seed INSERT (no constraints needed to insert)."""
60
+
61
+ return sa.Table(_PROVIDERS, sa.MetaData(), *_provider_columns())
62
+
63
+
64
+ def _proof_columns() -> tuple[sa.Column, ...]:
65
+ """Fresh Column objects per call: a Column may be bound to one table only."""
66
+
67
+ return (
68
+ sa.Column(
69
+ "test_login_user_id",
70
+ sa.String(length=36),
71
+ # Named because SQLite's batch mode rebuilds the table and refuses
72
+ # to re-add an anonymous constraint.
73
+ sa.ForeignKey("dashboard_users.id", ondelete="SET NULL", name=_PROOF_FK),
74
+ nullable=True,
75
+ ),
76
+ sa.Column("test_login_verified_at", sa.DateTime(timezone=True), nullable=True),
77
+ )
78
+
79
+
80
+ def _existing_columns(inspector: sa.Inspector, table: str) -> set[str]:
81
+ if not inspector.has_table(table):
82
+ return set()
83
+ return {column["name"] for column in inspector.get_columns(table)}
84
+
85
+
86
+ def upgrade() -> None:
87
+ bind = op.get_bind()
88
+ inspector = sa.inspect(bind)
89
+
90
+ existing = _existing_columns(inspector, _PROVIDERS)
91
+ missing = [column for column in _proof_columns() if column.name not in existing]
92
+ if missing:
93
+ with op.batch_alter_table(_PROVIDERS) as batch_op:
94
+ for column in missing:
95
+ batch_op.add_column(column)
96
+
97
+ if not inspector.has_table(_FLOWS):
98
+ # Constraints are declared inline: SQLite cannot add them afterwards.
99
+ op.create_table(
100
+ _FLOWS,
101
+ sa.Column("state_hash", sa.String(length=64), primary_key=True),
102
+ sa.Column("provider_id", sa.String(length=36), nullable=False),
103
+ sa.Column("nonce_hash", sa.String(length=64), nullable=False),
104
+ sa.Column("code_verifier_encrypted", sa.LargeBinary(), nullable=False),
105
+ sa.Column("purpose", sa.String(length=16), nullable=False),
106
+ sa.Column("acting_user_id", sa.String(length=36), nullable=True),
107
+ sa.Column("redirect_uri", sa.String(length=512), nullable=False),
108
+ # The connection document this flow was started against, so a
109
+ # completed pre-flight can only stamp the configuration it proved.
110
+ sa.Column("config_fingerprint", sa.String(length=64), nullable=False),
111
+ sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
112
+ sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
113
+ sa.ForeignKeyConstraint(["provider_id"], [f"{_PROVIDERS}.id"], ondelete="CASCADE"),
114
+ sa.ForeignKeyConstraint(["acting_user_id"], ["dashboard_users.id"], ondelete="CASCADE"),
115
+ )
116
+ op.create_index(_FLOWS_INDEX, _FLOWS, ["expires_at"])
117
+
118
+ # Outside the guards so a re-run after a partial failure still seeds, and
119
+ # insert-ignore so an operator's configuration survives the upgrade.
120
+ if inspector.has_table(_PROVIDERS):
121
+ seed_default_auth_providers(bind, _seed_table())
122
+
123
+
124
+ def downgrade() -> None:
125
+ bind = op.get_bind()
126
+ inspector = sa.inspect(bind)
127
+ if inspector.has_table(_FLOWS):
128
+ op.drop_table(_FLOWS)
129
+ present = _existing_columns(inspector, _PROVIDERS)
130
+ to_drop = [name for name in _PROOF_COLUMNS if name in present]
131
+ if to_drop:
132
+ with op.batch_alter_table(_PROVIDERS) as batch_op:
133
+ for name in to_drop:
134
+ batch_op.drop_column(name)
@@ -0,0 +1,78 @@
1
+ """Add the SCIM bearer-token table and the identity's pushed user name.
2
+
3
+ Revision ID: 20260914_000000_add_scim_tokens
4
+ Revises: 20260913_000000_add_oidc_provider_flow
5
+ Create Date: 2026-09-14
6
+
7
+ ``dashboard_scim_tokens`` holds only the SHA-256 digest of each secret, in a
8
+ unique index, so verification is an index equality and no readable copy of the
9
+ credential survives the response that issued it. ``provider_key`` is the
10
+ identity namespace the token owns; it is read from the row on every request and
11
+ never from the caller.
12
+
13
+ ``dashboard_identities.user_name`` is nullable and written only by the SCIM
14
+ path: our usernames are slugs without ``@``, so a ``userName eq`` filter on the
15
+ identity provider's own spelling can never match one, and an identity provider
16
+ reconciling the resources it created would otherwise find none of them.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import sqlalchemy as sa
22
+ from alembic import op
23
+
24
+ revision = "20260914_000000_add_scim_tokens"
25
+ down_revision = "20260913_000000_add_oidc_provider_flow"
26
+ branch_labels = None
27
+ depends_on = None
28
+
29
+ _TOKENS = "dashboard_scim_tokens"
30
+ _IDENTITIES = "dashboard_identities"
31
+ _USER_NAME_COLUMN = "user_name"
32
+ _CREATED_BY_FK = "fk_dashboard_scim_tokens_created_by_user_id"
33
+
34
+
35
+ def _existing_columns(inspector: sa.Inspector, table: str) -> set[str]:
36
+ if not inspector.has_table(table):
37
+ return set()
38
+ return {column["name"] for column in inspector.get_columns(table)}
39
+
40
+
41
+ def upgrade() -> None:
42
+ bind = op.get_bind()
43
+ inspector = sa.inspect(bind)
44
+
45
+ if not inspector.has_table(_TOKENS):
46
+ # Constraints are declared inline: SQLite cannot add them afterwards.
47
+ op.create_table(
48
+ _TOKENS,
49
+ sa.Column("id", sa.String(length=36), primary_key=True),
50
+ sa.Column("label", sa.String(length=64), nullable=False),
51
+ sa.Column("token_hash", sa.String(length=64), nullable=False, unique=True),
52
+ sa.Column("token_prefix", sa.String(length=32), nullable=False),
53
+ sa.Column("provider_key", sa.String(length=128), nullable=False),
54
+ sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
55
+ sa.Column("created_by_user_id", sa.String(length=36), nullable=True),
56
+ sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
57
+ sa.Column("rotated_at", sa.DateTime(timezone=True), nullable=True),
58
+ sa.ForeignKeyConstraint(
59
+ ["created_by_user_id"],
60
+ ["dashboard_users.id"],
61
+ ondelete="SET NULL",
62
+ name=_CREATED_BY_FK,
63
+ ),
64
+ )
65
+
66
+ if _USER_NAME_COLUMN not in _existing_columns(inspector, _IDENTITIES):
67
+ with op.batch_alter_table(_IDENTITIES) as batch_op:
68
+ batch_op.add_column(sa.Column(_USER_NAME_COLUMN, sa.String(length=256), nullable=True))
69
+
70
+
71
+ def downgrade() -> None:
72
+ bind = op.get_bind()
73
+ inspector = sa.inspect(bind)
74
+ if _USER_NAME_COLUMN in _existing_columns(inspector, _IDENTITIES):
75
+ with op.batch_alter_table(_IDENTITIES) as batch_op:
76
+ batch_op.drop_column(_USER_NAME_COLUMN)
77
+ if inspector.has_table(_TOKENS):
78
+ op.drop_table(_TOKENS)
@@ -0,0 +1,132 @@
1
+ """Drop the subscription-exhaustion overflow schema.
2
+
3
+ The feature was withdrawn (#2123): a 60-body capture sweep of real Codex
4
+ traffic found nothing the portability predicate would ever pass, and making it
5
+ pass would mean translating request bodies per provider. The code went in the
6
+ preceding revert; this revision takes the storage with it.
7
+
8
+ Production never wrote a row -- ``model_source_pins`` was empty, both
9
+ ``dashboard_settings`` columns were NULL -- so the upgrade cannot lose data.
10
+ Every step is guarded so a database missing either object (a partial dump, a
11
+ fresh install created from current metadata) upgrades cleanly. The downgrade
12
+ restores exactly what ``20260908_000000_add_subscription_overflow`` and
13
+ ``20260911_000000_model_source_pins_kind_expires_index`` built, so the pair
14
+ round-trips.
15
+
16
+ **This upgrade is not rolling-safe.** Every release below this one maps both
17
+ ``dashboard_settings`` columns and loads the settings row as a whole entity, so
18
+ its settings reads fail the moment the columns are gone -- and the chart's
19
+ migration Job is a ``pre-upgrade`` hook, which runs before the new pods roll and
20
+ therefore before the old ones drain. Pre-withdrawal replicas must be stopped
21
+ before this runs; ``docs/deployment/kubernetes.md`` carries the procedure. It
22
+ refuses nothing, and unlike ``20260912_010000_drop_legacy_dashboard_credentials``
23
+ it says nothing either: that revision's pre-DDL drain warning in
24
+ ``app/db/migrate.py`` is written around the credential columns it protects.
25
+ """
26
+
27
+ from collections.abc import Sequence
28
+
29
+ import sqlalchemy as sa
30
+ from alembic import op
31
+ from sqlalchemy.engine import Connection
32
+
33
+ revision = "20260914_000000_drop_subscription_overflow_schema"
34
+ down_revision = "20260913_000000_add_oidc_provider_flow"
35
+ branch_labels = None
36
+ depends_on = None
37
+
38
+ _SETTINGS_TABLE = "dashboard_settings"
39
+ _PINS_TABLE = "model_source_pins"
40
+ _PURGE_INDEX = "ix_model_source_pins_purge_at"
41
+ _KIND_EXPIRES_INDEX = "ix_model_source_pins_kind_expires_at"
42
+
43
+
44
+ def _settings_columns() -> tuple[sa.Column, ...]:
45
+ # Fresh Column objects per call: a Column binds to the table it is added
46
+ # to, so module-level instances could not be reused across one process.
47
+ return (
48
+ sa.Column("subscription_overflow_source_id", sa.String(), nullable=True),
49
+ sa.Column("subscription_overflow_drain_until", sa.DateTime(), nullable=True),
50
+ )
51
+
52
+
53
+ def _column_names(connection: Connection, table_name: str) -> set[str]:
54
+ inspector = sa.inspect(connection)
55
+ if not inspector.has_table(table_name):
56
+ return set()
57
+ return {str(column["name"]) for column in inspector.get_columns(table_name) if column.get("name") is not None}
58
+
59
+
60
+ def _index_names(connection: Connection, table_name: str) -> set[str]:
61
+ # A fresh inspector per call, because the caller may have created the table
62
+ # after an earlier reflection filled another inspector's cache.
63
+ inspector = sa.inspect(connection)
64
+ if not inspector.has_table(table_name):
65
+ return set()
66
+ return {str(index["name"]) for index in inspector.get_indexes(table_name) if index.get("name") is not None}
67
+
68
+
69
+ def _add_columns(names: Sequence[sa.Column]) -> None:
70
+ with op.batch_alter_table(_SETTINGS_TABLE) as batch_op:
71
+ for column in names:
72
+ batch_op.add_column(column)
73
+
74
+
75
+ def _drop_columns(names: Sequence[str]) -> None:
76
+ with op.batch_alter_table(_SETTINGS_TABLE) as batch_op:
77
+ for column_name in names:
78
+ batch_op.drop_column(column_name)
79
+
80
+
81
+ def upgrade() -> None:
82
+ bind = op.get_bind()
83
+
84
+ # DROP TABLE takes both indexes with it, so they need no separate step --
85
+ # and dropping them first would only widen the window where the table is
86
+ # unindexed. The table is empty, so the exclusive lock is instant.
87
+ if sa.inspect(bind).has_table(_PINS_TABLE):
88
+ op.drop_table(_PINS_TABLE)
89
+
90
+ existing_columns = _column_names(bind, _SETTINGS_TABLE)
91
+ present = [column.name for column in _settings_columns() if column.name in existing_columns]
92
+ if present:
93
+ _drop_columns(present)
94
+
95
+
96
+ def downgrade() -> None:
97
+ bind = op.get_bind()
98
+
99
+ existing_columns = _column_names(bind, _SETTINGS_TABLE)
100
+ missing = [column for column in _settings_columns() if column.name not in existing_columns]
101
+ if existing_columns and missing:
102
+ _add_columns(missing)
103
+
104
+ if not sa.inspect(bind).has_table(_PINS_TABLE):
105
+ op.create_table(
106
+ _PINS_TABLE,
107
+ sa.Column("pin_key", sa.String(), nullable=False),
108
+ sa.Column("kind", sa.String(), nullable=False),
109
+ # No foreign key, as in the original: rows had to outlive a deleted
110
+ # source for the drain window instead of cascading away.
111
+ sa.Column("source_id", sa.String(), nullable=False),
112
+ sa.Column("api_key_id", sa.String(), nullable=True),
113
+ sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
114
+ sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=False),
115
+ sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
116
+ sa.Column("purge_at", sa.DateTime(timezone=True), nullable=False),
117
+ sa.PrimaryKeyConstraint("pin_key"),
118
+ )
119
+
120
+ # Each index is reflected on its own rather than ridden along with CREATE
121
+ # TABLE, exactly as 20260908_000000_add_subscription_overflow does it: a
122
+ # downgrade interrupted between the table and its indexes -- or a partial
123
+ # restore that already carries the table -- would otherwise land stamped at
124
+ # the parent with neither index, and nothing later would build them.
125
+ # Both tables are empty here, so the plain in-transaction build is right on
126
+ # either dialect; CONCURRENTLY would buy nothing and cannot run in this
127
+ # transaction.
128
+ existing_indexes = _index_names(bind, _PINS_TABLE)
129
+ if _PURGE_INDEX not in existing_indexes:
130
+ op.create_index(_PURGE_INDEX, _PINS_TABLE, ["purge_at"], unique=False)
131
+ if _KIND_EXPIRES_INDEX not in existing_indexes:
132
+ op.create_index(_KIND_EXPIRES_INDEX, _PINS_TABLE, ["kind", "expires_at"], unique=False)
@@ -0,0 +1,17 @@
1
+ """Merge the SCIM token and subscription-overflow migration histories."""
2
+
3
+ revision = "20260918_000000_merge_scim_and_overflow_heads"
4
+ down_revision = (
5
+ "20260914_000000_add_scim_tokens",
6
+ "20260914_000000_drop_subscription_overflow_schema",
7
+ )
8
+ branch_labels = None
9
+ depends_on = None
10
+
11
+
12
+ def upgrade() -> None:
13
+ pass
14
+
15
+
16
+ def downgrade() -> None:
17
+ pass
@@ -0,0 +1 @@
1
+ from __future__ import annotations