omnibase_infra 0.2.6__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 (833) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/__init__.py +1 -0
  8. omnibase_infra/cli/commands.py +216 -0
  9. omnibase_infra/clients/__init__.py +0 -0
  10. omnibase_infra/configs/widget_mapping.yaml +176 -0
  11. omnibase_infra/constants_topic_patterns.py +26 -0
  12. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
  13. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
  14. omnibase_infra/decorators/__init__.py +29 -0
  15. omnibase_infra/decorators/allow_any.py +109 -0
  16. omnibase_infra/dlq/__init__.py +90 -0
  17. omnibase_infra/dlq/constants_dlq.py +57 -0
  18. omnibase_infra/dlq/models/__init__.py +26 -0
  19. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  20. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  21. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  22. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  23. omnibase_infra/enums/__init__.py +132 -0
  24. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  25. omnibase_infra/enums/enum_backend_type.py +27 -0
  26. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  27. omnibase_infra/enums/enum_capture_state.py +88 -0
  28. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  29. omnibase_infra/enums/enum_circuit_state.py +51 -0
  30. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  31. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  32. omnibase_infra/enums/enum_contract_type.py +84 -0
  33. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  34. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  35. omnibase_infra/enums/enum_environment.py +46 -0
  36. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  37. omnibase_infra/enums/enum_handler_error_type.py +111 -0
  38. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  39. omnibase_infra/enums/enum_handler_source_mode.py +86 -0
  40. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  41. omnibase_infra/enums/enum_handler_type.py +77 -0
  42. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  43. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  44. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  45. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  46. omnibase_infra/enums/enum_message_category.py +213 -0
  47. omnibase_infra/enums/enum_node_archetype.py +74 -0
  48. omnibase_infra/enums/enum_node_output_type.py +185 -0
  49. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  50. omnibase_infra/enums/enum_policy_type.py +32 -0
  51. omnibase_infra/enums/enum_registration_state.py +261 -0
  52. omnibase_infra/enums/enum_registration_status.py +33 -0
  53. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  54. omnibase_infra/enums/enum_response_status.py +26 -0
  55. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  56. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  57. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  58. omnibase_infra/enums/enum_topic_standard.py +42 -0
  59. omnibase_infra/enums/enum_validation_severity.py +78 -0
  60. omnibase_infra/errors/__init__.py +160 -0
  61. omnibase_infra/errors/error_architecture_violation.py +152 -0
  62. omnibase_infra/errors/error_binding_resolution.py +128 -0
  63. omnibase_infra/errors/error_chain_propagation.py +188 -0
  64. omnibase_infra/errors/error_compute_registry.py +95 -0
  65. omnibase_infra/errors/error_consul.py +132 -0
  66. omnibase_infra/errors/error_container_wiring.py +243 -0
  67. omnibase_infra/errors/error_event_bus_registry.py +105 -0
  68. omnibase_infra/errors/error_infra.py +610 -0
  69. omnibase_infra/errors/error_message_type_registry.py +101 -0
  70. omnibase_infra/errors/error_policy_registry.py +115 -0
  71. omnibase_infra/errors/error_vault.py +123 -0
  72. omnibase_infra/event_bus/__init__.py +72 -0
  73. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
  74. omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
  75. omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
  76. omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
  77. omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
  78. omnibase_infra/event_bus/models/__init__.py +29 -0
  79. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  80. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
  81. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  82. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  83. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  84. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  85. omnibase_infra/event_bus/testing/__init__.py +26 -0
  86. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  87. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  88. omnibase_infra/event_bus/topic_constants.py +376 -0
  89. omnibase_infra/handlers/__init__.py +82 -0
  90. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  91. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  92. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  93. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  94. omnibase_infra/handlers/handler_consul.py +795 -0
  95. omnibase_infra/handlers/handler_db.py +1046 -0
  96. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  97. omnibase_infra/handlers/handler_graph.py +2015 -0
  98. omnibase_infra/handlers/handler_http.py +926 -0
  99. omnibase_infra/handlers/handler_intent.py +387 -0
  100. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  101. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  102. omnibase_infra/handlers/handler_mcp.py +1430 -0
  103. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  104. omnibase_infra/handlers/handler_vault.py +428 -0
  105. omnibase_infra/handlers/mcp/__init__.py +19 -0
  106. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  107. omnibase_infra/handlers/mcp/protocols.py +178 -0
  108. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  109. omnibase_infra/handlers/mixins/__init__.py +47 -0
  110. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  111. omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
  112. omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
  113. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  114. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  115. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  116. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  117. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  118. omnibase_infra/handlers/models/__init__.py +286 -0
  119. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  120. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  121. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  122. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  123. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  124. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  125. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  126. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  127. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  128. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  129. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  130. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  131. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  132. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  133. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  134. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  135. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  136. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  137. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  138. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  139. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  140. omnibase_infra/handlers/models/http/__init__.py +50 -0
  141. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  142. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  143. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  144. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  145. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  146. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  147. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  148. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  149. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  150. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  151. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  152. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  153. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  154. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  155. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  156. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  157. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  158. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  159. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  160. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  161. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  162. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  163. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  164. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  165. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  166. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  167. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  168. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  169. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  170. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  171. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  172. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  173. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  174. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  175. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  176. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  177. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  178. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  179. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  180. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  181. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  182. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  183. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  184. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  185. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  186. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  187. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  188. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  189. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  190. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  191. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  192. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  193. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  194. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  195. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  196. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  197. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  198. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  199. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  200. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  201. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  202. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  203. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  204. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  205. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  206. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  207. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
  208. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  209. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  210. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  211. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  212. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  213. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
  214. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  215. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  216. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  217. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  218. omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
  219. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  220. omnibase_infra/idempotency/__init__.py +94 -0
  221. omnibase_infra/idempotency/models/__init__.py +43 -0
  222. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  223. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  224. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  225. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  226. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  227. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  228. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  229. omnibase_infra/idempotency/store_inmemory.py +265 -0
  230. omnibase_infra/idempotency/store_postgres.py +923 -0
  231. omnibase_infra/infrastructure/__init__.py +0 -0
  232. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  233. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  234. omnibase_infra/mixins/__init__.py +71 -0
  235. omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
  236. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  237. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  238. omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
  239. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  240. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  241. omnibase_infra/models/__init__.py +144 -0
  242. omnibase_infra/models/bindings/__init__.py +59 -0
  243. omnibase_infra/models/bindings/constants.py +144 -0
  244. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  245. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  246. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  247. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  248. omnibase_infra/models/corpus/__init__.py +17 -0
  249. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  250. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  251. omnibase_infra/models/discovery/__init__.py +42 -0
  252. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  253. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  254. omnibase_infra/models/discovery/model_introspection_config.py +330 -0
  255. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  256. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  257. omnibase_infra/models/dispatch/__init__.py +155 -0
  258. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  259. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  260. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  261. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  262. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  263. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  264. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  265. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  266. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  267. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  268. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  269. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  270. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  271. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  272. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  273. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  274. omnibase_infra/models/errors/__init__.py +45 -0
  275. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  276. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  277. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  278. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  279. omnibase_infra/models/handlers/__init__.py +80 -0
  280. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  281. omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
  282. omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
  283. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  284. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  285. omnibase_infra/models/health/__init__.py +9 -0
  286. omnibase_infra/models/health/model_health_check_result.py +40 -0
  287. omnibase_infra/models/lifecycle/__init__.py +39 -0
  288. omnibase_infra/models/logging/__init__.py +51 -0
  289. omnibase_infra/models/logging/model_log_context.py +756 -0
  290. omnibase_infra/models/mcp/__init__.py +15 -0
  291. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  292. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  293. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  294. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  295. omnibase_infra/models/model_node_identity.py +126 -0
  296. omnibase_infra/models/model_retry_error_classification.py +78 -0
  297. omnibase_infra/models/projection/__init__.py +43 -0
  298. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  299. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  300. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  301. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  302. omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
  303. omnibase_infra/models/projectors/__init__.py +41 -0
  304. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  305. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  306. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  307. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  308. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  309. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  310. omnibase_infra/models/registration/__init__.py +68 -0
  311. omnibase_infra/models/registration/commands/__init__.py +15 -0
  312. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  313. omnibase_infra/models/registration/events/__init__.py +56 -0
  314. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  315. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  316. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  317. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  318. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  319. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  320. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  321. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  322. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  323. omnibase_infra/models/registration/model_node_capabilities.py +190 -0
  324. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  325. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  326. omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
  327. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  328. omnibase_infra/models/registration/model_node_registration.py +162 -0
  329. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  330. omnibase_infra/models/registry/__init__.py +29 -0
  331. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  332. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  333. omnibase_infra/models/resilience/__init__.py +9 -0
  334. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  335. omnibase_infra/models/routing/__init__.py +25 -0
  336. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  337. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  338. omnibase_infra/models/runtime/__init__.py +49 -0
  339. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  340. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  341. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  342. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  343. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  344. omnibase_infra/models/runtime/model_handler_contract.py +296 -0
  345. omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
  346. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  347. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  348. omnibase_infra/models/security/__init__.py +50 -0
  349. omnibase_infra/models/security/classification_levels.py +99 -0
  350. omnibase_infra/models/security/model_environment_policy.py +145 -0
  351. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  352. omnibase_infra/models/security/model_security_error.py +81 -0
  353. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  354. omnibase_infra/models/security/model_security_warning.py +67 -0
  355. omnibase_infra/models/snapshot/__init__.py +27 -0
  356. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  357. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  358. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  359. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  360. omnibase_infra/models/types/__init__.py +71 -0
  361. omnibase_infra/models/validation/__init__.py +89 -0
  362. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  363. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  364. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  365. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  366. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  367. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  368. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  369. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  370. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  371. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  372. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  373. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  374. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  375. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  376. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  377. omnibase_infra/nodes/__init__.py +57 -0
  378. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  379. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  380. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
  381. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  382. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  383. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  384. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  385. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  386. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  387. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  388. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  389. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  390. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  391. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  392. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  393. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  394. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  395. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
  396. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  397. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  398. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  399. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  400. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  401. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  402. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  403. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  404. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  405. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  406. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  407. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  408. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  409. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  410. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  411. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  412. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  413. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  414. omnibase_infra/nodes/effects/README.md +358 -0
  415. omnibase_infra/nodes/effects/__init__.py +26 -0
  416. omnibase_infra/nodes/effects/contract.yaml +167 -0
  417. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  418. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  419. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  420. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  421. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  422. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  423. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  424. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  425. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  426. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  427. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  428. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  429. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  430. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  431. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  432. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  433. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  434. omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
  435. omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
  436. omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
  437. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
  438. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
  439. omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
  440. omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
  441. omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
  442. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  443. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  444. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  445. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  446. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  447. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  448. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  449. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  450. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  451. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  452. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  453. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  454. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  455. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  456. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  457. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  458. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  459. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  460. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  461. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  462. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  463. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  464. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
  465. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  466. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  467. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  468. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  469. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  470. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  471. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
  472. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  473. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  474. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  475. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  476. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  477. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  478. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  479. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  480. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  481. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  482. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  483. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  484. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  485. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  486. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  487. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  488. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  489. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  490. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  491. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  492. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  493. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
  494. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
  495. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
  496. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  497. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  498. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  499. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  500. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  501. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  502. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  503. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  504. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
  505. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  506. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  507. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  508. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  509. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  510. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  511. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  512. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  513. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  514. omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
  515. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  516. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  517. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  518. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
  519. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  520. omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
  521. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  522. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  523. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  524. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
  525. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  526. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  527. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  528. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  529. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  530. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  531. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  532. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  533. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  534. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  535. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  536. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  537. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  538. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  539. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  540. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  541. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  542. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  543. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  544. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  545. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  546. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  547. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  548. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  549. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  550. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
  551. omnibase_infra/nodes/reducers/__init__.py +30 -0
  552. omnibase_infra/nodes/reducers/models/__init__.py +37 -0
  553. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
  554. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  555. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  556. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  557. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  558. omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
  559. omnibase_infra/observability/__init__.py +143 -0
  560. omnibase_infra/observability/constants_metrics.py +91 -0
  561. omnibase_infra/observability/factory_observability_sink.py +525 -0
  562. omnibase_infra/observability/handlers/__init__.py +118 -0
  563. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  564. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  565. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  566. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  567. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  568. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  569. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  570. omnibase_infra/observability/hooks/__init__.py +74 -0
  571. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  572. omnibase_infra/observability/models/__init__.py +30 -0
  573. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  574. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  575. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  576. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  577. omnibase_infra/observability/sinks/__init__.py +69 -0
  578. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  579. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  580. omnibase_infra/plugins/__init__.py +27 -0
  581. omnibase_infra/plugins/examples/__init__.py +28 -0
  582. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  583. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  584. omnibase_infra/plugins/models/__init__.py +21 -0
  585. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  586. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  587. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  588. omnibase_infra/plugins/plugin_compute_base.py +449 -0
  589. omnibase_infra/projectors/__init__.py +30 -0
  590. omnibase_infra/projectors/contracts/__init__.py +63 -0
  591. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  592. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  593. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  594. omnibase_infra/protocols/__init__.py +104 -0
  595. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  596. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  597. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  598. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  599. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  600. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  601. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  602. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  603. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  604. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  605. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  606. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  607. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  608. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  609. omnibase_infra/runtime/__init__.py +445 -0
  610. omnibase_infra/runtime/binding_config_resolver.py +2771 -0
  611. omnibase_infra/runtime/binding_resolver.py +753 -0
  612. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  613. omnibase_infra/runtime/constants_notification.py +75 -0
  614. omnibase_infra/runtime/constants_security.py +70 -0
  615. omnibase_infra/runtime/contract_handler_discovery.py +587 -0
  616. omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
  617. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  618. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  619. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  620. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  621. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  622. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  623. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  624. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  625. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  626. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  627. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  628. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  629. omnibase_infra/runtime/enums/__init__.py +18 -0
  630. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  631. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  632. omnibase_infra/runtime/envelope_validator.py +179 -0
  633. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  634. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  635. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  636. omnibase_infra/runtime/handler_contract_source.py +750 -0
  637. omnibase_infra/runtime/handler_identity.py +81 -0
  638. omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
  639. omnibase_infra/runtime/handler_registry.py +329 -0
  640. omnibase_infra/runtime/handler_source_resolver.py +367 -0
  641. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  642. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  643. omnibase_infra/runtime/kernel.py +40 -0
  644. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  645. omnibase_infra/runtime/mixin_semver_cache.py +402 -0
  646. omnibase_infra/runtime/mixins/__init__.py +24 -0
  647. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  648. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
  649. omnibase_infra/runtime/models/__init__.py +229 -0
  650. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  651. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  652. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  653. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  654. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  655. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  656. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  657. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  658. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  659. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  660. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  661. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  662. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  663. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  664. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  665. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  666. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  667. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  668. omnibase_infra/runtime/models/model_health_check_result.py +229 -0
  669. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  670. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  671. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  672. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  673. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  674. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  675. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  676. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  677. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  678. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  679. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  680. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  681. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  682. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  683. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  684. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  685. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
  686. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  687. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  688. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  689. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  690. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  691. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  692. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  693. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  694. omnibase_infra/runtime/models/model_security_config.py +109 -0
  695. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  696. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  697. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  698. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  699. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  700. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  701. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  702. omnibase_infra/runtime/projector_shell.py +1330 -0
  703. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  704. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  705. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  706. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  707. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  708. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  709. omnibase_infra/runtime/protocol_policy.py +366 -0
  710. omnibase_infra/runtime/protocols/__init__.py +37 -0
  711. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  712. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  713. omnibase_infra/runtime/registry/__init__.py +93 -0
  714. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  715. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  716. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  717. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  718. omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
  719. omnibase_infra/runtime/registry_compute.py +1143 -0
  720. omnibase_infra/runtime/registry_contract_source.py +693 -0
  721. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  722. omnibase_infra/runtime/registry_policy.py +1185 -0
  723. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  724. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  725. omnibase_infra/runtime/secret_resolver.py +2112 -0
  726. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  727. omnibase_infra/runtime/service_kernel.py +1651 -0
  728. omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
  729. omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
  730. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  731. omnibase_infra/runtime/transition_notification_publisher.py +765 -0
  732. omnibase_infra/runtime/util_container_wiring.py +1124 -0
  733. omnibase_infra/runtime/util_validation.py +314 -0
  734. omnibase_infra/runtime/util_version.py +98 -0
  735. omnibase_infra/runtime/util_wiring.py +723 -0
  736. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  737. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  738. omnibase_infra/services/__init__.py +89 -0
  739. omnibase_infra/services/corpus_capture.py +684 -0
  740. omnibase_infra/services/mcp/__init__.py +31 -0
  741. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  742. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  743. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  744. omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
  745. omnibase_infra/services/registry_api/__init__.py +40 -0
  746. omnibase_infra/services/registry_api/main.py +261 -0
  747. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  748. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  749. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  750. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  751. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  752. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  753. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  754. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  755. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  756. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  757. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  758. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  759. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  760. omnibase_infra/services/registry_api/routes.py +371 -0
  761. omnibase_infra/services/registry_api/service.py +837 -0
  762. omnibase_infra/services/service_capability_query.py +945 -0
  763. omnibase_infra/services/service_health.py +898 -0
  764. omnibase_infra/services/service_node_selector.py +530 -0
  765. omnibase_infra/services/service_timeout_emitter.py +699 -0
  766. omnibase_infra/services/service_timeout_scanner.py +394 -0
  767. omnibase_infra/services/session/__init__.py +56 -0
  768. omnibase_infra/services/session/config_consumer.py +137 -0
  769. omnibase_infra/services/session/config_store.py +139 -0
  770. omnibase_infra/services/session/consumer.py +1007 -0
  771. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  772. omnibase_infra/services/session/store.py +997 -0
  773. omnibase_infra/services/snapshot/__init__.py +31 -0
  774. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  775. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  776. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  777. omnibase_infra/shared/__init__.py +8 -0
  778. omnibase_infra/testing/__init__.py +10 -0
  779. omnibase_infra/testing/utils.py +23 -0
  780. omnibase_infra/topics/__init__.py +45 -0
  781. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  782. omnibase_infra/topics/util_topic_composition.py +95 -0
  783. omnibase_infra/types/__init__.py +48 -0
  784. omnibase_infra/types/type_cache_info.py +49 -0
  785. omnibase_infra/types/type_dsn.py +173 -0
  786. omnibase_infra/types/type_infra_aliases.py +60 -0
  787. omnibase_infra/types/typed_dict/__init__.py +29 -0
  788. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  789. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  790. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  791. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  792. omnibase_infra/utils/__init__.py +117 -0
  793. omnibase_infra/utils/correlation.py +208 -0
  794. omnibase_infra/utils/util_atomic_file.py +261 -0
  795. omnibase_infra/utils/util_consumer_group.py +232 -0
  796. omnibase_infra/utils/util_datetime.py +372 -0
  797. omnibase_infra/utils/util_db_transaction.py +239 -0
  798. omnibase_infra/utils/util_dsn_validation.py +333 -0
  799. omnibase_infra/utils/util_env_parsing.py +264 -0
  800. omnibase_infra/utils/util_error_sanitization.py +457 -0
  801. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  802. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  803. omnibase_infra/utils/util_semver.py +233 -0
  804. omnibase_infra/validation/__init__.py +307 -0
  805. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  806. omnibase_infra/validation/enums/__init__.py +11 -0
  807. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  808. omnibase_infra/validation/infra_validators.py +1514 -0
  809. omnibase_infra/validation/linter_contract.py +907 -0
  810. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  811. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  812. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  813. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  814. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  815. omnibase_infra/validation/models/__init__.py +15 -0
  816. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  817. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  818. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  819. omnibase_infra/validation/validation_exemptions.yaml +2033 -0
  820. omnibase_infra/validation/validator_any_type.py +715 -0
  821. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  822. omnibase_infra/validation/validator_execution_shape.py +465 -0
  823. omnibase_infra/validation/validator_localhandler.py +261 -0
  824. omnibase_infra/validation/validator_registration_security.py +410 -0
  825. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  826. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  827. omnibase_infra/validation/validator_security.py +513 -0
  828. omnibase_infra/validation/validator_topic_category.py +1152 -0
  829. omnibase_infra-0.2.6.dist-info/METADATA +197 -0
  830. omnibase_infra-0.2.6.dist-info/RECORD +833 -0
  831. omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
  832. omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
  833. omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1120 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Prometheus metrics handler - EFFECT handler exposing /metrics HTTP endpoint.
