# SPDX-License-Identifier: GPL-3.0-or-later """EVE-style event envelope: one typed wrapper for every emitted record. Every envelope carries a stable ``schema`` id, an ``event_type`` discriminator, a ``timestamp`` and ``host``, and the type-specific payload under a key named by the event type (mirroring Suricata's EVE JSON, where an ``alert`` record holds its detail under ``"alert"``). This is the reference contract a future Go agent must emit byte-for-byte; the Python side is the oracle. """ from __future__ import annotations import os from datetime import datetime from .schemas import EVENT_V1 # Closed set of v1 discriminators. New types are added additively in later # phases (e.g. "dns", "tls", "http", "flow", "anomaly", "capture"); removing or # renaming a value is a breaking change. EVENT_TYPES = frozenset({"alert", "incident", "status"}) def build_event( event_type: str, payload: dict, *, host: str | None = None, timestamp: str | None = None, ) -> dict: """Wrap ``payload`` in an ``enodia.event.v1`` envelope. Raises ``ValueError`` if ``event_type`` is not a known v1 discriminator. """ if event_type not in EVENT_TYPES: raise ValueError(f"unknown event_type: {event_type!r}") if host is None: host = os.uname().nodename if timestamp is None: timestamp = datetime.now().astimezone().isoformat() return { "schema": EVENT_V1, "event_type": event_type, "timestamp": timestamp, "host": host, event_type: payload, } def from_alert(alert, *, host: str | None = None, timestamp: str | None = None) -> dict: """Wrap an ``Alert`` (via its ``to_dict()``) in an ``alert`` event envelope.""" return build_event("alert", alert.to_dict(), host=host, timestamp=timestamp)