feat(schema): add enodia.event.v1 EVE-style event envelope

This commit is contained in:
Luna 2026-07-10 04:43:35 -07:00
parent 8d929b50bd
commit 54bd8ff1f4
4 changed files with 111 additions and 0 deletions

46
enodia_sentinel/event.py Normal file
View file

@ -0,0 +1,46 @@
# 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,
}