4
+
5
+ This module provides an EFFECT handler that exposes Prometheus metrics via an
6
+ HTTP endpoint for scraping. It follows the ONEX handler pattern with contract-
7
+ driven lifecycle management.
8
+
9
+ Architecture Principle: "Handlers own lifecycle, sinks own hot path"
10
+ - Handler: Contract-driven lifecycle, expose /metrics HTTP endpoint
11
+ - Sink: Fast in-process emission (SinkMetricsPrometheus)
12
+
13
+ Supported Operations:
14
+ - metrics.scrape: Return current metrics in Prometheus text format
15
+ - metrics.push: Push metrics to Prometheus Pushgateway (if configured)
16
+
17
+ HTTP Server:
18
+ The handler starts an aiohttp-based HTTP server during initialize() that
19
+ serves metrics at the configured path (default: /metrics on port 9090).
20
+ The server is non-blocking and runs in the background.
21
+
22
+ **SECURE BY DEFAULT**: The server binds to localhost (127.0.0.1) by default.
23
+ External access requires explicit configuration AND reverse proxy protection.
24
+
25
+ Thread-Safety:
26
+ The handler is thread-safe. The underlying SinkMetricsPrometheus uses
27
+ thread-safe metric caches with locking. The aiohttp server handles
28
+ concurrent requests safely.
29
+
30
+ Security Model:
31
+ **Secure Defaults:**
32
+ - Binds to localhost (127.0.0.1) by default - NOT accessible externally
33
+ - Request body size limited to 1MB (configurable via max_request_size_bytes)
34
+ - Request timeout of 30 seconds (configurable via request_timeout_seconds)
35
+ - Error responses use generic messages - no stack traces or internal details
36
+
37
+ **Production Deployment Requirements:**
38
+
39
+ To expose metrics externally, you MUST:
40
+
41
+ 1. **Explicit Configuration**: Set host="0.0.0.0" explicitly (a warning will
42
+ be logged when binding to non-localhost addresses).
43
+
44
+ 2. **Reverse Proxy**: Deploy behind nginx/traefik/envoy with:
45
+ - TLS termination (HTTPS only)
46
+ - IP allowlisting for Prometheus scrapers
47
+ - Rate limiting to prevent DoS (NOT implemented in this handler)
48
+ - Authentication (mTLS or bearer tokens recommended)
49
+
50
+ 3. **Network Isolation**: Consider using internal network CIDRs for
51
+ container/pod networks instead of 0.0.0.0.
52
+
53
+ **Built-in Protections:**
54
+ - Input Validation: X-Correlation-ID headers validated as UUID format
55
+ - Error Sanitization: 500/503 errors return generic messages only
56
+ - Request Size Limits: Configurable max_request_size_bytes (default 1MB)
57
+ - Request Timeouts: Configurable request_timeout_seconds (default 30s)
58
+
59
+ **NOT Implemented (Requires Reverse Proxy):**
60
+ - Rate limiting: Use nginx/traefik/envoy limit_req/rate_limit
61
+ - TLS/HTTPS: Use reverse proxy for TLS termination
62
+ - Authentication: Use reverse proxy for mTLS or token auth
63
+
64
+ **Metric Labels**: Ensure metric labels do not contain:
65
+ - PII (personally identifiable information)
66
+ - Secrets or credentials
67
+ - Internal hostnames/IPs (if sensitive)
68
+
69
+ **Threat Model:**
70
+ - DoS via metric scraping: Mitigate with rate limiting at proxy
71
+ - Information disclosure: Metrics may reveal system topology
72
+ - Header injection: X-Correlation-ID is validated and sanitized
73
+ - Request body attacks: Limited by max_request_size_bytes
74
+
75
+ Usage:
76
+ >>> from omnibase_infra.observability.handlers import HandlerMetricsPrometheus
77
+ >>>
78
+ >>> # Secure default: localhost only
79
+ >>> handler = HandlerMetricsPrometheus()
80
+ >>> await handler.initialize({"port": 9090, "path": "/metrics"})
81
+ >>> # Metrics available at http://127.0.0.1:9090/metrics (localhost only)
82
+ >>>
83
+ >>> # External access (REQUIRES reverse proxy protection)
84
+ >>> handler = HandlerMetricsPrometheus()
85
+ >>> await handler.initialize({
86
+ ... "host": "0.0.0.0", # WARNING: Use only with reverse proxy
87
+ ... "port": 9090,
88
+ ... "path": "/metrics",
89
+ ... })
90
+ >>>
91
+ >>> await handler.shutdown()
92
+
93
+ See Also:
94
+ - SinkMetricsPrometheus: Hot-path metrics sink
95
+ - ModelMetricsHandlerConfig: Configuration model
96
+ - docs/patterns/observability_patterns.md: Full observability documentation
97
+ """
98
+
99
+ from __future__ import annotations
100
+
101
+ import asyncio
102
+ import logging
103
+ import re
104
+ import threading
105
+ import time
106
+ from datetime import UTC, datetime
107
+ from uuid import UUID, uuid4
108
+
109
+ # Optional dependencies with graceful degradation
110
+ # These are checked during initialization with clear error messages
111
+ _PROMETHEUS_AVAILABLE: bool = False
112
+ _AIOHTTP_AVAILABLE: bool = False
113
+
114
+ try:
115
+ from aiohttp import web
116
+
117
+ _AIOHTTP_AVAILABLE = True
118
+ except ImportError:
119
+ web = None # type: ignore[assignment, misc]
120
+
121
+ try:
122
+ from prometheus_client import CONTENT_TYPE_LATEST, Histogram, generate_latest
123
+
124
+ _PROMETHEUS_AVAILABLE = True
125
+ except ImportError:
126
+ # Provide stubs for type checking when prometheus_client is not installed
127
+ CONTENT_TYPE_LATEST = "text/plain; version=0.0.4; charset=utf-8"
128
+ Histogram = None # type: ignore[assignment, misc]
129
+
130
+ def generate_latest() -> bytes: # type: ignore[misc]
131
+ """Stub for when prometheus_client is not installed."""
132
+ raise ImportError("prometheus_client is required but not installed")
133
+
134
+
135
+ from omnibase_core.models.dispatch import ModelHandlerOutput
136
+ from omnibase_infra.enums import (
137
+ EnumHandlerType,
138
+ EnumHandlerTypeCategory,
139
+ EnumInfraTransportType,
140
+ EnumResponseStatus,
141
+ )
142
+ from omnibase_infra.errors import (
143
+ InfraTimeoutError,
144
+ ModelInfraErrorContext,
145
+ ModelTimeoutErrorContext,
146
+ ProtocolConfigurationError,
147
+ RuntimeHostError,
148
+ )
149
+ from omnibase_infra.mixins import MixinEnvelopeExtraction
150
+ from omnibase_infra.observability.handlers.model_metrics_handler_config import (
151
+ ModelMetricsHandlerConfig,
152
+ )
153
+ from omnibase_infra.observability.handlers.model_metrics_handler_response import (
154
+ ModelMetricsHandlerPayload,
155
+ ModelMetricsHandlerResponse,
156
+ )
157
+
158
+ logger = logging.getLogger(__name__)
159
+
160
+ # Handler ID for ModelHandlerOutput
161
+ HANDLER_ID_METRICS: str = "metrics-prometheus-handler"
162
+
163
+ # Security: UUID validation regex for X-Correlation-ID header
164
+ # Matches standard UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
165
+ _UUID_REGEX: re.Pattern[str] = re.compile(
166
+ r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
167
+ )
168
+
169
+ # Maximum length for correlation ID header to prevent memory issues
170
+ _MAX_CORRELATION_ID_LENGTH: int = 64
171
+
172
+ # Default timeout for metrics generation to prevent blocking (seconds)
173
+ # This is used when config is not yet loaded or as internal timeout
174
+ _DEFAULT_METRICS_GENERATION_TIMEOUT: float = 5.0
175
+
176
+ # Timeout for Pushgateway operations (seconds)
177
+ _PUSH_GATEWAY_TIMEOUT: float = 10.0
178
+
179
+ # Default maximum request body size (1MB) - used when config not loaded
180
+ _DEFAULT_MAX_REQUEST_SIZE_BYTES: int = 1048576
181
+
182
+ # Import scrape duration buckets from constants module (ONEX pattern: constants in constants_*.py)
183
+ # See constants_metrics.py for bucket configuration documentation.
184
+ from omnibase_infra.observability.constants_metrics import SCRAPE_DURATION_BUCKETS
185
+
186
+ _SCRAPE_DURATION_BUCKETS = SCRAPE_DURATION_BUCKETS
187
+
188
+ # Lazily initialized scrape duration histogram
189
+ # Initialized on first use to avoid import-time side effects when prometheus_client
190
+ # is not installed. The metric is observed AFTER generate_latest() returns, so the
191
+ # duration value is available in the NEXT scrape (avoiding chicken-and-egg recursion).
192
+ _scrape_duration_histogram: Histogram | None = None
193
+
194
+ # Lock for thread-safe lazy initialization of the scrape duration histogram.
195
+ # Prevents race conditions where multiple threads could create duplicate histograms.
196
+ _histogram_init_lock: threading.Lock = threading.Lock()
197
+
198
+
199
+ def _get_scrape_duration_histogram() -> Histogram | None:
200
+ """Get or create the scrape duration histogram.
201
+
202
+ Lazily initializes the histogram on first call to avoid import-time errors
203
+ when prometheus_client is not installed.
204
+
205
+ Thread Safety:
206
+ Uses double-checked locking pattern to ensure thread-safe initialization.
207
+ The lock is only acquired when the histogram needs to be created, minimizing
208
+ contention after initial setup.
209
+
210
+ Returns:
211
+ Histogram metric for scrape duration, or None if prometheus_client unavailable.
212
+ """
213
+ global _scrape_duration_histogram # noqa: PLW0603 - Module-level metric cache
214
+
215
+ if not _PROMETHEUS_AVAILABLE or Histogram is None:
216
+ return None
217
+
218
+ # Double-checked locking pattern for thread-safe lazy initialization.
219
+ # First check without lock (fast path for already-initialized case).
220
+ if _scrape_duration_histogram is None:
221
+ with _histogram_init_lock:
222
+ # Second check with lock (handles race condition where multiple threads
223
+ # passed the first check before one acquired the lock and initialized).
224
+ if _scrape_duration_histogram is None:
225
+ _scrape_duration_histogram = Histogram(
226
+ "prometheus_handler_scrape_duration_seconds",
227
+ "Time spent generating Prometheus metrics for scrape requests",
228
+ buckets=_SCRAPE_DURATION_BUCKETS,
229
+ )
230
+
231
+ return _scrape_duration_histogram
232
+
233
+
234
+ SUPPORTED_OPERATIONS: frozenset[str] = frozenset(
235
+ {
236
+ "metrics.scrape",
237
+ "metrics.push",
238
+ }
239
+ )
240
+
241
+
242
+ class HandlerMetricsPrometheus(MixinEnvelopeExtraction):
243
+ """Prometheus metrics EFFECT handler exposing HTTP /metrics endpoint.
244
+
245
+ This handler implements the ONEX handler protocol for exposing Prometheus
246
+ metrics via an HTTP endpoint. It manages the lifecycle of an aiohttp HTTP
247
+ server that serves metrics in Prometheus text exposition format.
248
+
249
+ Handler Classification:
250
+ - handler_type: INFRA_HANDLER (protocol/transport handler)
251
+ - handler_category: EFFECT (side-effecting I/O - HTTP server)
252
+
253
+ Lifecycle:
254
+ - initialize(): Validates config, starts HTTP server
255
+ - execute(): Returns metrics text or pushes to Pushgateway
256
+ - shutdown(): Gracefully stops HTTP server
257
+
258
+ HTTP Server Integration:
259
+ The handler uses aiohttp for async HTTP serving. The server runs in the
260
+ background without blocking the event loop. Multiple concurrent scrapes
261
+ are handled safely by aiohttp's request handling.
262
+
263
+ Push Gateway Support:
264
+ When push_gateway_url is configured, the handler can push metrics to a
265
+ Prometheus Pushgateway. This is useful for short-lived batch jobs that
266
+ may not live long enough to be scraped.
267
+
268
+ Security:
269
+ **SECURE BY DEFAULT**: The server binds to localhost (127.0.0.1) by default.
270
+
271
+ Built-in protections:
272
+ - Localhost binding by default (external access requires explicit config)
273
+ - Request size limits (configurable, default 1MB)
274
+ - Request timeouts (configurable, default 30s)
275
+ - Generic error messages (no stack traces or internal details)
276
+ - Correlation ID validation (UUID format enforced)
277
+
278
+ For external access, you MUST:
279
+ 1. Explicitly set host="0.0.0.0" (a warning will be logged)
280
+ 2. Deploy behind a reverse proxy with TLS, auth, and rate limiting
281
+
282
+ See module docstring for complete security model.
283
+
284
+ Attributes:
285
+ _config: Handler configuration.
286
+ _initialized: Whether the handler has been initialized.
287
+ _server: aiohttp web server instance.
288
+ _runner: aiohttp application runner.
289
+ _site: aiohttp TCP site for serving requests.
290
+
291
+ Example:
292
+ >>> # Secure default: localhost only
293
+ >>> handler = HandlerMetricsPrometheus()
294
+ >>> await handler.initialize({"port": 9090})
295
+ >>> # Metrics at http://127.0.0.1:9090/metrics (localhost only)
296
+ >>>
297
+ >>> # External access (REQUIRES reverse proxy)
298
+ >>> handler = HandlerMetricsPrometheus()
299
+ >>> await handler.initialize({
300
+ ... "host": "0.0.0.0", # WARNING: Use only with reverse proxy
301
+ ... "port": 9090,
302
+ ... })
303
+ >>> await handler.shutdown()
304
+ """
305
+
306
+ def __init__(self) -> None:
307
+ """Initialize HandlerMetricsPrometheus in uninitialized state.
308
+
309
+ The handler is not ready for use until initialize() is called with
310
+ a valid configuration dictionary.
311
+ """
312
+ self._config: ModelMetricsHandlerConfig | None = None
313
+ self._initialized: bool = False
314
+ self._app: web.Application | None = None
315
+ self._runner: web.AppRunner | None = None
316
+ self._site: web.TCPSite | None = None
317
+
318
+ @property
319
+ def handler_type(self) -> EnumHandlerType:
320
+ """Return the architectural role of this handler.
321
+
322
+ Returns:
323
+ EnumHandlerType.INFRA_HANDLER - This handler is an infrastructure
324
+ protocol/transport handler that manages an HTTP server for metrics
325
+ exposition.
326
+
327
+ Note:
328
+ handler_type determines lifecycle, protocol selection, and runtime
329
+ invocation patterns. It answers "what is this handler in the architecture?"
330
+
331
+ See Also:
332
+ - handler_category: Behavioral classification (EFFECT/COMPUTE)
333
+ """
334
+ return EnumHandlerType.INFRA_HANDLER
335
+
336
+ @property
337
+ def handler_category(self) -> EnumHandlerTypeCategory:
338
+ """Return the behavioral classification of this handler.
339
+
340
+ Returns:
341
+ EnumHandlerTypeCategory.EFFECT - This handler performs side-effecting
342
+ I/O operations (HTTP server, network I/O). EFFECT handlers are not
343
+ deterministic and interact with external systems.
344
+
345
+ Note:
346
+ handler_category determines security rules, determinism guarantees,
347
+ replay safety, and permissions. It answers "how does this handler
348
+ behave at runtime?"
349
+
350
+ See Also:
351
+ - handler_type: Architectural role (INFRA_HANDLER)
352
+ """
353
+ return EnumHandlerTypeCategory.EFFECT
354
+
355
+ async def initialize(self, config: dict[str, object]) -> None:
356
+ """Initialize the metrics handler with configuration.
357
+
358
+ Validates the configuration and starts the HTTP server for metric
359
+ scraping if enable_server is True.
360
+
361
+ Security Note:
362
+ The default bind address is "127.0.0.1" (localhost only).
363
+ To expose externally, explicitly set host="0.0.0.0" AND deploy
364
+ behind a reverse proxy with authentication, TLS, and rate limiting.
365
+
366
+ Args:
367
+ config: Configuration dict containing:
368
+ - host: Bind address (default: "127.0.0.1" - localhost only)
369
+ - port: Port number (default: 9090)
370
+ - path: Metrics endpoint path (default: "/metrics")
371
+ - push_gateway_url: Optional Pushgateway URL
372
+ - enable_server: Whether to start HTTP server (default: True)
373
+ - job_name: Job name for Pushgateway (default: "onex_metrics")
374
+ - shutdown_timeout_seconds: Shutdown timeout (default: 5.0)
375
+ - max_request_size_bytes: Max request body size (default: 1MB)
376
+ - request_timeout_seconds: Request processing timeout (default: 30s)
377
+
378
+ Raises:
379
+ ProtocolConfigurationError: If configuration validation fails.
380
+ RuntimeHostError: If HTTP server fails to start.
381
+
382
+ Example:
383
+ >>> handler = HandlerMetricsPrometheus()
384
+ >>> await handler.initialize({"port": 9091})
385
+ >>> # Server bound to 127.0.0.1:9091 (localhost only - secure)
386
+ """
387
+ init_correlation_id = uuid4()
388
+
389
+ # Validate required dependencies before proceeding
390
+ missing_deps: list[str] = []
391
+ if not _PROMETHEUS_AVAILABLE:
392
+ missing_deps.append("prometheus_client")
393
+ if not _AIOHTTP_AVAILABLE:
394
+ missing_deps.append("aiohttp")
395
+
396
+ if missing_deps:
397
+ context = ModelInfraErrorContext.with_correlation(
398
+ correlation_id=init_correlation_id,
399
+ transport_type=EnumInfraTransportType.HTTP,
400
+ operation="initialize",
401
+ target_name="metrics_prometheus_handler",
402
+ )
403
+ raise ProtocolConfigurationError(
404
+ f"Missing required dependencies: {', '.join(missing_deps)}. "
405
+ f"Install with: pip install {' '.join(missing_deps)}",
406
+ context=context,
407
+ )
408
+
409
+ logger.info(
410
+ "Initializing %s",
411
+ self.__class__.__name__,
412
+ extra={
413
+ "handler": self.__class__.__name__,
414
+ "correlation_id": str(init_correlation_id),
415
+ },
416
+ )
417
+
418
+ # Validate and parse configuration
419
+ # NOTE: Broad Exception catch is intentional here because Pydantic can raise
420
+ # various exception types (ValidationError, TypeError, ValueError) depending
421
+ # on the validation failure. We wrap all in ProtocolConfigurationError.
422
+ try:
423
+ self._config = ModelMetricsHandlerConfig(**config)
424
+ except Exception as e:
425
+ context = ModelInfraErrorContext.with_correlation(
426
+ correlation_id=init_correlation_id,
427
+ transport_type=EnumInfraTransportType.HTTP,
428
+ operation="initialize",
429
+ target_name="metrics_prometheus_handler",
430
+ )
431
+ raise ProtocolConfigurationError(
432
+ f"Invalid metrics handler configuration: {e}",
433
+ context=context,
434
+ ) from e
435
+
436
+ # Start HTTP server if enabled
437
+ if self._config.enable_server:
438
+ await self._start_http_server(init_correlation_id)
439
+
440
+ self._initialized = True
441
+
442
+ logger.info(
443
+ "HandlerMetricsPrometheus initialized successfully",
444
+ extra={
445
+ "correlation_id": str(init_correlation_id),
446
+ "host": self._config.host,
447
+ "port": self._config.port,
448
+ "path": self._config.path,
449
+ "server_enabled": self._config.enable_server,
450
+ "push_gateway_configured": self._config.push_gateway_url is not None,
451
+ },
452
+ )
453
+
454
+ async def _start_http_server(self, correlation_id: UUID) -> None:
455
+ """Start the aiohttp HTTP server for metrics exposition.
456
+
457
+ Creates an aiohttp web application with a single route for metrics
458
+ and starts it as a background TCP site.
459
+
460
+ Args:
461
+ correlation_id: Correlation ID for tracing.
462
+
463
+ Raises:
464
+ RuntimeHostError: If server fails to start.
465
+ """
466
+ if self._config is None:
467
+ context = ModelInfraErrorContext.with_correlation(
468
+ correlation_id=correlation_id,
469
+ transport_type=EnumInfraTransportType.HTTP,
470
+ operation="start_http_server",
471
+ target_name="metrics_prometheus_handler",
472
+ )
473
+ raise RuntimeHostError(
474
+ "Configuration not set before starting HTTP server",
475
+ context=context,
476
+ )
477
+
478
+ try:
479
+ # Create aiohttp application with security limits
480
+ # client_max_size limits the maximum request body size
481
+ self._app = web.Application(
482
+ client_max_size=self._config.max_request_size_bytes,
483
+ )
484
+ self._app.router.add_get(self._config.path, self._handle_metrics_request)
485
+
486
+ # Create runner and site
487
+ self._runner = web.AppRunner(self._app)
488
+ await self._runner.setup()
489
+
490
+ self._site = web.TCPSite(
491
+ self._runner,
492
+ self._config.host,
493
+ self._config.port,
494
+ )
495
+ await self._site.start()
496
+
497
+ # Log security-relevant configuration
498
+ is_localhost = self._config.host in ("127.0.0.1", "localhost", "::1")
499
+ if not is_localhost:
500
+ logger.warning(
501
+ "HTTP metrics server binding to non-localhost address - "
502
+ "ensure reverse proxy with auth/TLS is in place",
503
+ extra={
504
+ "correlation_id": str(correlation_id),
505
+ "host": self._config.host,
506
+ "port": self._config.port,
507
+ },
508
+ )
509
+
510
+ logger.info(
511
+ "HTTP metrics server started",
512
+ extra={
513
+ "correlation_id": str(correlation_id),
514
+ "host": self._config.host,
515
+ "port": self._config.port,
516
+ "path": self._config.path,
517
+ "max_request_size_bytes": self._config.max_request_size_bytes,
518
+ "request_timeout_seconds": self._config.request_timeout_seconds,
519
+ "localhost_only": is_localhost,
520
+ },
521
+ )
522
+
523
+ except OSError as e:
524
+ context = ModelInfraErrorContext.with_correlation(
525
+ correlation_id=correlation_id,
526
+ transport_type=EnumInfraTransportType.HTTP,
527
+ operation="start_http_server",
528
+ target_name="metrics_prometheus_handler",
529
+ )
530
+ raise RuntimeHostError(
531
+ f"Failed to start HTTP server on {self._config.host}:{self._config.port}: {e}",
532
+ context=context,
533
+ ) from e
534
+
535
+ def _parse_correlation_id_header(self, header_value: str | None) -> UUID:
536
+ """Parse and validate X-Correlation-ID header value with security guards.
537
+
538
+ Safely extracts a correlation ID from the request header. If the header
539
+ is missing, empty, or contains an invalid UUID format, generates a new
540
+ UUID and logs a warning for invalid values.
541
+
542
+ Security Measures:
543
+ - Length check: Headers exceeding 64 chars are rejected before parsing
544
+ - Format validation: Regex validates UUID format before UUID() parsing
545
+ - No reflection: Invalid values are NOT echoed back in responses
546
+ - Truncated logging: Invalid values logged with max 36 chars (UUID length)
547
+
548
+ Args:
549
+ header_value: The raw X-Correlation-ID header value, or None if absent.
550
+
551
+ Returns:
552
+ A valid UUID - either parsed from the header or newly generated.
553
+
554
+ Note:
555
+ Invalid correlation IDs are handled gracefully to avoid crashing
556
+ the metrics endpoint. A warning is logged to help identify
557
+ misconfigured clients, but the invalid value is NOT exposed in
558
+ HTTP responses to prevent header injection attacks.
559
+ """
560
+ if not header_value:
561
+ return uuid4()
562
+
563
+ # Security: Reject excessively long headers before any processing
564
+ if len(header_value) > _MAX_CORRELATION_ID_LENGTH:
565
+ fallback_id = uuid4()
566
+ logger.warning(
567
+ "X-Correlation-ID header exceeds maximum length, using generated UUID",
568
+ extra={
569
+ "header_length": len(header_value),
570
+ "max_length": _MAX_CORRELATION_ID_LENGTH,
571
+ "generated_correlation_id": str(fallback_id),
572
+ },
573
+ )
574
+ return fallback_id
575
+
576
+ # Security: Validate UUID format with regex before parsing
577
+ # This prevents malformed input from reaching UUID() constructor
578
+ if not _UUID_REGEX.match(header_value):
579
+ fallback_id = uuid4()
580
+ logger.warning(
581
+ "Invalid X-Correlation-ID header format, using generated UUID",
582
+ extra={
583
+ # Truncate to UUID length (36 chars) max for safe logging
584
+ "invalid_header_preview": header_value[:36],
585
+ "generated_correlation_id": str(fallback_id),
586
+ },
587
+ )
588
+ return fallback_id
589
+
590
+ try:
591
+ return UUID(header_value)
592
+ except (ValueError, AttributeError):
593
+ # Fallback for any edge cases not caught by regex
594
+ fallback_id = uuid4()
595
+ logger.warning(
596
+ "X-Correlation-ID UUID parsing failed, using generated UUID",
597
+ extra={
598
+ "generated_correlation_id": str(fallback_id),
599
+ },
600
+ )
601
+ return fallback_id
602
+
603
+ async def _handle_metrics_request(self, request: web.Request) -> web.Response:
604
+ """Handle HTTP GET requests to the metrics endpoint.
605
+
606
+ Generates Prometheus metrics in text exposition format and returns
607
+ them with the appropriate content type.
608
+
609
+ Security:
610
+ - Correlation IDs are validated before use (see _parse_correlation_id_header)
611
+ - Error messages are sanitized - no internal exception details exposed
612
+ - Timeout protection prevents DoS via slow metric generation
613
+
614
+ Args:
615
+ request: aiohttp Request object.
616
+
617
+ Returns:
618
+ aiohttp Response with metrics text, or generic error on failure.
619
+ """
620
+ # Get and validate correlation ID from request headers
621
+ correlation_id = self._parse_correlation_id_header(
622
+ request.headers.get("X-Correlation-ID")
623
+ )
624
+
625
+ logger.debug(
626
+ "Handling metrics scrape request",
627
+ extra={
628
+ "correlation_id": str(correlation_id),
629
+ "remote": str(request.remote),
630
+ },
631
+ )
632
+
633
+ # Start timing for scrape duration metric
634
+ start_time = time.perf_counter()
635
+
636
+ # Get timeout from config if available, otherwise use default
637
+ metrics_timeout = (
638
+ self._config.request_timeout_seconds
639
+ if self._config
640
+ else _DEFAULT_METRICS_GENERATION_TIMEOUT
641
+ )
642
+
643
+ try:
644
+ # Generate metrics with timeout protection to prevent DoS
645
+ # generate_latest() is synchronous, so run in executor with timeout
646
+ loop = asyncio.get_running_loop()
647
+ metrics_bytes = await asyncio.wait_for(
648
+ loop.run_in_executor(None, generate_latest),
649
+ timeout=metrics_timeout,
650
+ )
651
+
652
+ # Record scrape duration after generation completes
653
+ # The duration is observed AFTER generate_latest() returns, so it will
654
+ # be available in the NEXT scrape (avoiding chicken-and-egg recursion)
655
+ duration_seconds = time.perf_counter() - start_time
656
+ histogram = _get_scrape_duration_histogram()
657
+ if histogram is not None:
658
+ histogram.observe(duration_seconds)
659
+
660
+ logger.debug(
661
+ "Metrics scrape completed",
662
+ extra={
663
+ "correlation_id": str(correlation_id),
664
+ "duration_seconds": duration_seconds,
665
+ "metrics_size_bytes": len(metrics_bytes),
666
+ },
667
+ )
668
+
669
+ # Use headers dict for Content-Type because CONTENT_TYPE_LATEST includes
670
+ # charset which conflicts with aiohttp's content_type parameter validation
671
+ return web.Response(
672
+ body=metrics_bytes,
673
+ headers={
674
+ "Content-Type": CONTENT_TYPE_LATEST,
675
+ "X-Correlation-ID": str(correlation_id),
676
+ },
677
+ )
678
+
679
+ except TimeoutError:
680
+ # Record duration even on timeout (will be at or near timeout threshold)
681
+ duration_seconds = time.perf_counter() - start_time
682
+ histogram = _get_scrape_duration_histogram()
683
+ if histogram is not None:
684
+ histogram.observe(duration_seconds)
685
+
686
+ # Log timeout with full details, but return generic message
687
+ # Using warning level since timeout is expected behavior (DoS protection)
688
+ logger.warning(
689
+ "Metrics generation timed out",
690
+ extra={
691
+ "correlation_id": str(correlation_id),
692
+ "timeout_seconds": metrics_timeout,
693
+ "duration_seconds": duration_seconds,
694
+ },
695
+ )
696
+ # Security: Generic error message - no internal details exposed
697
+ return web.Response(
698
+ text="Service temporarily unavailable",
699
+ status=503, # Service Unavailable for timeout
700
+ headers={"X-Correlation-ID": str(correlation_id)},
701
+ )
702
+
703
+ except Exception:
704
+ # NOTE: Broad Exception catch is intentional here to ensure observability
705
+ # (duration recording) even for unexpected errors, while returning a
706
+ # generic 500 response that prevents internal details from leaking.
707
+ # The exception is fully logged internally for debugging.
708
+ #
709
+ # Record duration even on error for observability
710
+ duration_seconds = time.perf_counter() - start_time
711
+ histogram = _get_scrape_duration_histogram()
712
+ if histogram is not None:
713
+ histogram.observe(duration_seconds)
714
+
715
+ # Log full exception details internally for debugging
716
+ logger.exception(
717
+ "Failed to generate metrics",
718
+ extra={
719
+ "correlation_id": str(correlation_id),
720
+ "duration_seconds": duration_seconds,
721
+ },
722
+ )
723
+ # Security: Generic error message - no exception type or details exposed
724
+ # This prevents information leakage about internal implementation
725
+ return web.Response(
726
+ text="Internal server error generating metrics",
727
+ status=500,
728
+ headers={"X-Correlation-ID": str(correlation_id)},
729
+ )
730
+
731
+ async def shutdown(self) -> None:
732
+ """Shutdown the HTTP server and release resources.
733
+
734
+ Gracefully stops the HTTP server, waiting for pending requests
735
+ to complete within the configured timeout. The shutdown process:
736
+
737
+ 1. Stops accepting new connections (site.stop())
738
+ 2. Waits for pending requests to drain (runner.cleanup())
739
+ 3. Releases all resources
740
+
741
+ If cleanup times out, pending requests are forcibly terminated.
742
+ """
743
+ shutdown_correlation_id = uuid4()
744
+
745
+ logger.info(
746
+ "Shutting down HandlerMetricsPrometheus",
747
+ extra={"correlation_id": str(shutdown_correlation_id)},
748
+ )
749
+
750
+ timeout = self._config.shutdown_timeout_seconds if self._config else 5.0
751
+
752
+ # Phase 1: Stop accepting new connections
753
+ # This allows pending requests to complete before we close
754
+ if self._site is not None:
755
+ try:
756
+ await self._site.stop()
757
+ logger.debug(
758
+ "HTTP server stopped accepting new connections",
759
+ extra={"correlation_id": str(shutdown_correlation_id)},
760
+ )
761
+ except OSError as e:
762
+ # OSError can occur if socket is already closed
763
+ logger.warning(
764
+ "Error stopping HTTP site: %s",
765
+ e,
766
+ extra={
767
+ "correlation_id": str(shutdown_correlation_id),
768
+ "error_type": type(e).__name__,
769
+ },
770
+ )
771
+ finally:
772
+ self._site = None
773
+
774
+ # Phase 2: Drain pending requests and cleanup runner
775
+ # This gives in-flight requests time to complete
776
+ if self._runner is not None:
777
+ try:
778
+ await asyncio.wait_for(
779
+ self._runner.cleanup(),
780
+ timeout=timeout,
781
+ )
782
+ logger.debug(
783
+ "HTTP server cleanup completed successfully",
784
+ extra={"correlation_id": str(shutdown_correlation_id)},
785
+ )
786
+ except TimeoutError:
787
+ # Timeout means pending requests couldn't complete in time
788
+ # This is a warning because it may indicate slow consumers
789
+ logger.warning(
790
+ "HTTP server cleanup timed out after %.1f seconds - "
791
+ "pending requests may have been forcibly terminated",
792
+ timeout,
793
+ extra={
794
+ "correlation_id": str(shutdown_correlation_id),
795
+ "timeout_seconds": timeout,
796
+ },
797
+ )
798
+ except OSError as e:
799
+ # OSError can occur during cleanup if connections are in bad state
800
+ logger.warning(
801
+ "OSError during HTTP server cleanup: %s",
802
+ e,
803
+ extra={
804
+ "correlation_id": str(shutdown_correlation_id),
805
+ "error_type": type(e).__name__,
806
+ },
807
+ )
808
+ finally:
809
+ self._runner = None
810
+
811
+ self._app = None
812
+ self._initialized = False
813
+ self._config = None
814
+
815
+ logger.info(
816
+ "HandlerMetricsPrometheus shutdown complete",
817
+ extra={"correlation_id": str(shutdown_correlation_id)},
818
+ )
819
+
820
+ def _build_response(
821
+ self,
822
+ payload: ModelMetricsHandlerPayload,
823
+ correlation_id: UUID,
824
+ input_envelope_id: UUID,
825
+ ) -> ModelHandlerOutput[ModelMetricsHandlerResponse]:
826
+ """Build standardized response wrapped in ModelHandlerOutput.
827
+
828
+ Args:
829
+ payload: Operation-specific response payload.
830
+ correlation_id: Correlation ID for tracing.
831
+ input_envelope_id: Input envelope ID for causality tracking.
832
+
833
+ Returns:
834
+ ModelHandlerOutput wrapping the metrics handler response.
835
+ """
836
+ response = ModelMetricsHandlerResponse(
837
+ status=EnumResponseStatus.SUCCESS,
838
+ payload=payload,
839
+ correlation_id=correlation_id,
840
+ )
841
+ return ModelHandlerOutput.for_compute(
842
+ input_envelope_id=input_envelope_id,
843
+ correlation_id=correlation_id,
844
+ handler_id=HANDLER_ID_METRICS,
845
+ result=response,
846
+ )
847
+
848
+ async def execute(
849
+ self, envelope: dict[str, object]
850
+ ) -> ModelHandlerOutput[ModelMetricsHandlerResponse]:
851
+ """Execute a metrics operation from envelope.
852
+
853
+ Args:
854
+ envelope: Request envelope containing:
855
+ - operation: "metrics.scrape" or "metrics.push"
856
+ - payload: dict with operation-specific parameters (optional)
857
+ - correlation_id: Optional correlation ID for tracing
858
+ - envelope_id: Optional envelope ID for causality tracking
859
+
860
+ Returns:
861
+ ModelHandlerOutput wrapping the operation result.
862
+
863
+ Raises:
864
+ RuntimeHostError: If handler not initialized or invalid operation.
865
+
866
+ Example:
867
+ >>> result = await handler.execute({
868
+ ... "operation": "metrics.scrape",
869
+ ... "correlation_id": str(uuid4()),
870
+ ... })
871
+ """
872
+ correlation_id = self._extract_correlation_id(envelope)
873
+ input_envelope_id = self._extract_envelope_id(envelope)
874
+
875
+ if not self._initialized:
876
+ context = ModelInfraErrorContext.with_correlation(
877
+ correlation_id=correlation_id,
878
+ transport_type=EnumInfraTransportType.HTTP,
879
+ operation="execute",
880
+ target_name="metrics_prometheus_handler",
881
+ )
882
+ raise RuntimeHostError(
883
+ "Metrics handler not initialized. Call initialize() first.",
884
+ context=context,
885
+ )
886
+
887
+ operation = envelope.get("operation")
888
+ if not isinstance(operation, str):
889
+ context = ModelInfraErrorContext.with_correlation(
890
+ correlation_id=correlation_id,
891
+ transport_type=EnumInfraTransportType.HTTP,
892
+ operation="execute",
893
+ target_name="metrics_prometheus_handler",
894
+ )
895
+ raise RuntimeHostError(
896
+ "Missing or invalid 'operation' in envelope",
897
+ context=context,
898
+ )
899
+
900
+ if operation not in SUPPORTED_OPERATIONS:
901
+ context = ModelInfraErrorContext.with_correlation(
902
+ correlation_id=correlation_id,
903
+ transport_type=EnumInfraTransportType.HTTP,
904
+ operation=operation,
905
+ target_name="metrics_prometheus_handler",
906
+ )
907
+ raise RuntimeHostError(
908
+ f"Operation '{operation}' not supported. "
909
+ f"Available: {', '.join(sorted(SUPPORTED_OPERATIONS))}",
910
+ context=context,
911
+ )
912
+
913
+ # Route to appropriate handler
914
+ if operation == "metrics.scrape":
915
+ return await self._handle_scrape(correlation_id, input_envelope_id)
916
+ else: # metrics.push
917
+ return await self._handle_push(envelope, correlation_id, input_envelope_id)
918
+
919
+ async def _handle_scrape(
920
+ self,
921
+ correlation_id: UUID,
922
+ input_envelope_id: UUID,
923
+ ) -> ModelHandlerOutput[ModelMetricsHandlerResponse]:
924
+ """Handle metrics.scrape operation.
925
+
926
+ Generates Prometheus metrics from the default registry and returns
927
+ them in text exposition format.
928
+
929
+ Args:
930
+ correlation_id: Correlation ID for tracing.
931
+ input_envelope_id: Input envelope ID for causality tracking.
932
+
933
+ Returns:
934
+ ModelHandlerOutput with metrics text in payload.
935
+ """
936
+ logger.debug(
937
+ "Executing metrics.scrape operation",
938
+ extra={"correlation_id": str(correlation_id)},
939
+ )
940
+
941
+ # Get timeout from config if available, otherwise use default
942
+ scrape_timeout = (
943
+ self._config.request_timeout_seconds
944
+ if self._config
945
+ else _DEFAULT_METRICS_GENERATION_TIMEOUT
946
+ )
947
+
948
+ # Generate metrics from default Prometheus registry
949
+ # generate_latest() is synchronous, so run in executor with timeout
950
+ # to prevent blocking the event loop
951
+ try:
952
+ loop = asyncio.get_running_loop()
953
+ metrics_bytes = await asyncio.wait_for(
954
+ loop.run_in_executor(None, generate_latest),
955
+ timeout=scrape_timeout,
956
+ )
957
+ except TimeoutError:
958
+ context = ModelTimeoutErrorContext(
959
+ correlation_id=correlation_id,
960
+ transport_type=EnumInfraTransportType.HTTP,
961
+ operation="metrics.scrape",
962
+ target_name="metrics_prometheus_handler",
963
+ timeout_seconds=scrape_timeout,
964
+ )
965
+ raise InfraTimeoutError(
966
+ f"Metrics generation timed out after {scrape_timeout}s",
967
+ context=context,
968
+ ) from None
969
+
970
+ metrics_text = metrics_bytes.decode("utf-8")
971
+
972
+ payload = ModelMetricsHandlerPayload(
973
+ operation_type="metrics.scrape",
974
+ metrics_text=metrics_text,
975
+ content_type=CONTENT_TYPE_LATEST,
976
+ )
977
+
978
+ return self._build_response(payload, correlation_id, input_envelope_id)
979
+
980
+ async def _handle_push(
981
+ self,
982
+ envelope: dict[str, object],
983
+ correlation_id: UUID,
984
+ input_envelope_id: UUID,
985
+ ) -> ModelHandlerOutput[ModelMetricsHandlerResponse]:
986
+ """Handle metrics.push operation.
987
+
988
+ Pushes current metrics to the configured Prometheus Pushgateway.
989
+ Requires push_gateway_url to be configured.
990
+
991
+ Args:
992
+ envelope: Request envelope (may contain override configuration).
993
+ correlation_id: Correlation ID for tracing.
994
+ input_envelope_id: Input envelope ID for causality tracking.
995
+
996
+ Returns:
997
+ ModelHandlerOutput with push confirmation in payload.
998
+
999
+ Raises:
1000
+ RuntimeHostError: If Pushgateway is not configured.
1001
+ """
1002
+ if self._config is None or self._config.push_gateway_url is None:
1003
+ context = ModelInfraErrorContext.with_correlation(
1004
+ correlation_id=correlation_id,
1005
+ transport_type=EnumInfraTransportType.HTTP,
1006
+ operation="metrics.push",
1007
+ target_name="metrics_prometheus_handler",
1008
+ )
1009
+ raise RuntimeHostError(
1010
+ "Pushgateway not configured. Set push_gateway_url in config.",
1011
+ context=context,
1012
+ )
1013
+
1014
+ # Capture config values in local variables for lambda closure
1015
+ # This ensures type safety and avoids mypy errors with Optional access
1016
+ push_gateway_url: str = self._config.push_gateway_url
1017
+ job_name: str = self._config.job_name
1018
+
1019
+ logger.debug(
1020
+ "Executing metrics.push operation",
1021
+ extra={
1022
+ "correlation_id": str(correlation_id),
1023
+ "push_gateway_url": push_gateway_url,
1024
+ },
1025
+ )
1026
+
1027
+ # Use prometheus_client's push_to_gateway functionality
1028
+ # Note: This is a synchronous operation, so we run it in executor with timeout
1029
+ # NOTE: Broad Exception catch is intentional here because push_to_gateway
1030
+ # can raise various exceptions: URLError for network issues, HTTPError for
1031
+ # gateway errors, and other unexpected exceptions from the prometheus_client
1032
+ # library. We wrap all failures in RuntimeHostError for consistent handling.
1033
+ try:
1034
+ from prometheus_client import REGISTRY, push_to_gateway
1035
+
1036
+ loop = asyncio.get_running_loop()
1037
+ await asyncio.wait_for(
1038
+ loop.run_in_executor(
1039
+ None,
1040
+ lambda: push_to_gateway(
1041
+ push_gateway_url,
1042
+ job=job_name,
1043
+ registry=REGISTRY,
1044
+ ),
1045
+ ),
1046
+ timeout=_PUSH_GATEWAY_TIMEOUT,
1047
+ )
1048
+
1049
+ pushed_at = datetime.now(UTC).isoformat()
1050
+
1051
+ payload = ModelMetricsHandlerPayload(
1052
+ operation_type="metrics.push",
1053
+ pushed_at=pushed_at,
1054
+ push_gateway_url=push_gateway_url,
1055
+ job_name=job_name,
1056
+ )
1057
+
1058
+ logger.info(
1059
+ "Metrics pushed to Pushgateway",
1060
+ extra={
1061
+ "correlation_id": str(correlation_id),
1062
+ "push_gateway_url": push_gateway_url,
1063
+ "pushed_at": pushed_at,
1064
+ },
1065
+ )
1066
+
1067
+ return self._build_response(payload, correlation_id, input_envelope_id)
1068
+
1069
+ except TimeoutError:
1070
+ timeout_ctx = ModelTimeoutErrorContext(
1071
+ correlation_id=correlation_id,
1072
+ transport_type=EnumInfraTransportType.HTTP,
1073
+ operation="metrics.push",
1074
+ target_name="metrics_prometheus_handler",
1075
+ timeout_seconds=_PUSH_GATEWAY_TIMEOUT,
1076
+ )
1077
+ raise InfraTimeoutError(
1078
+ f"Pushgateway request timed out after {_PUSH_GATEWAY_TIMEOUT}s",
1079
+ context=timeout_ctx,
1080
+ ) from None
1081
+
1082
+ except Exception as e:
1083
+ # NOTE: Broad Exception catch is intentional here because httpx and
1084
+ # network operations can raise many exception types (ConnectionError,
1085
+ # ProtocolError, etc.). We wrap all in RuntimeHostError with correlation
1086
+ # context while preserving the error chain for debugging.
1087
+ context = ModelInfraErrorContext.with_correlation(
1088
+ correlation_id=correlation_id,
1089
+ transport_type=EnumInfraTransportType.HTTP,
1090
+ operation="metrics.push",
1091
+ target_name="metrics_prometheus_handler",
1092
+ )
1093
+ raise RuntimeHostError(
1094
+ f"Failed to push metrics to Pushgateway: {type(e).__name__}",
1095
+ context=context,
1096
+ ) from e
1097
+
1098
+ def describe(self) -> dict[str, object]:
1099
+ """Return handler metadata and capabilities for introspection.
1100
+
1101
+ Returns:
1102
+ dict containing handler type, category, operations, and status.
1103
+ """
1104
+ return {
1105
+ "handler_type": self.handler_type.value,
1106
+ "handler_category": self.handler_category.value,
1107
+ "supported_operations": sorted(SUPPORTED_OPERATIONS),
1108
+ "initialized": self._initialized,
1109
+ "server_enabled": (self._config.enable_server if self._config else False),
1110
+ "host": self._config.host if self._config else None,
1111
+ "port": self._config.port if self._config else None,
1112
+ "path": self._config.path if self._config else None,
1113
+ "push_gateway_configured": (
1114
+ self._config.push_gateway_url is not None if self._config else False
1115
+ ),
1116
+ "version": "0.1.0",
1117
+ }
1118
+
1119
+
1120
+ __all__: list[str] = ["HandlerMetricsPrometheus", "HANDLER_ID_METRICS"]