omnibase_infra 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. omnibase_infra/constants_topic_patterns.py +26 -0
  2. omnibase_infra/enums/__init__.py +3 -0
  3. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  4. omnibase_infra/enums/enum_handler_source_mode.py +16 -2
  5. omnibase_infra/errors/__init__.py +4 -0
  6. omnibase_infra/errors/error_binding_resolution.py +128 -0
  7. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +0 -2
  8. omnibase_infra/event_bus/event_bus_inmemory.py +64 -10
  9. omnibase_infra/event_bus/event_bus_kafka.py +105 -47
  10. omnibase_infra/event_bus/mixin_kafka_broadcast.py +3 -7
  11. omnibase_infra/event_bus/mixin_kafka_dlq.py +12 -6
  12. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +0 -81
  13. omnibase_infra/event_bus/testing/__init__.py +26 -0
  14. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  15. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  16. omnibase_infra/handlers/handler_consul.py +2 -0
  17. omnibase_infra/handlers/mixins/__init__.py +5 -0
  18. omnibase_infra/handlers/mixins/mixin_consul_service.py +274 -10
  19. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  20. omnibase_infra/handlers/models/model_filesystem_config.py +4 -4
  21. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  22. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  23. omnibase_infra/mixins/mixin_node_introspection.py +189 -19
  24. omnibase_infra/models/__init__.py +8 -0
  25. omnibase_infra/models/bindings/__init__.py +59 -0
  26. omnibase_infra/models/bindings/constants.py +144 -0
  27. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  28. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  29. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  30. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  31. omnibase_infra/models/discovery/model_introspection_config.py +25 -17
  32. omnibase_infra/models/dispatch/__init__.py +8 -0
  33. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  34. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  35. omnibase_infra/models/handlers/model_handler_source_config.py +1 -1
  36. omnibase_infra/models/model_node_identity.py +126 -0
  37. omnibase_infra/models/projection/model_snapshot_topic_config.py +3 -2
  38. omnibase_infra/models/registration/__init__.py +9 -0
  39. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  40. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  41. omnibase_infra/models/registration/model_node_introspection_event.py +11 -0
  42. omnibase_infra/models/runtime/__init__.py +9 -0
  43. omnibase_infra/models/validation/model_coverage_metrics.py +2 -2
  44. omnibase_infra/nodes/__init__.py +9 -0
  45. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  46. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  47. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  48. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  49. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  50. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  51. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  52. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  53. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  54. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  55. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  56. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  57. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  58. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  59. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  60. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  61. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  62. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  63. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  64. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  65. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  66. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  67. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  68. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  69. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  70. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  71. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  72. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  73. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  74. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  75. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  76. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  77. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  78. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  79. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  80. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  81. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  82. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  83. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  84. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  85. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  86. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +7 -5
  87. omnibase_infra/nodes/reducers/models/__init__.py +7 -2
  88. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +11 -0
  89. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  90. omnibase_infra/nodes/reducers/registration_reducer.py +1 -0
  91. omnibase_infra/protocols/__init__.py +3 -0
  92. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  93. omnibase_infra/runtime/__init__.py +60 -0
  94. omnibase_infra/runtime/binding_resolver.py +753 -0
  95. omnibase_infra/runtime/constants_security.py +70 -0
  96. omnibase_infra/runtime/contract_loaders/__init__.py +9 -0
  97. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  98. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  99. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  100. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  101. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  102. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  103. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  104. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  105. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  106. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  107. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  108. omnibase_infra/runtime/handler_source_resolver.py +43 -2
  109. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  110. omnibase_infra/runtime/models/__init__.py +13 -0
  111. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  112. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  113. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +4 -3
  114. omnibase_infra/runtime/models/model_security_config.py +109 -0
  115. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  116. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  117. omnibase_infra/runtime/service_kernel.py +76 -6
  118. omnibase_infra/runtime/service_message_dispatch_engine.py +558 -15
  119. omnibase_infra/runtime/service_runtime_host_process.py +770 -20
  120. omnibase_infra/runtime/transition_notification_publisher.py +3 -2
  121. omnibase_infra/runtime/util_wiring.py +206 -62
  122. omnibase_infra/services/mcp/service_mcp_tool_sync.py +27 -9
  123. omnibase_infra/services/session/config_consumer.py +25 -8
  124. omnibase_infra/services/session/config_store.py +2 -2
  125. omnibase_infra/services/session/consumer.py +1 -1
  126. omnibase_infra/topics/__init__.py +45 -0
  127. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  128. omnibase_infra/topics/util_topic_composition.py +95 -0
  129. omnibase_infra/types/typed_dict/__init__.py +9 -1
  130. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  131. omnibase_infra/utils/__init__.py +9 -0
  132. omnibase_infra/utils/util_consumer_group.py +232 -0
  133. omnibase_infra/validation/infra_validators.py +18 -1
  134. omnibase_infra/validation/validation_exemptions.yaml +192 -0
  135. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/METADATA +3 -3
  136. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/RECORD +139 -52
  137. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/entry_points.txt +1 -0
  138. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/WHEEL +0 -0
  139. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/licenses/LICENSE +0 -0
