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
@@ -0,0 +1,92 @@
1
+ """Ledger entry model for event ledger storage.
2
+
3
+ This module defines the data structure for a single event ledger entry,
4
+ representing one row in the event_ledger table.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from uuid import UUID
9
+
10
+ from pydantic import BaseModel, ConfigDict, Field
11
+
12
+ from omnibase_core.types import JsonType
13
+
14
+
15
+ class ModelLedgerEntry(BaseModel):
16
+ """Represents a single event ledger entry (one row in event_ledger table).
17
+
18
+ This model captures the complete state of an event as it was received
19
+ from Kafka, including the raw payload, Kafka position metadata, and
20
+ extracted envelope fields for queryability.
21
+
22
+ All extracted metadata fields are nullable because:
23
+ 1. Events may not conform to the ONEX envelope schema
24
+ 2. Malformed events should still be ledgered for debugging
25
+ 3. Legacy events may lack certain fields
26
+ """
27
+
28
+ model_config = ConfigDict(frozen=True, extra="forbid")
29
+
30
+ ledger_entry_id: UUID = Field(
31
+ ...,
32
+ description="Unique identifier for this ledger entry",
33
+ )
34
+
35
+ # Kafka position - uniquely identifies the event in the stream
36
+ topic: str = Field(
37
+ ...,
38
+ min_length=1,
39
+ description="Kafka topic from which the event was consumed",
40
+ )
41
+ partition: int = Field(
42
+ ...,
43
+ ge=0,
44
+ description="Kafka partition number",
45
+ )
46
+ kafka_offset: int = Field(
47
+ ...,
48
+ ge=0,
49
+ description="Kafka offset within the partition",
50
+ )
51
+
52
+ # Raw event data (base64 encoded for transport)
53
+ event_key: str | None = Field(
54
+ default=None,
55
+ description="Base64-encoded Kafka message key, if present",
56
+ )
57
+ event_value: str = Field(
58
+ ...,
59
+ description="Base64-encoded Kafka message value (the event payload)",
60
+ )
61
+ onex_headers: dict[str, JsonType] = Field(
62
+ default_factory=dict,
63
+ description="ONEX-specific headers extracted from Kafka headers",
64
+ )
65
+
66
+ # Extracted metadata (ALL NULLABLE for schema flexibility)
67
+ envelope_id: UUID | None = Field(
68
+ default=None,
69
+ description="Extracted envelope ID from the event, if present",
70
+ )
71
+ correlation_id: UUID | None = Field(
72
+ default=None,
73
+ description="Extracted correlation ID for request tracing",
74
+ )
75
+ event_type: str | None = Field(
76
+ default=None,
77
+ description="Extracted event type identifier",
78
+ )
79
+ source: str | None = Field(
80
+ default=None,
81
+ description="Extracted source system or service identifier",
82
+ )
83
+
84
+ # Timestamps
85
+ event_timestamp: datetime | None = Field(
86
+ default=None,
87
+ description="Original event timestamp from the envelope, if present",
88
+ )
89
+ ledger_written_at: datetime = Field(
90
+ ...,
91
+ description="Timestamp when this entry was written to the ledger",
92
+ )
@@ -0,0 +1,53 @@
1
+ """Ledger query model for searching ledger entries.
2
+
3
+ This module defines the query parameters for ledger search operations.
4
+ """
5
+
6
+ from datetime import datetime
7
+ from uuid import UUID
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class ModelLedgerQuery(BaseModel):
13
+ """Query parameters for ledger searches.
14
+
15
+ All filter fields are optional - omitting a field means no filtering
16
+ on that dimension. Multiple filters are combined with AND logic.
17
+
18
+ The limit and offset fields enable pagination through large result sets.
19
+ """
20
+
21
+ model_config = ConfigDict(frozen=True, extra="forbid")
22
+
23
+ correlation_id: UUID | None = Field(
24
+ default=None,
25
+ description="Filter by correlation ID for request tracing",
26
+ )
27
+ event_type: str | None = Field(
28
+ default=None,
29
+ description="Filter by event type identifier",
30
+ )
31
+ topic: str | None = Field(
32
+ default=None,
33
+ description="Filter by Kafka topic",
34
+ )
35
+ start_time: datetime | None = Field(
36
+ default=None,
37
+ description="Filter events at or after this timestamp",
38
+ )
39
+ end_time: datetime | None = Field(
40
+ default=None,
41
+ description="Filter events before this timestamp",
42
+ )
43
+ limit: int = Field(
44
+ default=100,
45
+ ge=1,
46
+ le=10000,
47
+ description="Maximum number of entries to return",
48
+ )
49
+ offset: int = Field(
50
+ default=0,
51
+ ge=0,
52
+ description="Number of entries to skip for pagination",
53
+ )
@@ -0,0 +1,41 @@
1
+ """Ledger query result model for search responses.
2
+
3
+ This module defines the result structure returned by ledger search operations.
4
+ """
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+ from omnibase_infra.nodes.node_ledger_write_effect.models.model_ledger_entry import (
9
+ ModelLedgerEntry,
10
+ )
11
+ from omnibase_infra.nodes.node_ledger_write_effect.models.model_ledger_query import (
12
+ ModelLedgerQuery,
13
+ )
14
+
15
+
16
+ class ModelLedgerQueryResult(BaseModel):
17
+ """Result of a ledger query operation.
18
+
19
+ Contains the matching entries along with pagination metadata
20
+ and the original query for reference.
21
+ """
22
+
23
+ model_config = ConfigDict(frozen=True, extra="forbid")
24
+
25
+ entries: list[ModelLedgerEntry] = Field(
26
+ ...,
27
+ description="List of matching ledger entries",
28
+ )
29
+ total_count: int = Field(
30
+ ...,
31
+ ge=0,
32
+ description="Total number of entries matching the query (before pagination)",
33
+ )
34
+ has_more: bool = Field(
35
+ ...,
36
+ description="True if more entries exist beyond the current page",
37
+ )
38
+ query: ModelLedgerQuery = Field(
39
+ ...,
40
+ description="The query parameters used to generate this result",
41
+ )
@@ -0,0 +1,89 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 OmniNode Team
3
+ """Node Ledger Write Effect - Capability-oriented event ledger write node.
4
+
5
+ This effect node provides event ledger write capabilities using PostgreSQL.
6
+ Named by capability ("ledger.write"), not by vendor (e.g., PostgreSQL).
7
+
8
+ Core Principle:
9
+ "I'm interested in what you do, not what you are"
10
+
11
+ This node follows the ONEX declarative pattern:
12
+ - DECLARATIVE effect driven by contract.yaml
13
+ - Zero custom storage logic - all behavior from handler
14
+ - Lightweight shell that delegates to handler implementation
15
+ - Pattern: "Contract-driven, handlers wired externally"
16
+
17
+ Extends NodeEffect from omnibase_core for external I/O operations.
18
+ All storage logic is 100% driven by handler implementations, not Python code.
19
+
20
+ Capabilities:
21
+ - ledger.write: Append events to the audit ledger
22
+ - ledger.query: Query events from the audit ledger
23
+
24
+ Event Topics:
25
+ Consumed:
26
+ - {env}.{namespace}.onex.cmd.ledger-append.v1
27
+ - {env}.{namespace}.onex.cmd.ledger-query.v1
28
+ Published:
29
+ - {env}.{namespace}.onex.evt.ledger-appended.v1
30
+ - {env}.{namespace}.onex.evt.ledger-query-result.v1
31
+
32
+ Design Decisions:
33
+ - 100% Contract-Driven: All capabilities in YAML, not Python
34
+ - Zero Custom Methods: Base class handles everything
35
+ - Declarative Execution: Handler wired externally
36
+ - Capability-Oriented: Named by what it does, not what it uses
37
+
38
+ Related:
39
+ - contract.yaml: Capability definitions and IO operations
40
+ - models/: Input, output models (ModelPayloadLedgerAppend, ModelLedgerAppendResult)
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ from typing import TYPE_CHECKING
46
+
47
+ from omnibase_core.nodes.node_effect import NodeEffect
48
+
49
+ if TYPE_CHECKING:
50
+ from omnibase_core.models.container.model_onex_container import ModelONEXContainer
51
+
52
+
53
+ class NodeLedgerWriteEffect(NodeEffect):
54
+ """Effect node for event ledger write operations.
55
+
56
+ Capability: ledger.write
57
+
58
+ Provides a capability-oriented interface for event ledger operations.
59
+ Uses PostgreSQL for storing events in an append-only audit ledger with
60
+ idempotent write support via (topic, partition, kafka_offset) constraint.
61
+
62
+ This node is declarative - all behavior is defined in contract.yaml and
63
+ implemented through the handler. No custom storage logic exists in this class.
64
+
65
+ The audit ledger serves as the system's source of truth for all events.
66
+ Events are never dropped - even malformed events are captured with raw
67
+ data intact for later analysis or re-processing.
68
+
69
+ Attributes:
70
+ container: ONEX dependency injection container
71
+
72
+ Example:
73
+ >>> from omnibase_core.models.container import ModelONEXContainer
74
+ >>> container = ModelONEXContainer()
75
+ >>> node = NodeLedgerWriteEffect(container)
76
+ >>> # Handler must be wired externally via registry
77
+ """
78
+
79
+ def __init__(self, container: ModelONEXContainer) -> None:
80
+ """Initialize the ledger write effect node.
81
+
82
+ Args:
83
+ container: ONEX dependency injection container for resolving
84
+ dependencies defined in contract.yaml.
85
+ """
86
+ super().__init__(container)
87
+
88
+
89
+ __all__ = ["NodeLedgerWriteEffect"]
@@ -0,0 +1,13 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 OmniNode Team
3
+ """Protocols for event ledger persistence operations.
4
+
5
+ This package defines the protocol interfaces for ledger persistence,
6
+ enabling duck typing and testability for handlers.
7
+ """
8
+
9
+ from omnibase_infra.nodes.node_ledger_write_effect.protocols.protocol_ledger_persistence import (
10
+ ProtocolLedgerPersistence,
11
+ )
12
+
13
+ __all__ = ["ProtocolLedgerPersistence"]
@@ -0,0 +1,127 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 OmniNode Team
3
+ """Protocol definition for event ledger persistence operations.
4
+
5
+ This module defines the ProtocolLedgerPersistence interface for ledger
6
+ storage and retrieval operations. Handlers implementing this protocol
7
+ can be used interchangeably for testing and production.
8
+
9
+ Design Decisions:
10
+ - runtime_checkable: Enables isinstance() checks for duck typing
11
+ - Async methods: All operations are async for non-blocking I/O
12
+ - Typed models: Uses Pydantic models for type safety
13
+ - Nullable metadata: Query filters are optional (None = no filter)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from datetime import datetime
19
+ from typing import TYPE_CHECKING, Protocol, runtime_checkable
20
+ from uuid import UUID
21
+
22
+ if TYPE_CHECKING:
23
+ from omnibase_infra.nodes.node_ledger_write_effect.models import (
24
+ ModelLedgerAppendResult,
25
+ ModelLedgerEntry,
26
+ )
27
+ from omnibase_infra.nodes.reducers.models import ModelPayloadLedgerAppend
28
+
29
+
30
+ @runtime_checkable
31
+ class ProtocolLedgerPersistence(Protocol):
32
+ """Protocol for event ledger persistence operations.
33
+
34
+ This protocol defines the interface for appending events to the audit
35
+ ledger and querying events by various criteria. Implementations must
36
+ provide idempotent append operations via the (topic, partition, kafka_offset)
37
+ unique constraint.
38
+
39
+ Implementations:
40
+ - HandlerLedgerAppend: Production handler composing with HandlerDb
41
+ - MockLedgerPersistence: Test double for unit testing
42
+
43
+ Example:
44
+ >>> async def process_events(
45
+ ... persistence: ProtocolLedgerPersistence,
46
+ ... payload: ModelPayloadLedgerAppend,
47
+ ... ) -> ModelLedgerAppendResult:
48
+ ... return await persistence.append(payload)
49
+ """
50
+
51
+ async def append(
52
+ self,
53
+ payload: ModelPayloadLedgerAppend,
54
+ ) -> ModelLedgerAppendResult:
55
+ """Append an event to the audit ledger with idempotent write support.
56
+
57
+ Uses INSERT ... ON CONFLICT DO NOTHING with the (topic, partition, kafka_offset)
58
+ unique constraint. Duplicate events are detected without raising errors.
59
+
60
+ Args:
61
+ payload: Event payload containing Kafka position and event data.
62
+ The payload includes base64-encoded event_key and event_value
63
+ which are decoded to BYTEA for storage.
64
+
65
+ Returns:
66
+ ModelLedgerAppendResult with:
67
+ - success: True if operation completed without error
68
+ - ledger_entry_id: UUID of created entry, None if duplicate
69
+ - duplicate: True if ON CONFLICT was triggered
70
+
71
+ Raises:
72
+ InfraConnectionError: If database connection fails
73
+ InfraTimeoutError: If operation times out
74
+ """
75
+ ...
76
+
77
+ async def query_by_correlation_id(
78
+ self,
79
+ correlation_id: UUID,
80
+ limit: int = 100,
81
+ offset: int = 0,
82
+ ) -> list[ModelLedgerEntry]:
83
+ """Query ledger entries by correlation ID for distributed tracing.
84
+
85
+ Args:
86
+ correlation_id: The correlation ID to search for.
87
+ limit: Maximum number of entries to return (default: 100).
88
+ offset: Number of entries to skip for pagination (default: 0).
89
+
90
+ Returns:
91
+ List of ModelLedgerEntry matching the correlation ID,
92
+ ordered by event_timestamp descending.
93
+ """
94
+ ...
95
+
96
+ async def query_by_time_range(
97
+ self,
98
+ start: datetime,
99
+ end: datetime,
100
+ correlation_id: UUID | None = None,
101
+ event_type: str | None = None,
102
+ topic: str | None = None,
103
+ limit: int = 100,
104
+ offset: int = 0,
105
+ ) -> list[ModelLedgerEntry]:
106
+ """Query ledger entries within a time range.
107
+
108
+ Uses COALESCE(event_timestamp, ledger_written_at) for consistent
109
+ ordering even when event_timestamp is NULL.
110
+
111
+ Args:
112
+ start: Start of time range (inclusive).
113
+ end: End of time range (exclusive).
114
+ correlation_id: Correlation ID for distributed tracing (auto-generated if None).
115
+ event_type: Optional filter by event type.
116
+ topic: Optional filter by Kafka topic.
117
+ limit: Maximum number of entries to return (default: 100, max: 10000).
118
+ offset: Number of entries to skip for pagination (default: 0).
119
+
120
+ Returns:
121
+ List of ModelLedgerEntry within the time range,
122
+ ordered by timestamp descending.
123
+ """
124
+ ...
125
+
126
+
127
+ __all__ = ["ProtocolLedgerPersistence"]
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 OmniNode Team
3
+ """Registry for event ledger write effect node."""
4
+
5
+ from omnibase_infra.nodes.node_ledger_write_effect.registry.registry_infra_ledger_write import (
6
+ RegistryInfraLedgerWrite,
7
+ )
8
+
9
+ __all__ = ["RegistryInfraLedgerWrite"]
@@ -0,0 +1,121 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 OmniNode Team
3
+ """Registry for event ledger write effect node components.
4
+
5
+ This registry exports all public symbols for the node_ledger_write_effect node,
6
+ providing a single import point for consumers of this node.
7
+
8
+ Exported Components:
9
+ - NodeLedgerWriteEffect: The declarative effect node
10
+ - HandlerLedgerAppend: Handler for idempotent append operations
11
+ - HandlerLedgerQuery: Handler for query operations
12
+ - ProtocolLedgerPersistence: Protocol for ledger persistence
13
+ - Models: Data models for ledger operations
14
+
15
+ Example:
16
+ >>> from omnibase_infra.nodes.node_ledger_write_effect.registry import (
17
+ ... RegistryInfraLedgerWrite,
18
+ ... )
19
+ >>> # Access all components via registry
20
+ >>> node_cls = RegistryInfraLedgerWrite.node
21
+ >>> append_handler = RegistryInfraLedgerWrite.handler_append
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import TYPE_CHECKING
27
+
28
+ # Handlers
29
+ from omnibase_infra.nodes.node_ledger_write_effect.handlers import (
30
+ HandlerLedgerAppend,
31
+ HandlerLedgerQuery,
32
+ )
33
+
34
+ # Models
35
+ from omnibase_infra.nodes.node_ledger_write_effect.models import (
36
+ ModelLedgerAppendResult,
37
+ ModelLedgerEntry,
38
+ ModelLedgerQuery,
39
+ ModelLedgerQueryResult,
40
+ )
41
+
42
+ # Node
43
+ from omnibase_infra.nodes.node_ledger_write_effect.node import NodeLedgerWriteEffect
44
+
45
+ # Protocol
46
+ from omnibase_infra.nodes.node_ledger_write_effect.protocols import (
47
+ ProtocolLedgerPersistence,
48
+ )
49
+
50
+ # Intent payload model (from reducers)
51
+ from omnibase_infra.nodes.reducers.models import ModelPayloadLedgerAppend
52
+
53
+
54
+ class RegistryInfraLedgerWrite:
55
+ """Registry providing access to all ledger write effect node components.
56
+
57
+ This class provides a centralized access point for all components of the
58
+ node_ledger_write_effect node. Use this registry for dependency injection
59
+ and container registration.
60
+
61
+ Class Attributes:
62
+ node: The NodeLedgerWriteEffect class
63
+ handler_append: The HandlerLedgerAppend class
64
+ handler_query: The HandlerLedgerQuery class
65
+ protocol: The ProtocolLedgerPersistence protocol
66
+ models: Tuple of all model classes
67
+
68
+ Example:
69
+ >>> from omnibase_infra.nodes.node_ledger_write_effect.registry import (
70
+ ... RegistryInfraLedgerWrite,
71
+ ... )
72
+ >>> # Create node instance
73
+ >>> node = RegistryInfraLedgerWrite.node(container)
74
+ >>> # Create handlers
75
+ >>> append_handler = RegistryInfraLedgerWrite.handler_append(container, db_handler)
76
+ """
77
+
78
+ # Node
79
+ node = NodeLedgerWriteEffect
80
+
81
+ # Handlers
82
+ handler_append = HandlerLedgerAppend
83
+ handler_query = HandlerLedgerQuery
84
+
85
+ # Protocol
86
+ protocol = ProtocolLedgerPersistence
87
+
88
+ # Models (as tuple for iteration)
89
+ models = (
90
+ ModelLedgerAppendResult,
91
+ ModelLedgerEntry,
92
+ ModelLedgerQuery,
93
+ ModelLedgerQueryResult,
94
+ ModelPayloadLedgerAppend,
95
+ )
96
+
97
+ # Individual model references
98
+ model_append_result = ModelLedgerAppendResult
99
+ model_entry = ModelLedgerEntry
100
+ model_query = ModelLedgerQuery
101
+ model_query_result = ModelLedgerQueryResult
102
+ model_payload_append = ModelPayloadLedgerAppend
103
+
104
+
105
+ # Re-export all components at module level for convenience
106
+ __all__ = [
107
+ "RegistryInfraLedgerWrite",
108
+ # Node
109
+ "NodeLedgerWriteEffect",
110
+ # Handlers
111
+ "HandlerLedgerAppend",
112
+ "HandlerLedgerQuery",
113
+ # Protocol
114
+ "ProtocolLedgerPersistence",
115
+ # Models
116
+ "ModelLedgerAppendResult",
117
+ "ModelLedgerEntry",
118
+ "ModelLedgerQuery",
119
+ "ModelLedgerQueryResult",
120
+ "ModelPayloadLedgerAppend",
121
+ ]
@@ -94,20 +94,22 @@ from omnibase_core.services.service_handler_registry import ServiceHandlerRegist
94
94
  from omnibase_infra.enums import EnumInfraTransportType
