Skip to content

event envelope

Kadyapam edited this page May 30, 2026 · 7 revisions

Event envelope

The wire format for POST /api/events — what workers (and CLI in distributed mode) send to ingest events into the event log.

Current shape (Rust noetl-server v1.0.3)

pub struct EventRequest {
    pub execution_id: String,    // bigint represented as decimal string
    pub step: String,             // e.g. "fetch_calendar"
    pub name: String,             // event_type, e.g. "step.enter", "call.done", "step.exit"
    pub payload: serde_json::Value,  // event-type-specific data
    pub meta: Option<serde_json::Value>,
    pub worker_id: Option<String>,
    pub actionable: bool,
    pub informative: bool,
}

Matches the Python noetl/server EventEmitRequest shape line-for-line — both implementations enforce the same external contract.

Four-shape divergence — reconciliation in flight

As of 2026-05-30, four envelope shapes exist across the NoETL Rust + Python codebase:

Source Type execution_id event name field extra fields
noetl-server (this crate) handlers::events::EventRequest String name payload, meta, worker_id, actionable, informative
Python noetl/server EventEmitRequest str event_type (enum) event_id, ...
noetl-worker client::WorkerEvent i64 ← diverges event_type (free string) payload
noetl-executor events::ExecutorEvent i64 ← diverges event_type (free string) step, status, created_at, context

The two servers agree. The worker and executor disagree on execution_id type AND on which fields are top-level vs nested in payload.

Current production behaviour: worker → server today works because:

  • Pydantic (Python) coerces i64str leniently during JSON deserialization.
  • The Rust server's sqlx layer likely does the same coercion.
  • The name vs event_type field naming is bridged somewhere (possibly the worker actually sends name not event_type and the local WorkerEvent is misnamed; needs verification).

Reconciliation roadmap

Tracked on noetl/ai-meta#30 — Appendix H Rust migration umbrella. Planned PR sequence:

  1. Server-side: add a v2 endpoint (POST /api/events/v2?) that accepts ExecutorEvent shape directly, alongside EventRequest for backward compat. Both implementations (Rust + Python) need the v2 path.
  2. Worker-side (R-1.2 PR-2c+ follow-up): switch client::ControlPlaneClient.emit_event to send ExecutorEvent to the v2 endpoint. Worker's WorkerEvent becomes a thin wrapper / direct re-export of executor::events::ExecutorEvent.
  3. CLI-side: same switch in distributed mode.
  4. Sunset: deprecate the v1 endpoint after a release cycle once all clients moved.

Each step is independently reviewable + reversible. No big-bang cutover.

Why ExecutorEvent's shape is the right target

  • step + status are first-class fields rather than buried in payload — easier for the projector + dashboard queries.
  • created_at is stamped at emit time → avoids server-clock skew when ordering events.
  • execution_id: i64 matches the Postgres bigint column type directly → no String⇄i64 conversion in the ingest path.
  • Documented in executor-crate-architecture as the design target.

See also

Clone this wiki locally