@@ -56,6 +56,7 @@ Exports:
56
56
  ModelBindingConfigCacheStats: Cache statistics for BindingConfigResolver
57
57
  ModelBindingConfigResolverConfig: Configuration for BindingConfigResolver
58
58
  ModelConfigCacheEntry: Internal cache entry for BindingConfigResolver
59
+ ModelSecurityConfig: Security configuration for handler namespace allowlisting
59
60
  """
60
61
 
61
62
  # Re-export from omnibase_core for convenience
@@ -81,6 +82,9 @@ from omnibase_infra.runtime.models.model_config_ref import ModelConfigRef
81
82
  from omnibase_infra.runtime.models.model_config_ref_parse_result import (
82
83
  ModelConfigRefParseResult,
83
84
  )
85
+ from omnibase_infra.runtime.models.model_contract_load_result import (
86
+ ModelContractLoadResult,
87
+ )
84
88
  from omnibase_infra.runtime.models.model_domain_plugin_config import (
85
89
  ModelDomainPluginConfig,
86
90
  )
@@ -128,6 +132,9 @@ from omnibase_infra.runtime.models.model_protocol_registration_config import (
128
132
  )
129
133
  from omnibase_infra.runtime.models.model_retry_policy import ModelRetryPolicy
130
134
  from omnibase_infra.runtime.models.model_runtime_config import ModelRuntimeConfig
135
+ from omnibase_infra.runtime.models.model_runtime_contract_config import (
136
+ ModelRuntimeContractConfig,
137
+ )
131
138
  from omnibase_infra.runtime.models.model_runtime_scheduler_config import (
132
139
  ModelRuntimeSchedulerConfig,
133
140
  )
@@ -148,6 +155,7 @@ from omnibase_infra.runtime.models.model_secret_source_spec import (
148
155
  ModelSecretSourceSpec,
149
156
  SecretSourceType,
150
157
  )
158
+ from omnibase_infra.runtime.models.model_security_config import ModelSecurityConfig
151
159
  from omnibase_infra.runtime.models.model_shutdown_batch_result import (
152
160
  ModelShutdownBatchResult,
153
161
  )
@@ -213,4 +221,9 @@ __all__: list[str] = [
213
221
  "ModelTransitionNotificationOutboxMetrics",
214
222
  "ModelTransitionNotificationPublisherMetrics",
215
223
  "SecretSourceType",
224
+ # Contract loading models (OMN-1519)
225
+ "ModelContractLoadResult",
226
+ "ModelRuntimeContractConfig",
227
+ # Security configuration (OMN-1519)
228
+ "ModelSecurityConfig",
216
229
  ]
@@ -0,0 +1,224 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Contract load result model for tracking individual contract loading.
4
+
5
+ This module provides the ModelContractLoadResult class for representing
6
+ the outcome of loading a single contract.yaml file, including any subcontracts
7
+ (handler_routing, operation_bindings) that were loaded from it.
8
+
9
+ Part of OMN-1519: Runtime contract config loader.
10
+
11
+ Design Pattern:
12
+ ModelContractLoadResult captures both successful and failed contract
13
+ loading attempts, allowing the runtime to continue loading other
14
+ contracts even if one fails. Errors are collected rather than raised
15
+ immediately.
16
+
17
+ Thread Safety:
18
+ ModelContractLoadResult is immutable (frozen=True) after creation,
19
+ making it thread-safe for concurrent read access.
20
+
21
+ Example:
22
+ >>> from pathlib import Path
23
+ >>> from omnibase_infra.runtime.models import ModelContractLoadResult
24
+ >>>
25
+ >>> # Successful load
26
+ >>> result = ModelContractLoadResult.succeeded(
27
+ ... contract_path=Path("nodes/auth/contract.yaml"),
28
+ ... handler_routing=routing_subcontract,
29
+ ... operation_bindings=bindings_subcontract,
30
+ ... )
31
+ >>> result.success
32
+ True
33
+ >>>
34
+ >>> # Failed load
35
+ >>> result = ModelContractLoadResult.failed(
36
+ ... contract_path=Path("nodes/broken/contract.yaml"),
37
+ ... error="Invalid YAML syntax",
38
+ ... )
39
+ >>> result.success
40
+ False
41
+
42
+ .. versionadded:: 0.2.8
43
+ Created as part of OMN-1519.
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ from pathlib import Path
49
+ from uuid import UUID
50
+
51
+ from pydantic import BaseModel, ConfigDict, Field
52
+
53
+ from omnibase_infra.models.bindings import ModelOperationBindingsSubcontract
54
+ from omnibase_infra.models.routing import ModelRoutingSubcontract
55
+
56
+
57
+ class ModelContractLoadResult(BaseModel):
58
+ """Result of loading a single contract.yaml file.
59
+
60
+ Captures the outcome of loading a contract, including any subcontracts
61
+ that were successfully loaded or any errors that occurred.
62
+
63
+ Attributes:
64
+ contract_path: Path to the contract.yaml file that was loaded.
65
+ success: True if the contract was loaded successfully.
66
+ error: Error message if loading failed, empty string otherwise.
67
+ handler_routing: Loaded handler routing subcontract, or None if not present.
68
+ operation_bindings: Loaded operation bindings subcontract, or None if not present.
69
+ correlation_id: Correlation ID for tracing this load operation.
70
+
71
+ Example:
72
+ >>> result = ModelContractLoadResult.succeeded(
73
+ ... contract_path=Path("nodes/auth/contract.yaml"),
74
+ ... handler_routing=routing,
75
+ ... operation_bindings=bindings,
76
+ ... )
77
+ >>> result.success
78
+ True
79
+ >>> result.handler_routing is not None
80
+ True
81
+ """
82
+
83
+ model_config = ConfigDict(
84
+ frozen=True,
85
+ extra="forbid",
86
+ arbitrary_types_allowed=True,
87
+ )
88
+
89
+ contract_path: Path = Field(
90
+ ...,
91
+ description="Path to the contract.yaml file that was loaded.",
92
+ )
93
+ success: bool = Field(
94
+ ...,
95
+ description="True if the contract was loaded successfully.",
96
+ )
97
+ error: str = Field(
98
+ default="",
99
+ description="Error message if loading failed, empty string otherwise.",
100
+ )
101
+ handler_routing: ModelRoutingSubcontract | None = Field(
102
+ default=None,
103
+ description="Loaded handler routing subcontract, or None if not present.",
104
+ )
105
+ operation_bindings: ModelOperationBindingsSubcontract | None = Field(
106
+ default=None,
107
+ description="Loaded operation bindings subcontract, or None if not present.",
108
+ )
109
+ correlation_id: UUID | None = Field(
110
+ default=None,
111
+ description="Correlation ID for tracing this load operation.",
112
+ )
113
+
114
+ @property
115
+ def has_error(self) -> bool:
116
+ """Check if an error message exists.
117
+
118
+ Returns:
119
+ True if error is non-empty, False otherwise.
120
+ """
121
+ return bool(self.error)
122
+
123
+ @property
124
+ def has_handler_routing(self) -> bool:
125
+ """Check if handler routing was loaded.
126
+
127
+ Returns:
128
+ True if handler_routing is not None.
129
+ """
130
+ return self.handler_routing is not None
131
+
132
+ @property
133
+ def has_operation_bindings(self) -> bool:
134
+ """Check if operation bindings were loaded.
135
+
136
+ Returns:
137
+ True if operation_bindings is not None.
138
+ """
139
+ return self.operation_bindings is not None
140
+
141
+ @classmethod
142
+ def succeeded(
143
+ cls,
144
+ contract_path: Path,
145
+ handler_routing: ModelRoutingSubcontract | None = None,
146
+ operation_bindings: ModelOperationBindingsSubcontract | None = None,
147
+ correlation_id: UUID | None = None,
148
+ ) -> ModelContractLoadResult:
149
+ """Create a successful contract load result.
150
+
151
+ Args:
152
+ contract_path: Path to the contract.yaml file.
153
+ handler_routing: Loaded handler routing subcontract, if present.
154
+ operation_bindings: Loaded operation bindings subcontract, if present.
155
+ correlation_id: Optional correlation ID for tracing.
156
+
157
+ Returns:
158
+ ModelContractLoadResult indicating success.
159
+ """
160
+ return cls(
161
+ contract_path=contract_path,
162
+ success=True,
163
+ error="",
164
+ handler_routing=handler_routing,
165
+ operation_bindings=operation_bindings,
166
+ correlation_id=correlation_id,
167
+ )
168
+
169
+ @classmethod
170
+ def failed(
171
+ cls,
172
+ contract_path: Path,
173
+ error: str,
174
+ correlation_id: UUID | None = None,
175
+ ) -> ModelContractLoadResult:
176
+ """Create a failed contract load result.
177
+
178
+ Args:
179
+ contract_path: Path to the contract.yaml file.
180
+ error: Description of the error that occurred.
181
+ correlation_id: Optional correlation ID for tracing.
182
+
183
+ Returns:
184
+ ModelContractLoadResult indicating failure with error details.
185
+ """
186
+ return cls(
187
+ contract_path=contract_path,
188
+ success=False,
189
+ error=error,
190
+ handler_routing=None,
191
+ operation_bindings=None,
192
+ correlation_id=correlation_id,
193
+ )
194
+
195
+ def __bool__(self) -> bool:
196
+ """Allow using result in boolean context.
197
+
198
+ Warning:
199
+ **Non-standard __bool__ behavior**: This model overrides ``__bool__`` to
200
+ return ``True`` only when ``success`` is True.
201
+
202
+ Returns:
203
+ True if contract was loaded successfully, False otherwise.
204
+ """
205
+ return self.success
206
+
207
+ def __str__(self) -> str:
208
+ """Return a human-readable string representation.
209
+
210
+ Returns:
211
+ String describing the load result.
212
+ """
213
+ if self.success:
214
+ parts = []
215
+ if self.has_handler_routing:
216
+ parts.append("handler_routing")
217
+ if self.has_operation_bindings:
218
+ parts.append("operation_bindings")
219
+ loaded = ", ".join(parts) if parts else "no subcontracts"
220
+ return f"ModelContractLoadResult({self.contract_path}: {loaded})"
221
+ return f"ModelContractLoadResult({self.contract_path}: FAILED - {self.error})"
222
+
223
+
224
+ __all__ = ["ModelContractLoadResult"]
@@ -0,0 +1,268 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Runtime contract configuration model for consolidated contract loading.
4
+
5
+ This module provides the ModelRuntimeContractConfig class for representing
6
+ the consolidated result of loading all contract.yaml files at startup.
7
+
8
+ Part of OMN-1519: Runtime contract config loader.
9
+
10
+ Design Pattern:
11
+ ModelRuntimeContractConfig aggregates results from loading multiple
12
+ contract.yaml files, providing a single source of truth for all
13
+ handler routing and operation bindings configuration.
14
+
15
+ Thread Safety:
16
+ ModelRuntimeContractConfig is immutable (frozen=True) after creation,
17
+ making it thread-safe for concurrent read access.
18
+
19
+ Example:
20
+ >>> from omnibase_infra.runtime.models import ModelRuntimeContractConfig
21
+ >>>
22
+ >>> config = ModelRuntimeContractConfig(
23
+ ... contract_results=[result1, result2],
24
+ ... total_contracts_found=5,
25
+ ... total_contracts_loaded=4,
26
+ ... total_errors=1,
27
+ ... )
28
+ >>> config.success_rate
29
+ 0.8
30
+
31
+ .. versionadded:: 0.2.8
32
+ Created as part of OMN-1519.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ from pathlib import Path
38
+ from uuid import UUID
39
+
40
+ from pydantic import BaseModel, ConfigDict, Field, computed_field
41
+
42
+ from omnibase_infra.models.bindings import ModelOperationBindingsSubcontract
43
+ from omnibase_infra.models.routing import ModelRoutingSubcontract
44
+ from omnibase_infra.runtime.models.model_contract_load_result import (
45
+ ModelContractLoadResult,
46
+ )
47
+
48
+
49
+ class ModelRuntimeContractConfig(BaseModel):
50
+ """Consolidated runtime contract configuration from all loaded contracts.
51
+
52
+ Aggregates the results of loading multiple contract.yaml files,
53
+ providing access to all handler routing and operation bindings
54
+ configuration loaded at startup.
55
+
56
+ Attributes:
57
+ contract_results: List of individual contract load results.
58
+ total_contracts_found: Number of contract.yaml files discovered.
59
+ total_contracts_loaded: Number of contracts successfully loaded.
60
+ total_errors: Number of contracts that failed to load.
61
+ correlation_id: Correlation ID for tracing this load session.
62
+
63
+ Example:
64
+ >>> config = ModelRuntimeContractConfig(
65
+ ... contract_results=[result1, result2],
66
+ ... total_contracts_found=2,
67
+ ... total_contracts_loaded=2,
68
+ ... total_errors=0,
69
+ ... )
70
+ >>> config.all_successful
71
+ True
72
+ >>> len(config.handler_routing_configs)
73
+ 2
74
+ """
75
+
76
+ model_config = ConfigDict(
77
+ frozen=True,
78
+ extra="forbid",
79
+ )
80
+
81
+ contract_results: list[ModelContractLoadResult] = Field(
82
+ default_factory=list,
83
+ description="List of individual contract load results.",
84
+ )
85
+ total_contracts_found: int = Field(
86
+ default=0,
87
+ ge=0,
88
+ description="Number of contract.yaml files discovered.",
89
+ )
90
+ total_contracts_loaded: int = Field(
91
+ default=0,
92
+ ge=0,
93
+ description="Number of contracts successfully loaded.",
94
+ )
95
+ total_errors: int = Field(
96
+ default=0,
97
+ ge=0,
98
+ description="Number of contracts that failed to load.",
99
+ )
100
+ correlation_id: UUID | None = Field(
101
+ default=None,
102
+ description="Correlation ID for tracing this load session.",
103
+ )
104
+
105
+ @computed_field # type: ignore[prop-decorator]
106
+ @property
107
+ def all_successful(self) -> bool:
108
+ """Check if all contracts were loaded successfully.
109
+
110
+ Returns:
111
+ True if total_errors is 0 and at least one contract was found.
112
+ """
113
+ return self.total_errors == 0 and self.total_contracts_found > 0
114
+
115
+ @computed_field # type: ignore[prop-decorator]
116
+ @property
117
+ def success_rate(self) -> float:
118
+ """Calculate the success rate of contract loading.
119
+
120
+ Returns:
121
+ Ratio of successfully loaded contracts to total found (0.0-1.0).
122
+ Returns 1.0 if no contracts were found.
123
+ """
124
+ if self.total_contracts_found == 0:
125
+ return 1.0
126
+ return self.total_contracts_loaded / self.total_contracts_found
127
+
128
+ @property
129
+ def successful_results(self) -> list[ModelContractLoadResult]:
130
+ """Get all successful contract load results.
131
+
132
+ Returns:
133
+ List of ModelContractLoadResult where success is True.
134
+ """
135
+ return [r for r in self.contract_results if r.success]
136
+
137
+ @property
138
+ def failed_results(self) -> list[ModelContractLoadResult]:
139
+ """Get all failed contract load results.
140
+
141
+ Returns:
142
+ List of ModelContractLoadResult where success is False.
143
+ """
144
+ return [r for r in self.contract_results if not r.success]
145
+
146
+ @property
147
+ def handler_routing_configs(self) -> dict[Path, ModelRoutingSubcontract]:
148
+ """Get all loaded handler routing configurations.
149
+
150
+ Returns:
151
+ Mapping of contract path to handler routing subcontract.
152
+ """
153
+ return {
154
+ r.contract_path: r.handler_routing
155
+ for r in self.contract_results
156
+ if r.handler_routing is not None
157
+ }
158
+
159
+ @property
160
+ def operation_bindings_configs(
161
+ self,
162
+ ) -> dict[Path, ModelOperationBindingsSubcontract]:
163
+ """Get all loaded operation bindings configurations.
164
+
165
+ Returns:
166
+ Mapping of contract path to operation bindings subcontract.
167
+ """
168
+ return {
169
+ r.contract_path: r.operation_bindings
170
+ for r in self.contract_results
171
+ if r.operation_bindings is not None
172
+ }
173
+
174
+ @property
175
+ def error_messages(self) -> dict[Path, str]:
176
+ """Get all error messages from failed loads.
177
+
178
+ Returns:
179
+ Mapping of contract path to error message.
180
+ """
181
+ return {r.contract_path: r.error for r in self.contract_results if r.error}
182
+
183
+ def get_routing_for_contract(
184
+ self, contract_path: Path
185
+ ) -> ModelRoutingSubcontract | None:
186
+ """Get handler routing for a specific contract.
187
+
188
+ Args:
189
+ contract_path: Path to the contract.yaml file.
190
+
191
+ Returns:
192
+ ModelRoutingSubcontract if found, None otherwise.
193
+ """
194
+ for result in self.contract_results:
195
+ if result.contract_path == contract_path:
196
+ return result.handler_routing
197
+ return None
198
+
199
+ def get_bindings_for_contract(
200
+ self, contract_path: Path
201
+ ) -> ModelOperationBindingsSubcontract | None:
202
+ """Get operation bindings for a specific contract.
203
+
204
+ Args:
205
+ contract_path: Path to the contract.yaml file.
206
+
207
+ Returns:
208
+ ModelOperationBindingsSubcontract if found, None otherwise.
209
+ """
210
+ for result in self.contract_results:
211
+ if result.contract_path == contract_path:
212
+ return result.operation_bindings
213
+ return None
214
+
215
+ def __bool__(self) -> bool:
216
+ """Allow using config in boolean context.
217
+
218
+ Warning:
219
+ **Non-standard __bool__ behavior**: This model overrides ``__bool__`` to
220
+ return ``True`` only when all contracts were loaded successfully.
221
+
222
+ Note:
223
+ **Edge case - no contracts found**: Returns ``False`` when
224
+ ``total_contracts_found == 0``, even if ``total_errors == 0``.
225
+ This is intentional: an empty result (no contracts discovered)
226
+ should trigger explicit handling by the caller rather than
227
+ silently proceeding as if everything succeeded.
228
+
229
+ Example:
230
+ >>> # Empty config with no errors still returns False
231
+ >>> config = ModelRuntimeContractConfig()
232
+ >>> bool(config)
233
+ False
234
+ >>> config.total_errors
235
+ 0
236
+ >>> config.total_contracts_found
237
+ 0
238
+ >>>
239
+ >>> # Only returns True when contracts are found AND no errors
240
+ >>> config_with_contracts = ModelRuntimeContractConfig(
241
+ ... total_contracts_found=1,
242
+ ... total_contracts_loaded=1,
243
+ ... )
244
+ >>> bool(config_with_contracts)
245
+ True
246
+
247
+ Returns:
248
+ True if all contracts loaded successfully (requires at least one
249
+ contract found and zero errors), False otherwise.
250
+ """
251
+ return self.all_successful
252
+
253
+ def __str__(self) -> str:
254
+ """Return a human-readable summary.
255
+
256
+ Returns:
257
+ String summarizing the load results.
258
+ """
259
+ return (
260
+ f"ModelRuntimeContractConfig("
261
+ f"found={self.total_contracts_found}, "
262
+ f"loaded={self.total_contracts_loaded}, "
263
+ f"errors={self.total_errors}, "
264
+ f"success_rate={self.success_rate:.1%})"
265
+ )
266
+
267
+
268
+ __all__ = ["ModelRuntimeContractConfig"]
@@ -28,7 +28,7 @@ Environment Variables:
28
28
  Example: "runtime-scheduler-prod-1"
29
29
 
30
30
  ONEX_RUNTIME_SCHEDULER_TICK_TOPIC: Kafka topic for publishing ticks
31
- Default: "runtime.tick.v1"
31
+ Default: SUFFIX_RUNTIME_TICK from omnibase_infra.topics
32
32
  Example: "prod.runtime.tick.v1"
33
33
 
34
34
  Restart-Safety Settings:
@@ -106,6 +106,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
106
106
 
107
107
  from omnibase_infra.enums import EnumInfraTransportType
108
108
  from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
109
+ from omnibase_infra.topics import SUFFIX_RUNTIME_TICK
109
110
 
110
111
  logger = logging.getLogger(__name__)
111
112
 
@@ -167,7 +168,7 @@ class ModelRuntimeSchedulerConfig(BaseModel):
167
168
  max_length=255,
168
169
  )
169
170
  tick_topic: str = Field(
170
- default="runtime.tick.v1",
171
+ default=SUFFIX_RUNTIME_TICK,
171
172
  description="Kafka topic for publishing tick events",
172
173
  min_length=1,
173
174
  max_length=255,
@@ -604,7 +605,7 @@ class ModelRuntimeSchedulerConfig(BaseModel):
604
605
  base_config = cls(
605
606
  tick_interval_ms=1000,
606
607
  scheduler_id="runtime-scheduler-default",
607
- tick_topic="runtime.tick.v1",
608
+ tick_topic=SUFFIX_RUNTIME_TICK,
608
609
  persist_sequence_number=True,
609
610
  sequence_number_key="runtime_scheduler_sequence",
610
611
  max_tick_jitter_ms=100,
@@ -0,0 +1,109 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Security Configuration Model for Runtime Handler Loading.
4
+
5
+ This module provides the Pydantic model for configuring trusted handler namespaces.
6
+ The model allows operators to extend the trusted namespace list via configuration
7
+ file while maintaining secure defaults.
8
+
9
+ Security Model:
10
+ - Default: Only omnibase_core. and omnibase_infra. are trusted
11
+ - Third-party: Requires allow_third_party_handlers=True AND
12
+ explicit listing in allowed_handler_namespaces
13
+ - Config file is auditable/reviewable (unlike env vars)
14
+
15
+ Example:
16
+ >>> from omnibase_infra.runtime.models import ModelSecurityConfig
17
+ >>> config = ModelSecurityConfig() # Secure defaults
18
+ >>> config.get_effective_namespaces()
19
+ ('omnibase_core.', 'omnibase_infra.')
20
+
21
+ >>> # Enable third-party handlers
22
+ >>> config = ModelSecurityConfig(
23
+ ... allow_third_party_handlers=True,
24
+ ... allowed_handler_namespaces=(
25
+ ... "omnibase_core.",
26
+ ... "omnibase_infra.",
27
+ ... "mycompany.handlers.",
28
+ ... ),
29
+ ... )
30
+ >>> config.get_effective_namespaces()
31
+ ('omnibase_core.', 'omnibase_infra.', 'mycompany.handlers.')
32
+
33
+ .. versionadded:: 0.2.8
34
+ Created as part of OMN-1519 security hardening.
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ from pydantic import BaseModel, ConfigDict, Field
40
+
41
+ from omnibase_infra.runtime.constants_security import TRUSTED_HANDLER_NAMESPACE_PREFIXES
42
+
43
+
44
+ class ModelSecurityConfig(BaseModel):
45
+ """Security configuration for runtime handler loading.
46
+
47
+ This model allows operators to extend the trusted namespace list
48
+ via configuration file. The defaults are secure - third-party
49
+ namespaces require explicit opt-in.
50
+
51
+ Security Model:
52
+ - Default: Only omnibase_core. and omnibase_infra. are trusted
53
+ - Third-party: Requires allow_third_party_handlers=True AND
54
+ explicit listing in allowed_handler_namespaces
55
+ - Config file is auditable/reviewable (unlike env vars)
56
+
57
+ Attributes:
58
+ allow_third_party_handlers: Enable loading handlers from third-party
59
+ namespaces. When False, only TRUSTED_HANDLER_NAMESPACE_PREFIXES
60
+ are allowed regardless of allowed_handler_namespaces setting.
61
+ allowed_handler_namespaces: Allowed namespace prefixes for handler
62
+ loading. Only effective when allow_third_party_handlers=True.
63
+ """
64
+
65
+ model_config = ConfigDict(
66
+ strict=True,
67
+ frozen=True,
68
+ extra="forbid",
69
+ from_attributes=True,
70
+ )
71
+
72
+ allow_third_party_handlers: bool = Field(
73
+ default=False,
74
+ description="Enable loading handlers from third-party namespaces. "
75
+ "When False, only TRUSTED_HANDLER_NAMESPACE_PREFIXES are allowed.",
76
+ )
77
+
78
+ allowed_handler_namespaces: tuple[str, ...] = Field(
79
+ default=TRUSTED_HANDLER_NAMESPACE_PREFIXES,
80
+ description="Allowed namespace prefixes for handler loading. "
81
+ "Only effective when allow_third_party_handlers=True.",
82
+ )
83
+
84
+ def get_effective_namespaces(self) -> tuple[str, ...]:
85
+ """Get the effective namespace allowlist based on configuration.
86
+
87
+ Returns:
88
+ Tuple of allowed namespace prefixes. If third-party handlers
89
+ are disabled, returns only the trusted defaults regardless
90
+ of the allowed_handler_namespaces setting.
91
+
92
+ Example:
93
+ >>> config = ModelSecurityConfig()
94
+ >>> config.get_effective_namespaces()
95
+ ('omnibase_core.', 'omnibase_infra.')
96
+
97
+ >>> config = ModelSecurityConfig(
98
+ ... allow_third_party_handlers=True,
99
+ ... allowed_handler_namespaces=("custom.namespace.",),
100
+ ... )
101
+ >>> config.get_effective_namespaces()
102
+ ('custom.namespace.',)
103
+ """
104
+ if not self.allow_third_party_handlers:
105
+ return TRUSTED_HANDLER_NAMESPACE_PREFIXES
106
+ return self.allowed_handler_namespaces
107
+
108
+
109
+ __all__: list[str] = ["ModelSecurityConfig"]