Skip to content

EventEnvelope Reference

EventEnvelope is the immutable message container between composable functions — the only thing that ever passes from one function to another. It is the Rust port of the Java org.platformlambda.core.models.EventEnvelope, and it has the same three parts:

Part Contents
metadata routing (id, to, from, reply_to), correlation (cid), tracing (trace_id, trace_path, span_id), status, exec_time
headers HashMap<String, String> — parameters and metadata about the body
body a dynamic rmpv::Value — the analog of Java's untyped Object payload

Envelopes are built with fluent setters and read with getters; every accessor documented here exists on the struct in crates/platform-core/src/envelope.rs.

use platform_core::EventEnvelope;

let event = EventEnvelope::new()
    .set_to("v1.get.profile")
    .set_header("type", "query")
    .set_body(ProfileRequest { id: 12 })?;

Constructing and addressing

EventEnvelope::new()

Returns Notes
EventEnvelope fresh envelope with a generated id, empty headers, Nil body

Every new envelope gets a unique dash-less UUID as its id.

set_to(route) / to()

Setter Getter
set_to(&str) -> Self to() -> Option<&str>

The destination route name. PostOffice::send returns a 400 error when to is missing and 404 when the route is not registered.

set_from(route) / from()

Setter Getter
set_from(&str) -> Self from() -> Option<&str>

The sender's route. Inside a traced request the platform stamps this automatically on outbound events (when unset), so replies and telemetry can attribute the caller.

set_reply_to(route) / reply_to()

Setter Getter
set_reply_to(&str) -> Self reply_to() -> Option<&str>

Where the function's return value is delivered. PostOffice::request sets this to a temporary RPC inbox automatically; event interceptors read it to reply manually.

set_correlation_id(cid) / correlation_id()

Setter Getter
set_correlation_id(&str) -> Self correlation_id() -> Option<&str>

