omnibase_infra 0.2.1__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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +261 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +138 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +123 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +101 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +156 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +92 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +102 -0
- omnibase_infra/errors/error_infra.py +608 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +112 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +86 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +743 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1658 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +184 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +765 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +725 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +75 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +787 -0
- omnibase_infra/handlers/handler_db.py +1039 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +1154 -0
- omnibase_infra/handlers/handler_http.py +920 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +748 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +422 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +42 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +337 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +277 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +915 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +747 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +99 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +655 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2465 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +136 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +311 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +147 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +37 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +80 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +185 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +590 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +59 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +179 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +175 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +40 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +280 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +120 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +48 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +208 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +99 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +172 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +475 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +609 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +525 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +392 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +742 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +225 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +109 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +194 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +682 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +416 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +214 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +32 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +76 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1137 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +435 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +99 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +296 -0
- omnibase_infra/runtime/binding_config_resolver.py +2706 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/contract_handler_discovery.py +582 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +42 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/handler_contract_source.py +669 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2029 -0
- omnibase_infra/runtime/handler_registry.py +321 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +378 -0
- omnibase_infra/runtime/mixins/__init__.py +17 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +757 -0
- omnibase_infra/runtime/models/__init__.py +192 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +228 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +624 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1102 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +27 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +444 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1502 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2110 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1573 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +1805 -0
- omnibase_infra/runtime/service_runtime_host_process.py +2260 -0
- omnibase_infra/runtime/util_container_wiring.py +1123 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +566 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/services/__init__.py +68 -0
- omnibase_infra/services/corpus_capture.py +678 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +897 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +682 -0
- omnibase_infra/services/service_timeout_scanner.py +390 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +21 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +89 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1486 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +1710 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +410 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.1.dist-info/METADATA +197 -0
- omnibase_infra-0.2.1.dist-info/RECORD +675 -0
- omnibase_infra-0.2.1.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.1.dist-info/entry_points.txt +4 -0
- omnibase_infra-0.2.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1573 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""ONEX Kernel - Minimal bootstrap for contract-driven runtime.
|
|
4
|
+
|
|
5
|
+
This is the kernel entrypoint for the ONEX runtime. It provides a contract-driven
|
|
6
|
+
bootstrap that wires configuration into the existing RuntimeHostProcess.
|
|
7
|
+
|
|
8
|
+
The kernel is responsible for:
|
|
9
|
+
1. Loading runtime configuration from contracts or environment
|
|
10
|
+
2. Creating and starting the event bus (EventBusInmemory or EventBusKafka)
|
|
11
|
+
3. Building the dependency container (event_bus, config)
|
|
12
|
+
4. Instantiating RuntimeHostProcess with contract-driven configuration
|
|
13
|
+
5. Starting the HTTP health server for Docker/K8s probes
|
|
14
|
+
6. Setting up graceful shutdown signal handlers
|
|
15
|
+
7. Running the runtime until shutdown is requested
|
|
16
|
+
|
|
17
|
+
Event Bus Selection:
|
|
18
|
+
The kernel supports two event bus implementations:
|
|
19
|
+
- EventBusInmemory: For local development and testing (default)
|
|
20
|
+
- EventBusKafka: For production use with Kafka/Redpanda
|
|
21
|
+
|
|
22
|
+
Selection is determined by:
|
|
23
|
+
- KAFKA_BOOTSTRAP_SERVERS environment variable (if set, uses Kafka)
|
|
24
|
+
- config.event_bus.type field in runtime_config.yaml
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
# Run with default contracts directory (./contracts)
|
|
28
|
+
python -m omnibase_infra.runtime.service_kernel
|
|
29
|
+
|
|
30
|
+
# Run with custom contracts directory
|
|
31
|
+
ONEX_CONTRACTS_DIR=/path/to/contracts python -m omnibase_infra.runtime.service_kernel
|
|
32
|
+
|
|
33
|
+
# Or via the installed entrypoint
|
|
34
|
+
onex-runtime
|
|
35
|
+
|
|
36
|
+
Environment Variables:
|
|
37
|
+
ONEX_CONTRACTS_DIR: Path to contracts directory (default: ./contracts)
|
|
38
|
+
ONEX_HTTP_PORT: Port for health check HTTP server (default: 8085)
|
|
39
|
+
ONEX_LOG_LEVEL: Logging level (default: INFO)
|
|
40
|
+
ONEX_ENVIRONMENT: Runtime environment name (default: local)
|
|
41
|
+
|
|
42
|
+
Note:
|
|
43
|
+
This kernel uses the existing RuntimeHostProcess as the core runtime engine.
|
|
44
|
+
A future refactor may integrate NodeOrchestrator as the primary execution
|
|
45
|
+
engine, but for MVP this lean kernel provides contract-driven bootstrap
|
|
46
|
+
with minimal risk and maximum reuse of tested code.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from __future__ import annotations
|
|
50
|
+
|
|
51
|
+
import asyncio
|
|
52
|
+
import logging
|
|
53
|
+
import os
|
|
54
|
+
import signal
|
|
55
|
+
import sys
|
|
56
|
+
import time
|
|
57
|
+
from collections.abc import Awaitable, Callable
|
|
58
|
+
from importlib.metadata import version as get_package_version
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
from typing import cast
|
|
61
|
+
from uuid import UUID
|
|
62
|
+
|
|
63
|
+
import asyncpg
|
|
64
|
+
import yaml
|
|
65
|
+
from pydantic import ValidationError
|
|
66
|
+
|
|
67
|
+
from omnibase_core.container import ModelONEXContainer
|
|
68
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
69
|
+
from omnibase_infra.errors import (
|
|
70
|
+
ModelInfraErrorContext,
|
|
71
|
+
ProtocolConfigurationError,
|
|
72
|
+
RuntimeHostError,
|
|
73
|
+
ServiceResolutionError,
|
|
74
|
+
)
|
|
75
|
+
from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
|
|
76
|
+
from omnibase_infra.event_bus.event_bus_kafka import EventBusKafka
|
|
77
|
+
from omnibase_infra.event_bus.models.config import ModelKafkaEventBusConfig
|
|
78
|
+
from omnibase_infra.nodes.node_registration_orchestrator.dispatchers import (
|
|
79
|
+
DispatcherNodeIntrospected,
|
|
80
|
+
)
|
|
81
|
+
from omnibase_infra.nodes.node_registration_orchestrator.introspection_event_router import (
|
|
82
|
+
IntrospectionEventRouter,
|
|
83
|
+
)
|
|
84
|
+
from omnibase_infra.runtime.handler_registry import RegistryProtocolBinding
|
|
85
|
+
from omnibase_infra.runtime.models import (
|
|
86
|
+
ModelProjectorPluginLoaderConfig,
|
|
87
|
+
ModelRuntimeConfig,
|
|
88
|
+
)
|
|
89
|
+
from omnibase_infra.runtime.projector_plugin_loader import (
|
|
90
|
+
ProjectorPluginLoader,
|
|
91
|
+
ProjectorShell,
|
|
92
|
+
ProtocolEventProjector,
|
|
93
|
+
)
|
|
94
|
+
from omnibase_infra.runtime.service_runtime_host_process import RuntimeHostProcess
|
|
95
|
+
from omnibase_infra.runtime.util_container_wiring import (
|
|
96
|
+
wire_infrastructure_services,
|
|
97
|
+
wire_registration_handlers,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Circular Import Note (OMN-529):
|
|
101
|
+
# ---------------------------------
|
|
102
|
+
# ServiceHealth and DEFAULT_HTTP_PORT are imported inside bootstrap() rather than
|
|
103
|
+
# at module level to avoid a circular import. The import chain is:
|
|
104
|
+
#
|
|
105
|
+
# 1. omnibase_infra/runtime/__init__.py imports kernel_bootstrap from kernel.py
|
|
106
|
+
# 2. If kernel.py imported ServiceHealth at module level, it would load service_health.py
|
|
107
|
+
# 3. service_health.py imports ModelHealthCheckResponse from runtime.models
|
|
108
|
+
# 4. This triggers initialization of omnibase_infra.runtime package (step 1)
|
|
109
|
+
# 5. Runtime package tries to import kernel.py which is still initializing -> circular!
|
|
110
|
+
#
|
|
111
|
+
# The lazy import in bootstrap() is acceptable because:
|
|
112
|
+
# - ServiceHealth is only instantiated at runtime, not at import time
|
|
113
|
+
# - Type checking uses forward references (no import needed)
|
|
114
|
+
# - No import-time side effects are bypassed
|
|
115
|
+
# - The omnibase_infra.services.__init__.py already excludes ServiceHealth exports
|
|
116
|
+
# to prevent accidental circular imports from other modules
|
|
117
|
+
#
|
|
118
|
+
# See also: omnibase_infra/services/__init__.py "ServiceHealth Import Guide" section
|
|
119
|
+
from omnibase_infra.runtime.util_validation import validate_runtime_config
|
|
120
|
+
from omnibase_infra.utils.correlation import generate_correlation_id
|
|
121
|
+
from omnibase_infra.utils.util_error_sanitization import sanitize_error_message
|
|
122
|
+
|
|
123
|
+
logger = logging.getLogger(__name__)
|
|
124
|
+
|
|
125
|
+
# Kernel version - read from installed package metadata to avoid version drift
|
|
126
|
+
# between code and pyproject.toml. Falls back to "unknown" if package is not
|
|
127
|
+
# installed (e.g., during development without editable install).
|
|
128
|
+
try:
|
|
129
|
+
KERNEL_VERSION = get_package_version("omnibase_infra")
|
|
130
|
+
except Exception:
|
|
131
|
+
KERNEL_VERSION = "unknown"
|
|
132
|
+
|
|
133
|
+
# Default configuration
|
|
134
|
+
DEFAULT_CONTRACTS_DIR = "./contracts"
|
|
135
|
+
DEFAULT_RUNTIME_CONFIG = "runtime/runtime_config.yaml"
|
|
136
|
+
|
|
137
|
+
# Environment variable name for contracts directory
|
|
138
|
+
ENV_CONTRACTS_DIR = "ONEX_CONTRACTS_DIR"
|
|
139
|
+
DEFAULT_INPUT_TOPIC = "requests"
|
|
140
|
+
DEFAULT_OUTPUT_TOPIC = "responses"
|
|
141
|
+
DEFAULT_GROUP_ID = "onex-runtime"
|
|
142
|
+
|
|
143
|
+
# Port validation constants
|
|
144
|
+
MIN_PORT = 1
|
|
145
|
+
MAX_PORT = 65535
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _get_contracts_dir() -> Path:
|
|
149
|
+
"""Get contracts directory from environment.
|
|
150
|
+
|
|
151
|
+
Reads the ONEX_CONTRACTS_DIR environment variable. If not set,
|
|
152
|
+
returns the default contracts directory.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Path to the contracts directory.
|
|
156
|
+
"""
|
|
157
|
+
onex_value = os.environ.get(ENV_CONTRACTS_DIR)
|
|
158
|
+
if onex_value:
|
|
159
|
+
return Path(onex_value)
|
|
160
|
+
|
|
161
|
+
return Path(DEFAULT_CONTRACTS_DIR)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def load_runtime_config(
|
|
165
|
+
contracts_dir: Path,
|
|
166
|
+
correlation_id: UUID | None = None,
|
|
167
|
+
) -> ModelRuntimeConfig:
|
|
168
|
+
"""Load runtime configuration from contract file or return defaults.
|
|
169
|
+
|
|
170
|
+
Attempts to load runtime_config.yaml from the contracts directory.
|
|
171
|
+
If the file doesn't exist, returns sensible defaults to allow
|
|
172
|
+
the runtime to start without requiring a config file.
|
|
173
|
+
|
|
174
|
+
Configuration Loading Process:
|
|
175
|
+
1. Check for runtime_config.yaml in contracts directory
|
|
176
|
+
2. If found, parse YAML and validate against ModelRuntimeConfig schema
|
|
177
|
+
3. If not found, construct config from environment variables and defaults
|
|
178
|
+
4. Return fully validated configuration model
|
|
179
|
+
|
|
180
|
+
Configuration Precedence:
|
|
181
|
+
- File-based config is returned as-is when present (no environment overrides)
|
|
182
|
+
- Environment variables are only used when no config file exists
|
|
183
|
+
- Defaults are used when neither file nor environment variables are set
|
|
184
|
+
- Note: Environment overrides (e.g., ONEX_ENVIRONMENT) are applied by the
|
|
185
|
+
caller (bootstrap), not by this function
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
contracts_dir: Path to the contracts directory containing runtime_config.yaml.
|
|
189
|
+
Example: Path("./contracts") or Path("/app/contracts")
|
|
190
|
+
correlation_id: Optional correlation ID for distributed tracing. If not
|
|
191
|
+
provided, a new one will be generated. Passing a correlation_id from
|
|
192
|
+
the caller (e.g., bootstrap) ensures consistent tracing across the
|
|
193
|
+
initialization sequence.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
ModelRuntimeConfig: Fully validated configuration model with runtime settings.
|
|
197
|
+
Contains event bus configuration, topic names, consumer group, shutdown
|
|
198
|
+
behavior, and logging configuration.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
ProtocolConfigurationError: If config file exists but cannot be parsed,
|
|
202
|
+
fails validation, or cannot be read due to filesystem errors. Error
|
|
203
|
+
includes correlation_id for tracing and detailed context for debugging.
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
>>> contracts_dir = Path("./contracts")
|
|
207
|
+
>>> config = load_runtime_config(contracts_dir)
|
|
208
|
+
>>> print(config.input_topic)
|
|
209
|
+
requests
|
|
210
|
+
>>> print(config.event_bus.type)
|
|
211
|
+
inmemory
|
|
212
|
+
|
|
213
|
+
Example Error:
|
|
214
|
+
>>> # If runtime_config.yaml has invalid YAML syntax
|
|
215
|
+
>>> load_runtime_config(Path("./invalid"))
|
|
216
|
+
ProtocolConfigurationError: Failed to parse runtime config YAML at ./invalid/runtime/runtime_config.yaml
|
|
217
|
+
(correlation_id: 123e4567-e89b-12d3-a456-426614174000)
|
|
218
|
+
"""
|
|
219
|
+
config_path = contracts_dir / DEFAULT_RUNTIME_CONFIG
|
|
220
|
+
# Use passed correlation_id for consistent tracing, or generate new one
|
|
221
|
+
effective_correlation_id = correlation_id or generate_correlation_id()
|
|
222
|
+
context = ModelInfraErrorContext(
|
|
223
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
224
|
+
operation="load_config",
|
|
225
|
+
target_name=str(config_path),
|
|
226
|
+
correlation_id=effective_correlation_id,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if config_path.exists():
|
|
230
|
+
logger.info(
|
|
231
|
+
"Loading runtime config from %s (correlation_id=%s)",
|
|
232
|
+
config_path,
|
|
233
|
+
effective_correlation_id,
|
|
234
|
+
)
|
|
235
|
+
try:
|
|
236
|
+
with config_path.open(encoding="utf-8") as f:
|
|
237
|
+
raw_config = yaml.safe_load(f) or {}
|
|
238
|
+
|
|
239
|
+
# Type guard: reject non-mapping YAML payloads
|
|
240
|
+
# yaml.safe_load() can return list, str, int, etc. for valid YAML
|
|
241
|
+
# but runtime config must be a dict (mapping) for model validation
|
|
242
|
+
if not isinstance(raw_config, dict):
|
|
243
|
+
raise ProtocolConfigurationError(
|
|
244
|
+
f"Runtime config at {config_path} must be a YAML mapping (dict), "
|
|
245
|
+
f"got {type(raw_config).__name__}",
|
|
246
|
+
context=context,
|
|
247
|
+
config_path=str(config_path),
|
|
248
|
+
error_details=f"Expected dict, got {type(raw_config).__name__}",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Contract validation: validate against schema before Pydantic
|
|
252
|
+
# This provides early, actionable error messages for pattern/range violations
|
|
253
|
+
contract_errors = validate_runtime_config(raw_config)
|
|
254
|
+
if contract_errors:
|
|
255
|
+
error_count = len(contract_errors)
|
|
256
|
+
# Create concise summary for log message (first 3 errors)
|
|
257
|
+
error_summary = "; ".join(contract_errors[:3])
|
|
258
|
+
if error_count > 3:
|
|
259
|
+
error_summary += f" (and {error_count - 3} more...)"
|
|
260
|
+
raise ProtocolConfigurationError(
|
|
261
|
+
f"Contract validation failed at {config_path}: {error_count} error(s). "
|
|
262
|
+
f"First errors: {error_summary}",
|
|
263
|
+
context=context,
|
|
264
|
+
config_path=str(config_path),
|
|
265
|
+
# Full error list for structured debugging (not truncated)
|
|
266
|
+
validation_errors=contract_errors,
|
|
267
|
+
error_count=error_count,
|
|
268
|
+
)
|
|
269
|
+
logger.debug(
|
|
270
|
+
"Contract validation passed (correlation_id=%s)",
|
|
271
|
+
effective_correlation_id,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
config = ModelRuntimeConfig.model_validate(raw_config)
|
|
275
|
+
logger.debug(
|
|
276
|
+
"Runtime config loaded successfully (correlation_id=%s)",
|
|
277
|
+
effective_correlation_id,
|
|
278
|
+
extra={
|
|
279
|
+
"input_topic": config.input_topic,
|
|
280
|
+
"output_topic": config.output_topic,
|
|
281
|
+
"consumer_group": config.consumer_group,
|
|
282
|
+
"event_bus_type": config.event_bus.type,
|
|
283
|
+
},
|
|
284
|
+
)
|
|
285
|
+
return config
|
|
286
|
+
except yaml.YAMLError as e:
|
|
287
|
+
raise ProtocolConfigurationError(
|
|
288
|
+
f"Failed to parse runtime config YAML at {config_path}: {e}",
|
|
289
|
+
context=context,
|
|
290
|
+
config_path=str(config_path),
|
|
291
|
+
error_details=str(e),
|
|
292
|
+
) from e
|
|
293
|
+
except ValidationError as e:
|
|
294
|
+
# Extract validation error details for actionable error messages
|
|
295
|
+
error_count = e.error_count()
|
|
296
|
+
# Convert Pydantic errors to list[str] for consistency with contract validation
|
|
297
|
+
# Both validation_errors fields should have the same type: list[str]
|
|
298
|
+
pydantic_errors = [
|
|
299
|
+
f"{'.'.join(str(loc) for loc in err['loc'])}: {err['msg']}"
|
|
300
|
+
for err in e.errors()
|
|
301
|
+
]
|
|
302
|
+
error_summary = "; ".join(pydantic_errors[:3])
|
|
303
|
+
raise ProtocolConfigurationError(
|
|
304
|
+
f"Runtime config validation failed at {config_path}: {error_count} error(s). "
|
|
305
|
+
f"First errors: {error_summary}",
|
|
306
|
+
context=context,
|
|
307
|
+
config_path=str(config_path),
|
|
308
|
+
validation_errors=pydantic_errors,
|
|
309
|
+
error_count=error_count,
|
|
310
|
+
) from e
|
|
311
|
+
except UnicodeDecodeError as e:
|
|
312
|
+
raise ProtocolConfigurationError(
|
|
313
|
+
f"Runtime config file contains binary or non-UTF-8 content: {config_path}",
|
|
314
|
+
context=context,
|
|
315
|
+
config_path=str(config_path),
|
|
316
|
+
error_details=f"Encoding error at position {e.start}-{e.end}: {e.reason}",
|
|
317
|
+
) from e
|
|
318
|
+
except OSError as e:
|
|
319
|
+
raise ProtocolConfigurationError(
|
|
320
|
+
f"Failed to read runtime config at {config_path}: {e}",
|
|
321
|
+
context=context,
|
|
322
|
+
config_path=str(config_path),
|
|
323
|
+
error_details=str(e),
|
|
324
|
+
) from e
|
|
325
|
+
|
|
326
|
+
# No config file - use environment variables and defaults
|
|
327
|
+
logger.info(
|
|
328
|
+
"No runtime config found at %s, using environment/defaults (correlation_id=%s)",
|
|
329
|
+
config_path,
|
|
330
|
+
effective_correlation_id,
|
|
331
|
+
)
|
|
332
|
+
config = ModelRuntimeConfig(
|
|
333
|
+
input_topic=os.getenv("ONEX_INPUT_TOPIC", DEFAULT_INPUT_TOPIC),
|
|
334
|
+
output_topic=os.getenv("ONEX_OUTPUT_TOPIC", DEFAULT_OUTPUT_TOPIC),
|
|
335
|
+
consumer_group=os.getenv("ONEX_GROUP_ID", DEFAULT_GROUP_ID),
|
|
336
|
+
)
|
|
337
|
+
logger.debug(
|
|
338
|
+
"Runtime config constructed from environment/defaults (correlation_id=%s)",
|
|
339
|
+
effective_correlation_id,
|
|
340
|
+
extra={
|
|
341
|
+
"input_topic": config.input_topic,
|
|
342
|
+
"output_topic": config.output_topic,
|
|
343
|
+
"consumer_group": config.consumer_group,
|
|
344
|
+
},
|
|
345
|
+
)
|
|
346
|
+
return config
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
async def bootstrap() -> int:
|
|
350
|
+
"""Bootstrap the ONEX runtime from contracts.
|
|
351
|
+
|
|
352
|
+
This is the main async entrypoint that orchestrates the complete runtime
|
|
353
|
+
initialization and lifecycle management. The bootstrap process follows a
|
|
354
|
+
structured sequence to ensure proper resource initialization and cleanup.
|
|
355
|
+
|
|
356
|
+
Bootstrap Sequence:
|
|
357
|
+
1. Determine contracts directory from ONEX_CONTRACTS_DIR environment variable
|
|
358
|
+
2. Load and validate runtime configuration from contracts or environment
|
|
359
|
+
3. Create and initialize event bus (EventBusInmemory or EventBusKafka based on config)
|
|
360
|
+
4. Create ModelONEXContainer and wire infrastructure services (async)
|
|
361
|
+
5. Resolve RegistryProtocolBinding from container (async)
|
|
362
|
+
6. Instantiate RuntimeHostProcess with validated configuration and pre-resolved registry
|
|
363
|
+
7. Setup graceful shutdown signal handlers (SIGINT, SIGTERM)
|
|
364
|
+
8. Start runtime and HTTP health server for Docker/Kubernetes health probes
|
|
365
|
+
9. Run runtime until shutdown signal received
|
|
366
|
+
10. Perform graceful shutdown with configurable timeout
|
|
367
|
+
11. Clean up resources in finally block to prevent resource leaks
|
|
368
|
+
|
|
369
|
+
Error Handling:
|
|
370
|
+
- Configuration errors: Logged with full context and correlation_id
|
|
371
|
+
- Runtime errors: Caught and logged with detailed error information
|
|
372
|
+
- Unexpected errors: Logged with exception details for debugging
|
|
373
|
+
- All errors include correlation_id for distributed tracing
|
|
374
|
+
|
|
375
|
+
Shutdown Behavior:
|
|
376
|
+
- Health server stopped first (fast, non-blocking operation)
|
|
377
|
+
- Runtime stopped with configurable grace period (default: 30s)
|
|
378
|
+
- Timeout enforcement prevents indefinite shutdown hangs
|
|
379
|
+
- Finally block ensures cleanup even on unexpected errors
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
Exit code (0 for success, non-zero for errors).
|
|
383
|
+
- 0: Clean shutdown after successful operation
|
|
384
|
+
- 1: Configuration error, runtime error, or unexpected failure
|
|
385
|
+
|
|
386
|
+
Environment Variables:
|
|
387
|
+
ONEX_CONTRACTS_DIR: Path to contracts directory (default: ./contracts)
|
|
388
|
+
ONEX_HTTP_PORT: Port for health check server (default: 8085)
|
|
389
|
+
ONEX_LOG_LEVEL: Logging level (default: INFO)
|
|
390
|
+
ONEX_ENVIRONMENT: Environment name (default: local)
|
|
391
|
+
ONEX_INPUT_TOPIC: Input topic override (default: requests)
|
|
392
|
+
ONEX_OUTPUT_TOPIC: Output topic override (default: responses)
|
|
393
|
+
ONEX_GROUP_ID: Consumer group override (default: onex-runtime)
|
|
394
|
+
|
|
395
|
+
Example:
|
|
396
|
+
>>> # Run bootstrap and handle exit code
|
|
397
|
+
>>> exit_code = await bootstrap()
|
|
398
|
+
>>> if exit_code == 0:
|
|
399
|
+
... print("Runtime shutdown successfully")
|
|
400
|
+
... else:
|
|
401
|
+
... print("Runtime encountered errors")
|
|
402
|
+
|
|
403
|
+
Example Startup Log:
|
|
404
|
+
============================================================
|
|
405
|
+
ONEX Runtime Kernel v0.1.0
|
|
406
|
+
Environment: production
|
|
407
|
+
Contracts: /app/contracts
|
|
408
|
+
Event Bus: inmemory (group: onex-runtime)
|
|
409
|
+
Topics: requests → responses
|
|
410
|
+
Health endpoint: http://0.0.0.0:8085/health
|
|
411
|
+
============================================================
|
|
412
|
+
"""
|
|
413
|
+
# Lazy import to break circular dependency chain - see "Circular Import Note"
|
|
414
|
+
# comment near line 98 for detailed explanation of the import cycle.
|
|
415
|
+
from omnibase_infra.services.service_health import (
|
|
416
|
+
DEFAULT_HTTP_PORT,
|
|
417
|
+
ServiceHealth,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# Initialize resources to None for cleanup guard in finally block
|
|
421
|
+
runtime: RuntimeHostProcess | None = None
|
|
422
|
+
health_server: ServiceHealth | None = None
|
|
423
|
+
postgres_pool: asyncpg.Pool | None = None
|
|
424
|
+
introspection_unsubscribe: Callable[[], Awaitable[None]] | None = None
|
|
425
|
+
correlation_id = generate_correlation_id()
|
|
426
|
+
bootstrap_start_time = time.time()
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
# 1. Determine contracts directory
|
|
430
|
+
contracts_dir = _get_contracts_dir()
|
|
431
|
+
logger.info(
|
|
432
|
+
"ONEX Kernel starting with contracts_dir=%s (correlation_id=%s)",
|
|
433
|
+
contracts_dir,
|
|
434
|
+
correlation_id,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# 2. Load runtime configuration (may raise ProtocolConfigurationError)
|
|
438
|
+
# Pass correlation_id for consistent tracing across initialization sequence
|
|
439
|
+
config_start_time = time.time()
|
|
440
|
+
config = load_runtime_config(contracts_dir, correlation_id=correlation_id)
|
|
441
|
+
config_duration = time.time() - config_start_time
|
|
442
|
+
# Log only safe config fields (no credentials or sensitive data)
|
|
443
|
+
# Full config.model_dump() could leak passwords, API keys, connection strings
|
|
444
|
+
logger.debug(
|
|
445
|
+
"Runtime config loaded in %.3fs (correlation_id=%s)",
|
|
446
|
+
config_duration,
|
|
447
|
+
correlation_id,
|
|
448
|
+
extra={
|
|
449
|
+
"duration_seconds": config_duration,
|
|
450
|
+
"input_topic": config.input_topic,
|
|
451
|
+
"output_topic": config.output_topic,
|
|
452
|
+
"consumer_group": config.consumer_group,
|
|
453
|
+
"event_bus_type": config.event_bus.type,
|
|
454
|
+
"shutdown_grace_period": config.shutdown.grace_period_seconds,
|
|
455
|
+
},
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
# 3. Create event bus
|
|
459
|
+
# Dispatch based on configuration or environment variable:
|
|
460
|
+
# - If KAFKA_BOOTSTRAP_SERVERS env var is set, use EventBusKafka
|
|
461
|
+
# - If config.event_bus.type == "kafka", use EventBusKafka
|
|
462
|
+
# - Otherwise, use EventBusInmemory for local development/testing
|
|
463
|
+
# Environment override takes precedence over config for environment field.
|
|
464
|
+
environment = os.getenv("ONEX_ENVIRONMENT") or config.event_bus.environment
|
|
465
|
+
kafka_bootstrap_servers = os.getenv("KAFKA_BOOTSTRAP_SERVERS")
|
|
466
|
+
|
|
467
|
+
# Explicit bool evaluation (not truthy string) for kafka usage.
|
|
468
|
+
# KAFKA_BOOTSTRAP_SERVERS env var takes precedence over config.event_bus.type.
|
|
469
|
+
# This prevents implicit "kafka but localhost" fallback scenarios.
|
|
470
|
+
use_kafka: bool = (
|
|
471
|
+
bool(kafka_bootstrap_servers) or config.event_bus.type == "kafka"
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Validate bootstrap_servers is provided when kafka is requested via config
|
|
475
|
+
# This prevents confusing implicit localhost:9092 fallback
|
|
476
|
+
if use_kafka and not kafka_bootstrap_servers:
|
|
477
|
+
context = ModelInfraErrorContext(
|
|
478
|
+
transport_type=EnumInfraTransportType.KAFKA,
|
|
479
|
+
operation="configure_event_bus",
|
|
480
|
+
correlation_id=correlation_id,
|
|
481
|
+
)
|
|
482
|
+
raise ProtocolConfigurationError(
|
|
483
|
+
"Kafka event bus requested (config.event_bus.type='kafka') but "
|
|
484
|
+
"KAFKA_BOOTSTRAP_SERVERS environment variable is not set. "
|
|
485
|
+
"Set KAFKA_BOOTSTRAP_SERVERS to the broker address (e.g., 'kafka:9092') "
|
|
486
|
+
"or use event_bus.type='inmemory' for local development.",
|
|
487
|
+
context=context,
|
|
488
|
+
parameter="KAFKA_BOOTSTRAP_SERVERS",
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
event_bus_start_time = time.time()
|
|
492
|
+
event_bus: EventBusInmemory | EventBusKafka
|
|
493
|
+
event_bus_type: str
|
|
494
|
+
|
|
495
|
+
if use_kafka:
|
|
496
|
+
# Use EventBusKafka for production/integration testing
|
|
497
|
+
# NOTE: bootstrap_servers is guaranteed non-empty at this point due to validation
|
|
498
|
+
# above, but mypy cannot narrow the Optional[str] type through control flow.
|
|
499
|
+
kafka_config = ModelKafkaEventBusConfig(
|
|
500
|
+
bootstrap_servers=kafka_bootstrap_servers, # type: ignore[arg-type] # NOTE: control flow narrowing limitation
|
|
501
|
+
environment=environment,
|
|
502
|
+
group=config.consumer_group,
|
|
503
|
+
circuit_breaker_threshold=config.event_bus.circuit_breaker_threshold,
|
|
504
|
+
)
|
|
505
|
+
event_bus = EventBusKafka(config=kafka_config)
|
|
506
|
+
event_bus_type = "kafka"
|
|
507
|
+
|
|
508
|
+
# Start EventBusKafka to connect to Kafka/Redpanda and enable consumers
|
|
509
|
+
# Without this, the event bus cannot publish or consume messages
|
|
510
|
+
try:
|
|
511
|
+
await event_bus.start()
|
|
512
|
+
logger.debug(
|
|
513
|
+
"EventBusKafka started successfully (correlation_id=%s)",
|
|
514
|
+
correlation_id,
|
|
515
|
+
)
|
|
516
|
+
except Exception as e:
|
|
517
|
+
context = ModelInfraErrorContext(
|
|
518
|
+
transport_type=EnumInfraTransportType.KAFKA,
|
|
519
|
+
operation="start_event_bus",
|
|
520
|
+
correlation_id=correlation_id,
|
|
521
|
+
target_name=kafka_bootstrap_servers,
|
|
522
|
+
)
|
|
523
|
+
raise RuntimeHostError(
|
|
524
|
+
f"Failed to start EventBusKafka: {sanitize_error_message(e)}",
|
|
525
|
+
context=context,
|
|
526
|
+
) from e
|
|
527
|
+
|
|
528
|
+
logger.info(
|
|
529
|
+
"Using EventBusKafka (correlation_id=%s)",
|
|
530
|
+
correlation_id,
|
|
531
|
+
extra={
|
|
532
|
+
"bootstrap_servers": kafka_bootstrap_servers,
|
|
533
|
+
"environment": environment,
|
|
534
|
+
"consumer_group": config.consumer_group,
|
|
535
|
+
},
|
|
536
|
+
)
|
|
537
|
+
else:
|
|
538
|
+
# Use EventBusInmemory for local development/testing
|
|
539
|
+
event_bus = EventBusInmemory(
|
|
540
|
+
environment=environment,
|
|
541
|
+
group=config.consumer_group,
|
|
542
|
+
)
|
|
543
|
+
event_bus_type = "inmemory"
|
|
544
|
+
|
|
545
|
+
event_bus_duration = time.time() - event_bus_start_time
|
|
546
|
+
logger.debug(
|
|
547
|
+
"Event bus created in %.3fs (correlation_id=%s)",
|
|
548
|
+
event_bus_duration,
|
|
549
|
+
correlation_id,
|
|
550
|
+
extra={
|
|
551
|
+
"duration_seconds": event_bus_duration,
|
|
552
|
+
"event_bus_type": event_bus_type,
|
|
553
|
+
"environment": environment,
|
|
554
|
+
"consumer_group": config.consumer_group,
|
|
555
|
+
},
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
# 4. Create and wire container for dependency injection
|
|
559
|
+
container_start_time = time.time()
|
|
560
|
+
container = ModelONEXContainer()
|
|
561
|
+
if container.service_registry is None:
|
|
562
|
+
logger.warning(
|
|
563
|
+
"DEGRADED_MODE: service_registry is None (omnibase_core circular import bug?), "
|
|
564
|
+
"skipping container wiring (correlation_id=%s)",
|
|
565
|
+
correlation_id,
|
|
566
|
+
extra={
|
|
567
|
+
"error_type": "NoneType",
|
|
568
|
+
"correlation_id": correlation_id,
|
|
569
|
+
"degraded_mode": True,
|
|
570
|
+
"degraded_reason": "service_registry_unavailable",
|
|
571
|
+
"component": "container_wiring",
|
|
572
|
+
},
|
|
573
|
+
)
|
|
574
|
+
wire_summary: dict[str, list[str] | str] = {
|
|
575
|
+
"services": [],
|
|
576
|
+
"status": "degraded",
|
|
577
|
+
} # Empty summary for degraded mode
|
|
578
|
+
else:
|
|
579
|
+
try:
|
|
580
|
+
wire_summary = await wire_infrastructure_services(container)
|
|
581
|
+
except ServiceResolutionError as e:
|
|
582
|
+
# Service resolution failed during wiring - container configuration issue.
|
|
583
|
+
logger.warning(
|
|
584
|
+
"DEGRADED_MODE: Container wiring failed due to service resolution error, "
|
|
585
|
+
"continuing in degraded mode (correlation_id=%s): %s",
|
|
586
|
+
correlation_id,
|
|
587
|
+
e,
|
|
588
|
+
extra={
|
|
589
|
+
"error_type": type(e).__name__,
|
|
590
|
+
"correlation_id": correlation_id,
|
|
591
|
+
"degraded_mode": True,
|
|
592
|
+
"degraded_reason": "service_resolution_error",
|
|
593
|
+
"component": "container_wiring",
|
|
594
|
+
},
|
|
595
|
+
)
|
|
596
|
+
wire_summary = {"services": [], "status": "degraded"}
|
|
597
|
+
except (RuntimeError, AttributeError) as e:
|
|
598
|
+
# Unexpected error during wiring - container internals issue.
|
|
599
|
+
logger.warning(
|
|
600
|
+
"DEGRADED_MODE: Container wiring failed with unexpected error, "
|
|
601
|
+
"continuing in degraded mode (correlation_id=%s): %s",
|
|
602
|
+
correlation_id,
|
|
603
|
+
e,
|
|
604
|
+
extra={
|
|
605
|
+
"error_type": type(e).__name__,
|
|
606
|
+
"correlation_id": correlation_id,
|
|
607
|
+
"degraded_mode": True,
|
|
608
|
+
"degraded_reason": "wiring_error",
|
|
609
|
+
"component": "container_wiring",
|
|
610
|
+
},
|
|
611
|
+
)
|
|
612
|
+
wire_summary = {"services": [], "status": "degraded"}
|
|
613
|
+
container_duration = time.time() - container_start_time
|
|
614
|
+
logger.debug(
|
|
615
|
+
"Container wired in %.3fs (correlation_id=%s)",
|
|
616
|
+
container_duration,
|
|
617
|
+
correlation_id,
|
|
618
|
+
extra={
|
|
619
|
+
"duration_seconds": container_duration,
|
|
620
|
+
"services": wire_summary["services"],
|
|
621
|
+
},
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
# 4.5. Create PostgreSQL pool for projections
|
|
625
|
+
# Only create if POSTGRES_HOST is set (indicates registration should be enabled)
|
|
626
|
+
projector: ProjectorShell | None = None
|
|
627
|
+
introspection_dispatcher: DispatcherNodeIntrospected | None = None
|
|
628
|
+
consul_handler = None # Will be initialized if Consul is configured
|
|
629
|
+
|
|
630
|
+
postgres_host = os.getenv("POSTGRES_HOST")
|
|
631
|
+
if postgres_host:
|
|
632
|
+
postgres_pool_start_time = time.time()
|
|
633
|
+
try:
|
|
634
|
+
postgres_pool = await asyncpg.create_pool(
|
|
635
|
+
user=os.getenv("POSTGRES_USER", "postgres"),
|
|
636
|
+
password=os.getenv("POSTGRES_PASSWORD", ""),
|
|
637
|
+
host=postgres_host,
|
|
638
|
+
port=int(os.getenv("POSTGRES_PORT", "5432")),
|
|
639
|
+
database=os.getenv("POSTGRES_DATABASE", "omninode_bridge"),
|
|
640
|
+
min_size=2,
|
|
641
|
+
max_size=10,
|
|
642
|
+
)
|
|
643
|
+
postgres_pool_duration = time.time() - postgres_pool_start_time
|
|
644
|
+
logger.info(
|
|
645
|
+
"PostgreSQL pool created in %.3fs (correlation_id=%s)",
|
|
646
|
+
postgres_pool_duration,
|
|
647
|
+
correlation_id,
|
|
648
|
+
extra={
|
|
649
|
+
"host": postgres_host,
|
|
650
|
+
"port": os.getenv("POSTGRES_PORT", "5432"),
|
|
651
|
+
"database": os.getenv("POSTGRES_DATABASE", "omninode_bridge"),
|
|
652
|
+
},
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
# 4.6. Load projectors from contracts via ProjectorPluginLoader (OMN-1170/1169)
|
|
656
|
+
#
|
|
657
|
+
# This section implements fully contract-driven projector management. The
|
|
658
|
+
# loader discovers projector contracts from the package's projectors/contracts
|
|
659
|
+
# directory and creates ProjectorShell instances for runtime use.
|
|
660
|
+
#
|
|
661
|
+
# Contract-driven approach:
|
|
662
|
+
# - Projector behavior defined in YAML contracts (registration_projector.yaml)
|
|
663
|
+
# - ProjectorPluginLoader discovers and loads projectors from contracts
|
|
664
|
+
# - ProjectorShell provides generic projection operations (project, partial_update)
|
|
665
|
+
# - Schema initialization is decoupled - SQL executed directly from schema file
|
|
666
|
+
#
|
|
667
|
+
# The registration projector is identified by projector_id="registration-projector"
|
|
668
|
+
# and is passed to wire_registration_handlers() for handler injection.
|
|
669
|
+
#
|
|
670
|
+
projector_contracts_dir = (
|
|
671
|
+
Path(__file__).parent.parent / "projectors" / "contracts"
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
# Try to discover projectors from contracts
|
|
675
|
+
projector_loader = ProjectorPluginLoader(
|
|
676
|
+
config=ModelProjectorPluginLoaderConfig(graceful_mode=True),
|
|
677
|
+
container=container,
|
|
678
|
+
pool=postgres_pool,
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
discovered_projectors: list[ProtocolEventProjector] = []
|
|
682
|
+
if projector_contracts_dir.exists():
|
|
683
|
+
try:
|
|
684
|
+
discovered_projectors = (
|
|
685
|
+
await projector_loader.load_from_directory(
|
|
686
|
+
projector_contracts_dir
|
|
687
|
+
)
|
|
688
|
+
)
|
|
689
|
+
if discovered_projectors:
|
|
690
|
+
logger.info(
|
|
691
|
+
"Discovered %d projector(s) from contracts (correlation_id=%s)",
|
|
692
|
+
len(discovered_projectors),
|
|
693
|
+
correlation_id,
|
|
694
|
+
extra={
|
|
695
|
+
"discovered_count": len(discovered_projectors),
|
|
696
|
+
"contracts_dir": str(projector_contracts_dir),
|
|
697
|
+
"projector_ids": [
|
|
698
|
+
getattr(p, "projector_id", "unknown")
|
|
699
|
+
for p in discovered_projectors
|
|
700
|
+
],
|
|
701
|
+
},
|
|
702
|
+
)
|
|
703
|
+
else:
|
|
704
|
+
logger.warning(
|
|
705
|
+
"No projector contracts found in %s (correlation_id=%s)",
|
|
706
|
+
projector_contracts_dir,
|
|
707
|
+
correlation_id,
|
|
708
|
+
extra={
|
|
709
|
+
"contracts_dir": str(projector_contracts_dir),
|
|
710
|
+
},
|
|
711
|
+
)
|
|
712
|
+
except Exception as discovery_error:
|
|
713
|
+
# Log warning but continue - projector discovery is best-effort
|
|
714
|
+
# Registration features will be unavailable if discovery fails
|
|
715
|
+
logger.warning(
|
|
716
|
+
"Projector contract discovery failed: %s (correlation_id=%s)",
|
|
717
|
+
sanitize_error_message(discovery_error),
|
|
718
|
+
correlation_id,
|
|
719
|
+
extra={
|
|
720
|
+
"error_type": type(discovery_error).__name__,
|
|
721
|
+
"contracts_dir": str(projector_contracts_dir),
|
|
722
|
+
},
|
|
723
|
+
)
|
|
724
|
+
else:
|
|
725
|
+
logger.debug(
|
|
726
|
+
"Projector contracts directory not found, skipping discovery "
|
|
727
|
+
"(correlation_id=%s)",
|
|
728
|
+
correlation_id,
|
|
729
|
+
extra={
|
|
730
|
+
"contracts_dir": str(projector_contracts_dir),
|
|
731
|
+
},
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
# Extract registration projector from discovered projectors (OMN-1169)
|
|
735
|
+
# This replaces the legacy ProjectorRegistration with contract-loaded ProjectorShell
|
|
736
|
+
registration_projector_id = "registration-projector"
|
|
737
|
+
for discovered in discovered_projectors:
|
|
738
|
+
if (
|
|
739
|
+
getattr(discovered, "projector_id", None)
|
|
740
|
+
== registration_projector_id
|
|
741
|
+
):
|
|
742
|
+
# Cast to ProjectorShell (loader creates ProjectorShell when pool provided)
|
|
743
|
+
if isinstance(discovered, ProjectorShell):
|
|
744
|
+
projector = discovered
|
|
745
|
+
logger.info(
|
|
746
|
+
"Using contract-loaded ProjectorShell for registration "
|
|
747
|
+
"(correlation_id=%s)",
|
|
748
|
+
correlation_id,
|
|
749
|
+
extra={
|
|
750
|
+
"projector_id": registration_projector_id,
|
|
751
|
+
"aggregate_type": projector.aggregate_type,
|
|
752
|
+
},
|
|
753
|
+
)
|
|
754
|
+
break
|
|
755
|
+
|
|
756
|
+
if projector is None:
|
|
757
|
+
# Fallback: No registration projector discovered from contracts
|
|
758
|
+
logger.warning(
|
|
759
|
+
"Registration projector not found in contracts, "
|
|
760
|
+
"registration features will be unavailable (correlation_id=%s)",
|
|
761
|
+
correlation_id,
|
|
762
|
+
extra={
|
|
763
|
+
"expected_projector_id": registration_projector_id,
|
|
764
|
+
"discovered_count": len(discovered_projectors),
|
|
765
|
+
},
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
# Initialize schema by executing SQL file directly
|
|
769
|
+
# Schema initialization is decoupled from the projector - it just ensures
|
|
770
|
+
# the table and indexes exist. The ProjectorShell uses the schema at runtime.
|
|
771
|
+
schema_file = (
|
|
772
|
+
Path(__file__).parent.parent
|
|
773
|
+
/ "schemas"
|
|
774
|
+
/ "schema_registration_projection.sql"
|
|
775
|
+
)
|
|
776
|
+
if schema_file.exists():
|
|
777
|
+
try:
|
|
778
|
+
schema_sql = schema_file.read_text()
|
|
779
|
+
async with postgres_pool.acquire() as conn:
|
|
780
|
+
await conn.execute(schema_sql)
|
|
781
|
+
logger.info(
|
|
782
|
+
"Registration projection schema initialized (correlation_id=%s)",
|
|
783
|
+
correlation_id,
|
|
784
|
+
)
|
|
785
|
+
except Exception as schema_error:
|
|
786
|
+
# Log warning but continue - schema may already exist
|
|
787
|
+
logger.warning(
|
|
788
|
+
"Schema initialization encountered error: %s (correlation_id=%s)",
|
|
789
|
+
sanitize_error_message(schema_error),
|
|
790
|
+
correlation_id,
|
|
791
|
+
extra={
|
|
792
|
+
"error_type": type(schema_error).__name__,
|
|
793
|
+
},
|
|
794
|
+
)
|
|
795
|
+
else:
|
|
796
|
+
logger.warning(
|
|
797
|
+
"Schema file not found: %s (correlation_id=%s)",
|
|
798
|
+
schema_file,
|
|
799
|
+
correlation_id,
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
# 4.6.5. Initialize HandlerConsul if Consul is configured
|
|
803
|
+
# CONSUL_HOST determines whether to enable Consul registration
|
|
804
|
+
consul_host = os.getenv("CONSUL_HOST")
|
|
805
|
+
if consul_host:
|
|
806
|
+
# Validate CONSUL_PORT environment variable
|
|
807
|
+
consul_port_str = os.getenv("CONSUL_PORT", "8500")
|
|
808
|
+
try:
|
|
809
|
+
consul_port = int(consul_port_str)
|
|
810
|
+
if not MIN_PORT <= consul_port <= MAX_PORT:
|
|
811
|
+
logger.warning(
|
|
812
|
+
"CONSUL_PORT %d outside valid range %d-%d, using default 8500 (correlation_id=%s)",
|
|
813
|
+
consul_port,
|
|
814
|
+
MIN_PORT,
|
|
815
|
+
MAX_PORT,
|
|
816
|
+
correlation_id,
|
|
817
|
+
)
|
|
818
|
+
consul_port = 8500
|
|
819
|
+
except ValueError:
|
|
820
|
+
logger.warning(
|
|
821
|
+
"Invalid CONSUL_PORT value '%s', using default 8500 (correlation_id=%s)",
|
|
822
|
+
consul_port_str,
|
|
823
|
+
correlation_id,
|
|
824
|
+
)
|
|
825
|
+
consul_port = 8500
|
|
826
|
+
|
|
827
|
+
try:
|
|
828
|
+
# Deferred import: Only load HandlerConsul when Consul is configured.
|
|
829
|
+
# This avoids loading the consul dependency (and its transitive deps)
|
|
830
|
+
# when Consul integration is disabled, reducing startup time.
|
|
831
|
+
from omnibase_infra.handlers import HandlerConsul
|
|
832
|
+
|
|
833
|
+
consul_handler = HandlerConsul()
|
|
834
|
+
await consul_handler.initialize(
|
|
835
|
+
{
|
|
836
|
+
"host": consul_host,
|
|
837
|
+
"port": consul_port,
|
|
838
|
+
}
|
|
839
|
+
)
|
|
840
|
+
logger.info(
|
|
841
|
+
"HandlerConsul initialized for dual registration (correlation_id=%s)",
|
|
842
|
+
correlation_id,
|
|
843
|
+
extra={
|
|
844
|
+
"consul_host": consul_host,
|
|
845
|
+
"consul_port": consul_port,
|
|
846
|
+
},
|
|
847
|
+
)
|
|
848
|
+
except Exception as consul_error:
|
|
849
|
+
# Log warning but continue without Consul (PostgreSQL is source of truth)
|
|
850
|
+
# Use sanitize_error_message to prevent credential leakage in logs
|
|
851
|
+
logger.warning(
|
|
852
|
+
"Failed to initialize HandlerConsul, proceeding without Consul: %s (correlation_id=%s)",
|
|
853
|
+
sanitize_error_message(consul_error),
|
|
854
|
+
correlation_id,
|
|
855
|
+
extra={
|
|
856
|
+
"error_type": type(consul_error).__name__,
|
|
857
|
+
},
|
|
858
|
+
)
|
|
859
|
+
consul_handler = None
|
|
860
|
+
else:
|
|
861
|
+
logger.debug(
|
|
862
|
+
"CONSUL_HOST not set, Consul registration disabled (correlation_id=%s)",
|
|
863
|
+
correlation_id,
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
# 4.7. Wire registration handlers with projector and consul_handler
|
|
867
|
+
registration_summary = await wire_registration_handlers(
|
|
868
|
+
container,
|
|
869
|
+
postgres_pool,
|
|
870
|
+
projector=projector,
|
|
871
|
+
consul_handler=consul_handler,
|
|
872
|
+
)
|
|
873
|
+
logger.info(
|
|
874
|
+
"Registration handlers wired (correlation_id=%s)",
|
|
875
|
+
correlation_id,
|
|
876
|
+
extra={
|
|
877
|
+
"services": registration_summary["services"],
|
|
878
|
+
},
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
# 4.8. Create introspection dispatcher for routing events
|
|
882
|
+
# Deferred import: HandlerNodeIntrospected depends on PostgreSQL and
|
|
883
|
+
# registration infrastructure. Only loaded after PostgreSQL pool is
|
|
884
|
+
# successfully created and registration handlers are wired.
|
|
885
|
+
from omnibase_infra.nodes.node_registration_orchestrator.handlers import (
|
|
886
|
+
HandlerNodeIntrospected,
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
# Check if service_registry is available (may be None in omnibase_core 0.6.x)
|
|
890
|
+
if container.service_registry is None:
|
|
891
|
+
logger.warning(
|
|
892
|
+
"DEGRADED_MODE: ServiceRegistry not available, skipping introspection dispatcher creation (correlation_id=%s)",
|
|
893
|
+
correlation_id,
|
|
894
|
+
extra={
|
|
895
|
+
"error_type": "NoneType",
|
|
896
|
+
"correlation_id": correlation_id,
|
|
897
|
+
"degraded_mode": True,
|
|
898
|
+
"degraded_reason": "service_registry_unavailable",
|
|
899
|
+
"component": "introspection_dispatcher",
|
|
900
|
+
},
|
|
901
|
+
)
|
|
902
|
+
# Set introspection_dispatcher to None and continue without it
|
|
903
|
+
introspection_dispatcher = None
|
|
904
|
+
else:
|
|
905
|
+
logger.debug(
|
|
906
|
+
"Resolving HandlerNodeIntrospected from container (correlation_id=%s)",
|
|
907
|
+
correlation_id,
|
|
908
|
+
)
|
|
909
|
+
handler_introspected: HandlerNodeIntrospected = (
|
|
910
|
+
await container.service_registry.resolve_service(
|
|
911
|
+
HandlerNodeIntrospected
|
|
912
|
+
)
|
|
913
|
+
)
|
|
914
|
+
logger.debug(
|
|
915
|
+
"HandlerNodeIntrospected resolved successfully (correlation_id=%s)",
|
|
916
|
+
correlation_id,
|
|
917
|
+
extra={
|
|
918
|
+
"handler_class": handler_introspected.__class__.__name__,
|
|
919
|
+
},
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
introspection_dispatcher = DispatcherNodeIntrospected(
|
|
923
|
+
handler_introspected
|
|
924
|
+
)
|
|
925
|
+
logger.info(
|
|
926
|
+
"Introspection dispatcher created and wired (correlation_id=%s)",
|
|
927
|
+
correlation_id,
|
|
928
|
+
extra={
|
|
929
|
+
"dispatcher_class": introspection_dispatcher.__class__.__name__,
|
|
930
|
+
"handler_class": handler_introspected.__class__.__name__,
|
|
931
|
+
},
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
except Exception as pool_error:
|
|
935
|
+
# Log warning but continue without registration support
|
|
936
|
+
# Use sanitize_error_message to prevent credential leakage in logs
|
|
937
|
+
# (PostgreSQL connection errors may include DSN with password)
|
|
938
|
+
logger.warning(
|
|
939
|
+
"Failed to initialize PostgreSQL pool for registration: %s (correlation_id=%s)",
|
|
940
|
+
sanitize_error_message(pool_error),
|
|
941
|
+
correlation_id,
|
|
942
|
+
extra={
|
|
943
|
+
"error_type": type(pool_error).__name__,
|
|
944
|
+
},
|
|
945
|
+
)
|
|
946
|
+
if postgres_pool is not None:
|
|
947
|
+
try:
|
|
948
|
+
await postgres_pool.close()
|
|
949
|
+
except Exception as cleanup_error:
|
|
950
|
+
# Sanitize cleanup errors to prevent credential leakage
|
|
951
|
+
# NOTE: Do NOT use exc_info=True here - tracebacks may contain
|
|
952
|
+
# connection strings with credentials from PostgreSQL errors
|
|
953
|
+
logger.warning(
|
|
954
|
+
"Cleanup failed for PostgreSQL pool close: %s (correlation_id=%s)",
|
|
955
|
+
sanitize_error_message(cleanup_error),
|
|
956
|
+
correlation_id,
|
|
957
|
+
)
|
|
958
|
+
postgres_pool = None
|
|
959
|
+
projector = None
|
|
960
|
+
introspection_dispatcher = None
|
|
961
|
+
else:
|
|
962
|
+
logger.debug(
|
|
963
|
+
"POSTGRES_HOST not set, skipping registration handler wiring (correlation_id=%s)",
|
|
964
|
+
correlation_id,
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
# 5. Resolve RegistryProtocolBinding from container or create new instance
|
|
968
|
+
# NOTE: Fallback to creating new instance is intentional degraded mode behavior.
|
|
969
|
+
# The handler registry is optional for basic runtime operation - core event
|
|
970
|
+
# processing continues even without explicit handler bindings. However,
|
|
971
|
+
# ProtocolConfigurationError should NOT be masked as it indicates invalid
|
|
972
|
+
# configuration that would cause undefined behavior.
|
|
973
|
+
handler_registry: RegistryProtocolBinding | None = None
|
|
974
|
+
|
|
975
|
+
# Check if service_registry is available (may be None in omnibase_core 0.6.x)
|
|
976
|
+
if container.service_registry is not None:
|
|
977
|
+
try:
|
|
978
|
+
handler_registry = await container.service_registry.resolve_service(
|
|
979
|
+
RegistryProtocolBinding
|
|
980
|
+
)
|
|
981
|
+
except ServiceResolutionError as e:
|
|
982
|
+
# Service not registered - expected in minimal configurations.
|
|
983
|
+
# Create a new instance directly as fallback.
|
|
984
|
+
logger.warning(
|
|
985
|
+
"DEGRADED_MODE: RegistryProtocolBinding not registered in container, "
|
|
986
|
+
"creating new instance (correlation_id=%s): %s",
|
|
987
|
+
correlation_id,
|
|
988
|
+
e,
|
|
989
|
+
extra={
|
|
990
|
+
"error_type": type(e).__name__,
|
|
991
|
+
"correlation_id": correlation_id,
|
|
992
|
+
"degraded_mode": True,
|
|
993
|
+
"degraded_reason": "service_not_registered",
|
|
994
|
+
"component": "handler_registry",
|
|
995
|
+
},
|
|
996
|
+
)
|
|
997
|
+
handler_registry = RegistryProtocolBinding()
|
|
998
|
+
except (RuntimeError, AttributeError) as e:
|
|
999
|
+
# Unexpected resolution failure - container internals issue.
|
|
1000
|
+
# Log with more diagnostic context but still allow degraded operation.
|
|
1001
|
+
logger.warning(
|
|
1002
|
+
"DEGRADED_MODE: Unexpected error resolving RegistryProtocolBinding, "
|
|
1003
|
+
"creating new instance (correlation_id=%s): %s",
|
|
1004
|
+
correlation_id,
|
|
1005
|
+
e,
|
|
1006
|
+
extra={
|
|
1007
|
+
"error_type": type(e).__name__,
|
|
1008
|
+
"correlation_id": correlation_id,
|
|
1009
|
+
"degraded_mode": True,
|
|
1010
|
+
"degraded_reason": "resolution_error",
|
|
1011
|
+
"component": "handler_registry",
|
|
1012
|
+
},
|
|
1013
|
+
)
|
|
1014
|
+
handler_registry = RegistryProtocolBinding()
|
|
1015
|
+
# NOTE: ProtocolConfigurationError is NOT caught here - configuration
|
|
1016
|
+
# errors should propagate and stop startup to prevent undefined behavior.
|
|
1017
|
+
else:
|
|
1018
|
+
# ServiceRegistry not available, create a new RegistryProtocolBinding directly
|
|
1019
|
+
logger.warning(
|
|
1020
|
+
"DEGRADED_MODE: ServiceRegistry not available, creating RegistryProtocolBinding directly (correlation_id=%s)",
|
|
1021
|
+
correlation_id,
|
|
1022
|
+
extra={
|
|
1023
|
+
"error_type": "NoneType",
|
|
1024
|
+
"correlation_id": correlation_id,
|
|
1025
|
+
"degraded_mode": True,
|
|
1026
|
+
"degraded_reason": "service_registry_unavailable",
|
|
1027
|
+
"component": "handler_registry",
|
|
1028
|
+
},
|
|
1029
|
+
)
|
|
1030
|
+
handler_registry = RegistryProtocolBinding()
|
|
1031
|
+
|
|
1032
|
+
# 6. Create runtime host process with config and pre-resolved registry
|
|
1033
|
+
# RuntimeHostProcess accepts config as dict; cast model_dump() result to
|
|
1034
|
+
# dict[str, object] to avoid implicit Any typing (Pydantic's model_dump()
|
|
1035
|
+
# returns dict[str, Any] but all our model fields are strongly typed)
|
|
1036
|
+
runtime_create_start_time = time.time()
|
|
1037
|
+
runtime = RuntimeHostProcess(
|
|
1038
|
+
container=container,
|
|
1039
|
+
event_bus=event_bus,
|
|
1040
|
+
input_topic=config.input_topic,
|
|
1041
|
+
output_topic=config.output_topic,
|
|
1042
|
+
config=cast("dict[str, object]", config.model_dump()),
|
|
1043
|
+
handler_registry=handler_registry,
|
|
1044
|
+
# Pass contracts directory for handler discovery (OMN-1317)
|
|
1045
|
+
# This enables contract-based handler registration instead of
|
|
1046
|
+
# falling back to wire_handlers() with an empty registry
|
|
1047
|
+
contract_paths=[str(contracts_dir)],
|
|
1048
|
+
)
|
|
1049
|
+
runtime_create_duration = time.time() - runtime_create_start_time
|
|
1050
|
+
logger.debug(
|
|
1051
|
+
"Runtime host process created in %.3fs (correlation_id=%s)",
|
|
1052
|
+
runtime_create_duration,
|
|
1053
|
+
correlation_id,
|
|
1054
|
+
extra={
|
|
1055
|
+
"duration_seconds": runtime_create_duration,
|
|
1056
|
+
"input_topic": config.input_topic,
|
|
1057
|
+
"output_topic": config.output_topic,
|
|
1058
|
+
},
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
# 7. Setup graceful shutdown
|
|
1062
|
+
shutdown_event = asyncio.Event()
|
|
1063
|
+
loop = asyncio.get_running_loop()
|
|
1064
|
+
|
|
1065
|
+
def handle_shutdown(sig: signal.Signals) -> None:
|
|
1066
|
+
"""Handle shutdown signal with correlation tracking."""
|
|
1067
|
+
logger.info(
|
|
1068
|
+
"Received %s, initiating graceful shutdown... (correlation_id=%s)",
|
|
1069
|
+
sig.name,
|
|
1070
|
+
correlation_id,
|
|
1071
|
+
)
|
|
1072
|
+
shutdown_event.set()
|
|
1073
|
+
|
|
1074
|
+
# Register signal handlers for graceful shutdown
|
|
1075
|
+
if sys.platform != "win32":
|
|
1076
|
+
# Unix: Use asyncio's signal handler for proper event loop integration
|
|
1077
|
+
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
1078
|
+
loop.add_signal_handler(sig, handle_shutdown, sig)
|
|
1079
|
+
else:
|
|
1080
|
+
# Windows: asyncio signal handlers not supported, use signal.signal()
|
|
1081
|
+
# for SIGINT (Ctrl+C). Note: SIGTERM not available on Windows.
|
|
1082
|
+
#
|
|
1083
|
+
# Thread-safety: On Windows, signal.signal() handlers execute in a
|
|
1084
|
+
# different thread than the event loop. While asyncio.Event.set() is
|
|
1085
|
+
# documented as thread-safe, we use loop.call_soon_threadsafe() to
|
|
1086
|
+
# schedule the set() call on the event loop thread. This ensures
|
|
1087
|
+
# proper cross-thread communication and avoids potential race
|
|
1088
|
+
# conditions with any event loop state inspection.
|
|
1089
|
+
def windows_handler(signum: int, frame: object) -> None:
|
|
1090
|
+
"""Windows-compatible signal handler wrapper.
|
|
1091
|
+
|
|
1092
|
+
Uses call_soon_threadsafe to safely communicate with the event
|
|
1093
|
+
loop from the signal handler thread.
|
|
1094
|
+
"""
|
|
1095
|
+
sig = signal.Signals(signum)
|
|
1096
|
+
logger.info(
|
|
1097
|
+
"Received %s, initiating graceful shutdown... (correlation_id=%s)",
|
|
1098
|
+
sig.name,
|
|
1099
|
+
correlation_id,
|
|
1100
|
+
)
|
|
1101
|
+
loop.call_soon_threadsafe(shutdown_event.set)
|
|
1102
|
+
|
|
1103
|
+
signal.signal(signal.SIGINT, windows_handler)
|
|
1104
|
+
|
|
1105
|
+
# 8. Start runtime and health server
|
|
1106
|
+
runtime_start_time = time.time()
|
|
1107
|
+
logger.info(
|
|
1108
|
+
"Starting ONEX runtime... (correlation_id=%s)",
|
|
1109
|
+
correlation_id,
|
|
1110
|
+
)
|
|
1111
|
+
await runtime.start()
|
|
1112
|
+
runtime_start_duration = time.time() - runtime_start_time
|
|
1113
|
+
logger.debug(
|
|
1114
|
+
"Runtime started in %.3fs (correlation_id=%s)",
|
|
1115
|
+
runtime_start_duration,
|
|
1116
|
+
correlation_id,
|
|
1117
|
+
extra={
|
|
1118
|
+
"duration_seconds": runtime_start_duration,
|
|
1119
|
+
},
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
# 9. Start HTTP health server for Docker/K8s probes
|
|
1123
|
+
# Port can be configured via ONEX_HTTP_PORT environment variable
|
|
1124
|
+
http_port_str = os.getenv("ONEX_HTTP_PORT", str(DEFAULT_HTTP_PORT))
|
|
1125
|
+
try:
|
|
1126
|
+
http_port = int(http_port_str)
|
|
1127
|
+
if not MIN_PORT <= http_port <= MAX_PORT:
|
|
1128
|
+
logger.warning(
|
|
1129
|
+
"ONEX_HTTP_PORT %d outside valid range %d-%d, using default %d (correlation_id=%s)",
|
|
1130
|
+
http_port,
|
|
1131
|
+
MIN_PORT,
|
|
1132
|
+
MAX_PORT,
|
|
1133
|
+
DEFAULT_HTTP_PORT,
|
|
1134
|
+
correlation_id,
|
|
1135
|
+
)
|
|
1136
|
+
http_port = DEFAULT_HTTP_PORT
|
|
1137
|
+
except ValueError:
|
|
1138
|
+
logger.warning(
|
|
1139
|
+
"Invalid ONEX_HTTP_PORT value '%s', using default %d (correlation_id=%s)",
|
|
1140
|
+
http_port_str,
|
|
1141
|
+
DEFAULT_HTTP_PORT,
|
|
1142
|
+
correlation_id,
|
|
1143
|
+
)
|
|
1144
|
+
http_port = DEFAULT_HTTP_PORT
|
|
1145
|
+
|
|
1146
|
+
health_server = ServiceHealth(
|
|
1147
|
+
container=container,
|
|
1148
|
+
runtime=runtime,
|
|
1149
|
+
port=http_port,
|
|
1150
|
+
version=KERNEL_VERSION,
|
|
1151
|
+
)
|
|
1152
|
+
health_start_time = time.time()
|
|
1153
|
+
await health_server.start()
|
|
1154
|
+
health_start_duration = time.time() - health_start_time
|
|
1155
|
+
logger.debug(
|
|
1156
|
+
"Health server started in %.3fs (correlation_id=%s)",
|
|
1157
|
+
health_start_duration,
|
|
1158
|
+
correlation_id,
|
|
1159
|
+
extra={
|
|
1160
|
+
"duration_seconds": health_start_duration,
|
|
1161
|
+
"port": http_port,
|
|
1162
|
+
},
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1165
|
+
# 9.5. Start introspection event consumer if dispatcher is available
|
|
1166
|
+
# This consumer subscribes to the input topic and routes introspection
|
|
1167
|
+
# events to the HandlerNodeIntrospected via DispatcherNodeIntrospected.
|
|
1168
|
+
# Unlike RuntimeHostProcess which routes based on handler_type field,
|
|
1169
|
+
# this consumer directly parses introspection events from JSON.
|
|
1170
|
+
#
|
|
1171
|
+
# The message handler is extracted to IntrospectionMessageHandler for
|
|
1172
|
+
# better testability and separation of concerns (PR #101 code quality).
|
|
1173
|
+
#
|
|
1174
|
+
# Duck typing approach per CLAUDE.md architectural guidelines:
|
|
1175
|
+
# Check for subscribe() capability via hasattr/callable instead of isinstance.
|
|
1176
|
+
# This enables any event bus implementing subscribe() to participate in
|
|
1177
|
+
# introspection event consumption, following protocol-based polymorphism.
|
|
1178
|
+
#
|
|
1179
|
+
# Production considerations:
|
|
1180
|
+
# - EventBusKafka: Uses distributed consumer groups for production workloads
|
|
1181
|
+
# - EventBusInmemory: subscribe() works for testing scenarios
|
|
1182
|
+
# - Other implementations: Will work if they implement subscribe()
|
|
1183
|
+
#
|
|
1184
|
+
# The duck typing approach allows new event bus implementations to
|
|
1185
|
+
# participate in introspection without modifying this code.
|
|
1186
|
+
has_subscribe = hasattr(event_bus, "subscribe") and callable(
|
|
1187
|
+
getattr(event_bus, "subscribe", None)
|
|
1188
|
+
)
|
|
1189
|
+
if introspection_dispatcher is not None and has_subscribe:
|
|
1190
|
+
# Create extracted event router with container-based DI pattern
|
|
1191
|
+
# Dependencies are passed explicitly since they are created at runtime
|
|
1192
|
+
# by the kernel and may not be registered in the container yet
|
|
1193
|
+
introspection_event_router = IntrospectionEventRouter(
|
|
1194
|
+
container=container,
|
|
1195
|
+
output_topic=config.output_topic,
|
|
1196
|
+
dispatcher=introspection_dispatcher,
|
|
1197
|
+
event_bus=event_bus,
|
|
1198
|
+
)
|
|
1199
|
+
|
|
1200
|
+
# Subscribe with callback - returns unsubscribe function
|
|
1201
|
+
subscribe_start_time = time.time()
|
|
1202
|
+
logger.info(
|
|
1203
|
+
"Subscribing to introspection events on event bus (correlation_id=%s)",
|
|
1204
|
+
correlation_id,
|
|
1205
|
+
extra={
|
|
1206
|
+
"topic": config.input_topic,
|
|
1207
|
+
"consumer_group": f"{config.consumer_group}-introspection",
|
|
1208
|
+
"event_bus_type": event_bus_type,
|
|
1209
|
+
},
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
introspection_unsubscribe = await event_bus.subscribe(
|
|
1213
|
+
topic=config.input_topic,
|
|
1214
|
+
group_id=f"{config.consumer_group}-introspection",
|
|
1215
|
+
on_message=introspection_event_router.handle_message,
|
|
1216
|
+
)
|
|
1217
|
+
subscribe_duration = time.time() - subscribe_start_time
|
|
1218
|
+
|
|
1219
|
+
logger.info(
|
|
1220
|
+
"Introspection event consumer started successfully in %.3fs (correlation_id=%s)",
|
|
1221
|
+
subscribe_duration,
|
|
1222
|
+
correlation_id,
|
|
1223
|
+
extra={
|
|
1224
|
+
"topic": config.input_topic,
|
|
1225
|
+
"consumer_group": f"{config.consumer_group}-introspection",
|
|
1226
|
+
"subscribe_duration_seconds": subscribe_duration,
|
|
1227
|
+
"event_bus_type": event_bus_type,
|
|
1228
|
+
},
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
# Calculate total bootstrap time
|
|
1232
|
+
bootstrap_duration = time.time() - bootstrap_start_time
|
|
1233
|
+
|
|
1234
|
+
# Display startup banner with key configuration
|
|
1235
|
+
if introspection_dispatcher is not None:
|
|
1236
|
+
if consul_handler is not None:
|
|
1237
|
+
registration_status = "enabled (PostgreSQL + Consul)"
|
|
1238
|
+
else:
|
|
1239
|
+
registration_status = "enabled (PostgreSQL only)"
|
|
1240
|
+
else:
|
|
1241
|
+
registration_status = "disabled"
|
|
1242
|
+
banner_lines = [
|
|
1243
|
+
"=" * 60,
|
|
1244
|
+
f"ONEX Runtime Kernel v{KERNEL_VERSION}",
|
|
1245
|
+
f"Environment: {environment}",
|
|
1246
|
+
f"Contracts: {contracts_dir}",
|
|
1247
|
+
f"Event Bus: {event_bus_type} (group: {config.consumer_group})",
|
|
1248
|
+
f"Topics: {config.input_topic} → {config.output_topic}",
|
|
1249
|
+
f"Registration: {registration_status}",
|
|
1250
|
+
f"Health endpoint: http://0.0.0.0:{http_port}/health",
|
|
1251
|
+
f"Bootstrap time: {bootstrap_duration:.3f}s",
|
|
1252
|
+
f"Correlation ID: {correlation_id}",
|
|
1253
|
+
"=" * 60,
|
|
1254
|
+
]
|
|
1255
|
+
banner = "\n".join(banner_lines)
|
|
1256
|
+
logger.info("\n%s", banner)
|
|
1257
|
+
|
|
1258
|
+
logger.info(
|
|
1259
|
+
"ONEX runtime started successfully in %.3fs (correlation_id=%s)",
|
|
1260
|
+
bootstrap_duration,
|
|
1261
|
+
correlation_id,
|
|
1262
|
+
extra={
|
|
1263
|
+
"bootstrap_duration_seconds": bootstrap_duration,
|
|
1264
|
+
"config_load_seconds": config_duration,
|
|
1265
|
+
"event_bus_create_seconds": event_bus_duration,
|
|
1266
|
+
"container_wire_seconds": container_duration,
|
|
1267
|
+
"runtime_create_seconds": runtime_create_duration,
|
|
1268
|
+
"runtime_start_seconds": runtime_start_duration,
|
|
1269
|
+
"health_start_seconds": health_start_duration,
|
|
1270
|
+
},
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
# Wait for shutdown signal
|
|
1274
|
+
await shutdown_event.wait()
|
|
1275
|
+
|
|
1276
|
+
grace_period = config.shutdown.grace_period_seconds
|
|
1277
|
+
shutdown_start_time = time.time()
|
|
1278
|
+
logger.info(
|
|
1279
|
+
"Shutdown signal received, stopping runtime (timeout=%ss, correlation_id=%s)",
|
|
1280
|
+
grace_period,
|
|
1281
|
+
correlation_id,
|
|
1282
|
+
)
|
|
1283
|
+
|
|
1284
|
+
# Stop introspection consumer first (fast)
|
|
1285
|
+
if introspection_unsubscribe is not None:
|
|
1286
|
+
try:
|
|
1287
|
+
await introspection_unsubscribe()
|
|
1288
|
+
logger.debug(
|
|
1289
|
+
"Introspection consumer stopped (correlation_id=%s)",
|
|
1290
|
+
correlation_id,
|
|
1291
|
+
)
|
|
1292
|
+
except Exception as consumer_stop_error:
|
|
1293
|
+
logger.warning(
|
|
1294
|
+
"Failed to stop introspection consumer: %s (correlation_id=%s)",
|
|
1295
|
+
sanitize_error_message(consumer_stop_error),
|
|
1296
|
+
correlation_id,
|
|
1297
|
+
)
|
|
1298
|
+
introspection_unsubscribe = None
|
|
1299
|
+
|
|
1300
|
+
# Stop health server (fast, non-blocking)
|
|
1301
|
+
if health_server is not None:
|
|
1302
|
+
try:
|
|
1303
|
+
health_stop_start_time = time.time()
|
|
1304
|
+
await health_server.stop()
|
|
1305
|
+
health_stop_duration = time.time() - health_stop_start_time
|
|
1306
|
+
logger.debug(
|
|
1307
|
+
"Health server stopped in %.3fs (correlation_id=%s)",
|
|
1308
|
+
health_stop_duration,
|
|
1309
|
+
correlation_id,
|
|
1310
|
+
extra={
|
|
1311
|
+
"duration_seconds": health_stop_duration,
|
|
1312
|
+
},
|
|
1313
|
+
)
|
|
1314
|
+
except Exception as health_stop_error:
|
|
1315
|
+
logger.warning(
|
|
1316
|
+
"Failed to stop health server: %s (correlation_id=%s)",
|
|
1317
|
+
health_stop_error,
|
|
1318
|
+
correlation_id,
|
|
1319
|
+
extra={
|
|
1320
|
+
"error_type": type(health_stop_error).__name__,
|
|
1321
|
+
},
|
|
1322
|
+
)
|
|
1323
|
+
health_server = None
|
|
1324
|
+
|
|
1325
|
+
# Stop runtime with timeout
|
|
1326
|
+
try:
|
|
1327
|
+
runtime_stop_start_time = time.time()
|
|
1328
|
+
await asyncio.wait_for(runtime.stop(), timeout=grace_period)
|
|
1329
|
+
runtime_stop_duration = time.time() - runtime_stop_start_time
|
|
1330
|
+
logger.debug(
|
|
1331
|
+
"Runtime stopped in %.3fs (correlation_id=%s)",
|
|
1332
|
+
runtime_stop_duration,
|
|
1333
|
+
correlation_id,
|
|
1334
|
+
extra={
|
|
1335
|
+
"duration_seconds": runtime_stop_duration,
|
|
1336
|
+
},
|
|
1337
|
+
)
|
|
1338
|
+
except TimeoutError:
|
|
1339
|
+
logger.warning(
|
|
1340
|
+
"Graceful shutdown timed out after %s seconds, forcing stop (correlation_id=%s)",
|
|
1341
|
+
grace_period,
|
|
1342
|
+
correlation_id,
|
|
1343
|
+
)
|
|
1344
|
+
runtime = None # Mark as stopped to prevent double-stop in finally
|
|
1345
|
+
|
|
1346
|
+
# Close PostgreSQL pool
|
|
1347
|
+
if postgres_pool is not None:
|
|
1348
|
+
try:
|
|
1349
|
+
pool_close_start_time = time.time()
|
|
1350
|
+
await postgres_pool.close()
|
|
1351
|
+
pool_close_duration = time.time() - pool_close_start_time
|
|
1352
|
+
logger.debug(
|
|
1353
|
+
"PostgreSQL pool closed in %.3fs (correlation_id=%s)",
|
|
1354
|
+
pool_close_duration,
|
|
1355
|
+
correlation_id,
|
|
1356
|
+
)
|
|
1357
|
+
except Exception as pool_close_error:
|
|
1358
|
+
# Sanitize to prevent credential leakage
|
|
1359
|
+
logger.warning(
|
|
1360
|
+
"Failed to close PostgreSQL pool: %s (correlation_id=%s)",
|
|
1361
|
+
sanitize_error_message(pool_close_error),
|
|
1362
|
+
correlation_id,
|
|
1363
|
+
)
|
|
1364
|
+
postgres_pool = None
|
|
1365
|
+
|
|
1366
|
+
shutdown_duration = time.time() - shutdown_start_time
|
|
1367
|
+
logger.info(
|
|
1368
|
+
"ONEX runtime stopped successfully in %.3fs (correlation_id=%s)",
|
|
1369
|
+
shutdown_duration,
|
|
1370
|
+
correlation_id,
|
|
1371
|
+
extra={
|
|
1372
|
+
"shutdown_duration_seconds": shutdown_duration,
|
|
1373
|
+
},
|
|
1374
|
+
)
|
|
1375
|
+
return 0
|
|
1376
|
+
|
|
1377
|
+
except ProtocolConfigurationError as e:
|
|
1378
|
+
# Configuration errors already have proper context and chaining
|
|
1379
|
+
logger.exception(
|
|
1380
|
+
"ONEX runtime configuration failed (correlation_id=%s)",
|
|
1381
|
+
correlation_id,
|
|
1382
|
+
extra={
|
|
1383
|
+
"error_type": type(e).__name__,
|
|
1384
|
+
"error_code": e.model.error_code.name if hasattr(e, "model") else None,
|
|
1385
|
+
},
|
|
1386
|
+
)
|
|
1387
|
+
return 1
|
|
1388
|
+
|
|
1389
|
+
except RuntimeHostError as e:
|
|
1390
|
+
# Runtime host errors already have proper structure
|
|
1391
|
+
logger.exception(
|
|
1392
|
+
"ONEX runtime host error (correlation_id=%s)",
|
|
1393
|
+
correlation_id,
|
|
1394
|
+
extra={
|
|
1395
|
+
"error_type": type(e).__name__,
|
|
1396
|
+
"error_code": e.model.error_code.name if hasattr(e, "model") else None,
|
|
1397
|
+
},
|
|
1398
|
+
)
|
|
1399
|
+
return 1
|
|
1400
|
+
|
|
1401
|
+
except Exception as e:
|
|
1402
|
+
# Unexpected errors: log with full context and return error code
|
|
1403
|
+
# (consistent with ProtocolConfigurationError and RuntimeHostError handlers)
|
|
1404
|
+
# Sanitize error message to prevent credential leakage
|
|
1405
|
+
logger.exception(
|
|
1406
|
+
"ONEX runtime failed with unexpected error: %s (correlation_id=%s)",
|
|
1407
|
+
sanitize_error_message(e),
|
|
1408
|
+
correlation_id,
|
|
1409
|
+
extra={
|
|
1410
|
+
"error_type": type(e).__name__,
|
|
1411
|
+
},
|
|
1412
|
+
)
|
|
1413
|
+
return 1
|
|
1414
|
+
|
|
1415
|
+
finally:
|
|
1416
|
+
# Guard cleanup - stop all resources if not already stopped
|
|
1417
|
+
# Order: introspection consumer -> health server -> runtime -> pool
|
|
1418
|
+
|
|
1419
|
+
if introspection_unsubscribe is not None:
|
|
1420
|
+
try:
|
|
1421
|
+
await introspection_unsubscribe()
|
|
1422
|
+
except Exception as cleanup_error:
|
|
1423
|
+
logger.warning(
|
|
1424
|
+
"Failed to stop introspection consumer during cleanup: %s (correlation_id=%s)",
|
|
1425
|
+
sanitize_error_message(cleanup_error),
|
|
1426
|
+
correlation_id,
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1429
|
+
if health_server is not None:
|
|
1430
|
+
try:
|
|
1431
|
+
await health_server.stop()
|
|
1432
|
+
except Exception as cleanup_error:
|
|
1433
|
+
logger.warning(
|
|
1434
|
+
"Failed to stop health server during cleanup: %s (correlation_id=%s)",
|
|
1435
|
+
sanitize_error_message(cleanup_error),
|
|
1436
|
+
correlation_id,
|
|
1437
|
+
)
|
|
1438
|
+
|
|
1439
|
+
if runtime is not None:
|
|
1440
|
+
try:
|
|
1441
|
+
await runtime.stop()
|
|
1442
|
+
except Exception as cleanup_error:
|
|
1443
|
+
# Log cleanup failures with context instead of suppressing them
|
|
1444
|
+
# Sanitize to prevent potential credential leakage from runtime errors
|
|
1445
|
+
logger.warning(
|
|
1446
|
+
"Failed to stop runtime during cleanup: %s (correlation_id=%s)",
|
|
1447
|
+
sanitize_error_message(cleanup_error),
|
|
1448
|
+
correlation_id,
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
if postgres_pool is not None:
|
|
1452
|
+
try:
|
|
1453
|
+
await postgres_pool.close()
|
|
1454
|
+
except Exception as cleanup_error:
|
|
1455
|
+
# Sanitize to prevent credential leakage from PostgreSQL errors
|
|
1456
|
+
logger.warning(
|
|
1457
|
+
"Failed to close PostgreSQL pool during cleanup: %s (correlation_id=%s)",
|
|
1458
|
+
sanitize_error_message(cleanup_error),
|
|
1459
|
+
correlation_id,
|
|
1460
|
+
)
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
def configure_logging() -> None:
|
|
1464
|
+
"""Configure logging for the kernel with structured format.
|
|
1465
|
+
|
|
1466
|
+
Sets up structured logging with appropriate log level from the
|
|
1467
|
+
ONEX_LOG_LEVEL environment variable (default: INFO). This function
|
|
1468
|
+
must be called early in the bootstrap process to ensure logging
|
|
1469
|
+
is available for all subsequent operations.
|
|
1470
|
+
|
|
1471
|
+
Logging Configuration:
|
|
1472
|
+
- Log Level: Controlled by ONEX_LOG_LEVEL environment variable
|
|
1473
|
+
- Format: Timestamp, level, logger name, message, extras
|
|
1474
|
+
- Date Format: ISO-8601 compatible (YYYY-MM-DD HH:MM:SS)
|
|
1475
|
+
- Structured Extras: Support for correlation_id and custom fields
|
|
1476
|
+
|
|
1477
|
+
Bootstrap Order Rationale:
|
|
1478
|
+
This function is called BEFORE runtime config is loaded because logging
|
|
1479
|
+
must be available during config loading itself (to log errors, warnings,
|
|
1480
|
+
and info about config discovery). Therefore, logging configuration uses
|
|
1481
|
+
environment variables rather than contract-based config values.
|
|
1482
|
+
|
|
1483
|
+
This is a deliberate chicken-and-egg solution:
|
|
1484
|
+
- Environment variables control early bootstrap logging
|
|
1485
|
+
- Contract config controls runtime behavior after bootstrap
|
|
1486
|
+
|
|
1487
|
+
Environment Variables:
|
|
1488
|
+
ONEX_LOG_LEVEL: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
1489
|
+
Default: INFO
|
|
1490
|
+
|
|
1491
|
+
Log Format Example:
|
|
1492
|
+
2025-01-15 10:30:45 [INFO] omnibase_infra.runtime.service_kernel: ONEX Kernel v0.1.0
|
|
1493
|
+
2025-01-15 10:30:45 [DEBUG] omnibase_infra.runtime.service_kernel: Runtime config loaded
|
|
1494
|
+
(correlation_id=123e4567-e89b-12d3-a456-426614174000)
|
|
1495
|
+
|
|
1496
|
+
Structured Logging Extras:
|
|
1497
|
+
All log calls support structured extras for observability:
|
|
1498
|
+
- correlation_id: UUID for distributed tracing
|
|
1499
|
+
- duration_seconds: Operation timing metrics
|
|
1500
|
+
- error_type: Exception class name for error analysis
|
|
1501
|
+
- Custom fields: Any JSON-serializable data
|
|
1502
|
+
|
|
1503
|
+
Example:
|
|
1504
|
+
>>> configure_logging()
|
|
1505
|
+
>>> logger.info("Operation completed", extra={"duration_seconds": 1.234})
|
|
1506
|
+
"""
|
|
1507
|
+
log_level = os.getenv("ONEX_LOG_LEVEL", "INFO").upper()
|
|
1508
|
+
|
|
1509
|
+
# Validate log level and provide helpful error if invalid
|
|
1510
|
+
valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
|
|
1511
|
+
if log_level not in valid_levels:
|
|
1512
|
+
print(
|
|
1513
|
+
f"Warning: Invalid ONEX_LOG_LEVEL '{log_level}', using INFO. "
|
|
1514
|
+
f"Valid levels: {', '.join(sorted(valid_levels))}",
|
|
1515
|
+
file=sys.stderr,
|
|
1516
|
+
)
|
|
1517
|
+
log_level = "INFO"
|
|
1518
|
+
|
|
1519
|
+
logging.basicConfig(
|
|
1520
|
+
level=getattr(logging, log_level, logging.INFO),
|
|
1521
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
1522
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
1523
|
+
)
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
def main() -> None:
|
|
1527
|
+
"""Entry point for the ONEX runtime kernel.
|
|
1528
|
+
|
|
1529
|
+
This is the synchronous entry point for the kernel. It configures
|
|
1530
|
+
logging, initiates the async bootstrap process, and handles the
|
|
1531
|
+
final exit code.
|
|
1532
|
+
|
|
1533
|
+
Execution Flow:
|
|
1534
|
+
1. Configure logging from environment variables
|
|
1535
|
+
2. Log kernel version for startup identification
|
|
1536
|
+
3. Run async bootstrap function in event loop
|
|
1537
|
+
4. Exit with appropriate exit code (0=success, 1=error)
|
|
1538
|
+
|
|
1539
|
+
Exit Codes:
|
|
1540
|
+
0: Successful startup and clean shutdown
|
|
1541
|
+
1: Configuration error, runtime error, or unexpected failure
|
|
1542
|
+
|
|
1543
|
+
This function is the target for:
|
|
1544
|
+
- The installed entrypoint: `onex-runtime`
|
|
1545
|
+
- Direct module execution: `python -m omnibase_infra.runtime.service_kernel`
|
|
1546
|
+
- Docker CMD/ENTRYPOINT in container deployments
|
|
1547
|
+
|
|
1548
|
+
Example:
|
|
1549
|
+
>>> # From command line
|
|
1550
|
+
>>> python -m omnibase_infra.runtime.service_kernel
|
|
1551
|
+
>>> # Or via installed entrypoint
|
|
1552
|
+
>>> onex-runtime
|
|
1553
|
+
|
|
1554
|
+
Docker Usage:
|
|
1555
|
+
CMD ["onex-runtime"]
|
|
1556
|
+
# Container will start runtime and expose health endpoint
|
|
1557
|
+
"""
|
|
1558
|
+
configure_logging()
|
|
1559
|
+
logger.info("ONEX Kernel v%s initializing...", KERNEL_VERSION)
|
|
1560
|
+
exit_code = asyncio.run(bootstrap())
|
|
1561
|
+
sys.exit(exit_code)
|
|
1562
|
+
|
|
1563
|
+
|
|
1564
|
+
if __name__ == "__main__":
|
|
1565
|
+
main()
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
__all__: list[str] = [
|
|
1569
|
+
"ENV_CONTRACTS_DIR",
|
|
1570
|
+
"bootstrap",
|
|
1571
|
+
"load_runtime_config",
|
|
1572
|
+
"main",
|
|
1573
|
+
]
|