95
95
  from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
96
96
  from omnibase_infra.protocols import ProtocolContainerAware
97
+ from omnibase_infra.runtime.constants_security import (
98
+ TRUSTED_HANDLER_NAMESPACE_PREFIXES,
99
+ )
97
100
  from omnibase_infra.runtime.contract_loaders import (
98
101
  load_handler_class_info_from_contract,
99
102
  )
100
103
 
101
104
  logger = logging.getLogger(__name__)
102
105
 
103
- # Security: Namespace allowlist for dynamic handler imports
106
+ # Security: Use centralized namespace allowlist for dynamic handler imports
104
107
  # Per CLAUDE.md Handler Plugin Loader security patterns, only trusted namespaces
105
108
  # are allowed for dynamic imports to prevent arbitrary code execution.
106
109
  # Error code: NAMESPACE_NOT_ALLOWED (HANDLER_LOADER_013)
107
- ALLOWED_NAMESPACES: tuple[str, ...] = (
108
- "omnibase_infra.",
109
- "omnibase_core.",
110
- )
110
+ # NOTE: Aliased for backwards compatibility - prefer importing directly from
111
+ # constants_security for new code.
112
+ ALLOWED_NAMESPACES: tuple[str, ...] = TRUSTED_HANDLER_NAMESPACE_PREFIXES
111
113
 