The correlation id. On the RPC path this is transport plumbing (the caller's id is kept, or one is minted); the worker copies the request's correlation id onto the reply. The business correlation-id of the current request is read through PostOffice::my_correlation_id instead.

Tracing metadata

set_trace(trace_id, trace_path) / trace_id() / trace_path()

Setter Getters
set_trace(&str, &str) -> Self trace_id() -> Option<&str>, trace_path() -> Option<&str>

The distributed-trace identity and path (e.g. GET /api/greeting/{user}). Normally the platform manages these: the REST edge starts a trace when the endpoint declares tracing: true, and every PostOffice send/RPC inside a traced function propagates them.

set_span_id(span_id) / span_id()

Setter Getter
set_span_id(&str) -> Self span_id() -> Option<&str>

The sender's span id, carried so the receiver knows its own parent span — this is how parent-child span lineage chains across hops (and across HTTP boundaries via the W3C traceparent header).

Status and errors

set_status(code) / status() / has_error()

Method Behavior
set_status(i32) -> Self set an HTTP-style status code
status() -> i32 unset means 200
has_error() -> bool status() >= 400

Status codes are HTTP-style throughout the platform: a status of 400 or higher is an error. A function returning Err(AppError) becomes an error reply whose status is the AppError's code and whose body is its message; a timed-out RPC yields status 408.

let reply = po.request(event, Duration::from_secs(5)).await?;
if reply.has_error() {
    log::warn!("call failed with status {}", reply.status());
}

Headers

set_header(key, value) / headers() / header(key)

Method Behavior
set_header(&str, &str) -> Self add or replace one header
headers() -> &HashMap<String, String> all headers
header(&str) -> Option<&str> one header

Headers carry parameters and metadata about the body — both are delivered to the target function (handle_event receives the headers map alongside the input).

Body

set_body(value)

Signature Returns
set_body<T: Serialize>(self, value: T) Result<Self, AppError>

Serializes any serde-serializable value into the dynamic body — the analog of Java's setBody(Object) PoJo transport. Fails with status 500 if the value cannot be represented.

set_raw_body(value)

Signature Returns
set_raw_body(self, value: rmpv::Value) Self

Sets the body from an already-dynamic value — no serialization step, no Result.

body() / body_as::<T>()

Method Behavior
body() -> &rmpv::Value the raw dynamic body
body_as<T: DeserializeOwned>() -> Result<T, AppError> deserialize into a concrete type (Java getBody(Class))
let request: serde_json::Value = input.body_as()?;      // free-form
let profile: Profile = reply.body_as()?;                // typed

Performance metadata

exec_time()

Signature Returns
exec_time(&self) Option<f32> (milliseconds)

The function execution time, stamped by the worker on every reply and rounded to three decimal places at the source — the reply envelope, the telemetry dataset, and the Playground traveler all report the same value.

Fields set automatically

Application code normally sets only to, headers, and body (plus reply_to for manual replies). The platform manages the rest:

  • PostOffice (outbound, inside a traced request): trace id/path, the sender's span id, from (when unset), and the business correlation-id (when the event carries none).
  • PostOffice::request: reply_to (a temporary inbox) and the correlation id.
  • The worker (on every reply): the request's correlation id, from (the executing route), to (the reply route), exec_time, and the trace triple so the next hop chains correctly.

Serialization

The body is an rmpv::Value (MessagePack's dynamic value type), so a body is already in wire shape in memory — typed values pass through serde on set_body/body_as.

  • In-process events are never serialized. The in-memory bus moves the envelope directly; to_bytes()/from_bytes() (MsgPack) are used only on the spill path — when a route's elastic queue buffers a burst to disk.
  • JSON at HTTP boundaries. REST automation renders a map/array body as application/json, a string body as text/plain, and a binary body as application/octet-stream; websocket frames and outbound HTTP client bodies are JSON as well.
  • Null omission — absent means null. At every boundary — the wire (JSON and MsgPack alike) and every in-memory event-bus hop — null map key-values are omitted by default, so a consumer must treat an absent key and a null value as the same thing; the behavior is deterministic and identical to the Java engine on every delivery path. Set serializer.null.transport: true to transport nulls explicitly. Array elements are never dropped (ordering is preserved), and an empty [] or {} is a real value. See the Configuration Reference.

to_bytes() / from_bytes(bytes)

Method Behavior
to_bytes() -> Result<Vec<u8>, AppError> encode the envelope as MsgPack
EventEnvelope::from_bytes(&[u8]) -> Result<Self, AppError> decode an envelope

Reserved envelope tags

The wire's optional tags field (map of str → str) carries engine-managed metadata. Tags are never visible to a user function — the engine consumes them at delivery. Two tag names are reserved with cross-language semantics that a conforming implementation must honor (see the Java repo's wire-format specification):

  • rpc — marks an RPC request (value = timeout in ms). The serving engine suppresses the worker-side trace record for an RPC-served execution; the caller's round-trip record is the single record for that span.
  • my_cid — carries the business correlation-id across touch points and Event-over-HTTP hops. Metadata is never transported as envelope headers; the receiving engine injects the read-only my_correlation_id key into the function's input header copy from this tag at delivery.

Rust port

The envelope encodes as the standard event envelope wire format — a MsgPack map with descriptive string keys shared verbatim with the Java engine (normative spec: the Java repo's Event Envelope Wire Format; golden conformance vectors are kept byte-identical in both repositories). Java's legacy compact flag-keyed encoding is not decoded (v1 is standard-only). Envelope features tied to unported subsystems do not exist here: serialized exception/stack-trace transport (getException, stack.trace.transport.size), broadcast flags, and the Kv helper. tags are engine-reserved (see above), and envelope-level annotations ride REPLY envelopes only (the worker attaches PostOffice::annotate_trace values; the RPC caller folds them into the trace record and strips them). Trace annotation and business-context APIs are on the PostOffice — see the API Overview.


Adapted from the mercury-composable guide docs/guides/event-envelope-reference.md; keys/APIs enumerated from this repository's source.