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
Nesting limit¶
A payload nested deeper than 64 maps and lists is refused as a decoding error, the envelope map being level 1,
so a body may nest 63 levels (serializer::MAX_DEPTH, the Java engine's MsgPack.MAX_DEPTH). The limit keeps a
crafted payload from exhausting the decoder's stack. EventEnvelope::from_bytes reports
Nesting deeper than 64 levels, and the Event API answers such a request with HTTP 400.
A header that promises more than the input holds is refused at the header, before anything is allocated for its
declared length or count, and so is the format byte 0xc1, which the specification never uses; the shared vector file
msgpack-hostile-header-vectors.json pins the same inputs in both engines.
Empty input decodes as an empty map, so an empty byte array is an empty envelope and never a decoding error, as the Java engine - the reference implementation - reads it; the canonical package decoder refuses it, because a package is never empty.
Bytes after the top-level container, well-formed or not, are refused as a decoding error: a payload holds exactly one
value, in the event codec as in the canonical package decoder. EventEnvelope::from_bytes reports Unexpected bytes
after the value at offset N, and the Event API answers such a request with HTTP 400; the Java engine, the reference
implementation, applies the same rule.
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 astext/plain, and a binary body asapplication/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: trueto 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-onlymy_correlation_idkey 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.