112
114
 
113
115
  def _validate_handler_protocol(handler: object) -> tuple[bool, list[str]]:
@@ -1,19 +1,23 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  # Copyright (c) 2025 OmniNode Team
3
- """Models for Registration Reducers.
3
+ """Models for Reducers.
4
4
 
5
- This module exports models used by the RegistrationReducer (pure function pattern).
5
+ This module exports models used by infrastructure reducers (pure function pattern).
6
6
 
7
7
  Available Models:
8
8
  - ModelRegistrationState: Immutable state for pure reducer pattern
9
9
  - ModelRegistrationConfirmation: Confirmation event from Effect layer (Phase 2)
10
10
  - ModelPayloadConsulRegister: Payload for Consul registration intents
11
+ - ModelPayloadLedgerAppend: Payload for audit ledger append intents
11
12
  - ModelPayloadPostgresUpsertRegistration: Payload for PostgreSQL upsert intents
12
13
  """
13
14
 
14
15
  from omnibase_infra.nodes.reducers.models.model_payload_consul_register import (
15
16
  ModelPayloadConsulRegister,
16
17
  )
18
+ from omnibase_infra.nodes.reducers.models.model_payload_ledger_append import (
19
+ ModelPayloadLedgerAppend,
20
+ )
17
21
  from omnibase_infra.nodes.reducers.models.model_payload_postgres_upsert_registration import (
18
22
  ModelPayloadPostgresUpsertRegistration,
19
23
  )
@@ -26,6 +30,7 @@ from omnibase_infra.nodes.reducers.models.model_registration_state import (
26
30
 
27
31
  __all__ = [
28
32
  "ModelPayloadConsulRegister",
33
+ "ModelPayloadLedgerAppend",
29
34
  "ModelPayloadPostgresUpsertRegistration",
30
35
  "ModelRegistrationConfirmation",
31
36
  "ModelRegistrationState",
@@ -18,6 +18,10 @@ from uuid import UUID
18
18
 
19
19
  from pydantic import BaseModel, ConfigDict, Field
20
20
 
21
+ from omnibase_infra.models.registration.model_node_event_bus_config import (
22
+ ModelNodeEventBusConfig,
23
+ )
24
+
21
25
  # NOTE: ModelIntentPayloadBase was removed in omnibase_core 0.6.2
22
26
  # Using pydantic.BaseModel directly as the base class
23
27
 
@@ -34,6 +38,8 @@ class ModelPayloadConsulRegister(BaseModel):
34
38
  service_name: Service name for Consul service catalog.
35
39
  tags: Service tags for filtering and categorization.
36
40
  health_check: Optional health check configuration.
41
+ event_bus_config: Resolved event bus topics for registry storage.
42
+ If None, node is not included in dynamic topic routing lookups.
37
43
  """
38
44
 
39
45
  model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
@@ -70,6 +76,11 @@ class ModelPayloadConsulRegister(BaseModel):
70
76
  description="Optional health check configuration (HTTP, Interval, Timeout).",
71
77
  )
72
78
 
79
+ event_bus_config: ModelNodeEventBusConfig | None = Field(
80
+ default=None,
81
+ description="Resolved event bus topics for registry storage.",
82
+ )
83
+
73
84
 
74
85
  __all__ = [
75
86
  "ModelPayloadConsulRegister",