API Overview — Platform & PostOffice¶
Two small classes are the whole runtime surface a composable function ever touches:
Platform— the service registry: register a function at a route with a pool of worker instances, query and release routes.PostOffice— the messaging client: fire-and-forget send, RPC with timeout, scheduled delivery, and the trace-aware business APIs.
Functions themselves stay plain Rust (see
Write Your First Function); these APIs are how
events move between them. Every method documented here exists on the structs in
crates/platform-core/src/platform.rs and crates/platform-core/src/post_office.rs.
use std::time::Duration;
use platform_core::{EventEnvelope, Platform, PostOffice};
let po = PostOffice::new(&Platform::get_instance());
let reply = po
.request(
EventEnvelope::new().set_to("v1.get.profile").set_body(request)?,
Duration::from_secs(5),
)
.await?;
Rust port
Java PostOffice capabilities not (yet) in this port: broadcast, fork-n-join
parallel RPC (request(List<EventEnvelope>, timeout)), event-over-HTTP
(asyncRequest against a remote instance), the exists() multi-route check
(use Platform::has_route per route), journaling helpers, and the Kv convenience
class. Java Platform extras not ported: waitForProvider and setSelf/personality.
The public/private function distinction IS ported: functions are private by
default (is_private = true on #[preload], register vs register_public), and a
private function is not reachable through the /api/event endpoint (403), exactly as
in Java. The port adds nothing that Java lacks — absent here means deferred or out of
scope, never renamed.
Platform¶
Platform::get_instance()¶
| Signature | Returns |
|---|---|
Platform::get_instance() |
Platform |
The process-wide platform (Java Platform.getInstance()), created on first use. The handle
is cheap to clone. Platform::new() remains available for isolated registries in tests;
the application lifecycle uses the shared instance.
Platform::name()¶
| Signature | Returns |
|---|---|
Platform::name() |
String |
The application name: the application.name configuration key, else application.
Platform::origin()¶
| Signature | Returns |
|---|---|
Platform::origin() |
&'static str |
This process's unique origin id — a dash-less UUID minted once per process. It appears in telemetry datasets and in the per-instance transient-store directory name.
register(route, function, instances)¶
| Signature | Returns |
|---|---|
register(&self, route: &str, function: Arc<dyn ComposableFunction>, instances: usize) |
Result<(), AppError> |
Registers a function at a route with instances concurrent workers (Java
platform.register(route, lambda, instances)). Route names are lowercase letters, digits,
., -, _, with at least one dot and no leading, trailing, or consecutive dots.
Registration fails (status 400) on an invalid name, a duplicate route, or zero instances.
Must be called within a tokio runtime — the manager and workers are spawned tasks.
Most applications never call this directly: #[preload] registers functions
declaratively at startup (see the Macros Reference).
register_with_options(route, function, instances, options)¶
| Signature | Returns |
|---|---|
register_with_options(&self, route, function, instances, options: FunctionOptions) |
Result<(), AppError> |
Registration with explicit FunctionOptions — the programmatic form of the annotation
markers: zero_traced (the #[zero_tracing] analog — executions excluded from trace
recording) and interceptor (the #[event_interceptor] analog — the function replies
manually and the worker sends no automatic reply on success).
platform.register_with_options(
"manual.reply.service",
Arc::new(InterceptorFunction),
10,
FunctionOptions { zero_traced: false, interceptor: true },
)?;
has_route(route)¶
| Signature | Returns |
|---|---|
has_route(&self, route: &str) |
bool |
True when the route is registered locally (Java hasRoute).
release(route)¶
| Signature | Returns |
|---|---|
release(&self, route: &str) |
bool |
Releases a route (Java release): the manager stops, the workers exit as their channels
close, and the route's elastic queue is destroyed. Returns whether the route existed.
routes()¶
| Signature | Returns |
|---|---|
routes(&self) |
Vec<String> |
All registered route names, sorted for stable output.
instances(route)¶
| Signature | Returns |
|---|---|
instances(&self, route: &str) |
Option<usize> |
The worker count of a registered route, or None when the route does not exist.
PostOffice¶
PostOffice::new(&platform)¶
| Signature | Returns |
|---|---|
PostOffice::new(platform: &Platform) |
PostOffice |
The messaging client, holding a handle to the platform. Cheap to clone; create one wherever you need it.
When used inside a traced function, every send/request automatically stamps the
current trace context onto the outbound event: the trace id/path, this function's span id
(the receiver's parent span), the sender route (when from is unset), and the business
correlation-id (when the event carries none). Outside a trace this is a no-op.
send(event)¶
| Signature | Returns |
|---|---|
async send(&self, event: EventEnvelope) |
Result<(), AppError> |
Fire-and-forget delivery to event.to() (Java po.send). Errors: 400 when to is
missing, 404 when the route is not registered. When the destination's bounded mailbox is
full the call awaits — reactive back-pressure, not drops.
request(event, timeout)¶
| Signature | Returns |
|---|---|
async request(&self, event: EventEnvelope, timeout: Duration) |
Result<EventEnvelope, AppError> |
RPC (Java po.request(event, timeout)): delivers the event with the reserved
temporary.inbox reply listener as reply_to and a unique correlation id, and awaits
the reply (the caller's original correlation id is restored on it). On expiry the call
fails with status 408. request(...).await reads as synchronous but never blocks a
runtime thread — the calling task is suspended until the reply arrives.
let reply = po
.request(
EventEnvelope::new().set_to("greeting.demo").set_body(req)?,
Duration::from_secs(5),
)
.await?;
let body: GreetingResponse = reply.body_as()?;
A reply with status() >= 400 is a delivered error response — check has_error() when the
callee can fail meaningfully.
send_later(event, delay)¶
| Signature | Returns |
|---|---|
send_later(&self, event: EventEnvelope, delay: Duration) |
String (timer id) |
Schedules a future one-time delivery (Java po.sendLater(event, time)): the event is sent
after delay. The returned timer id cancels it via cancel_future_event. The Event Script
engine uses this for flow TTL watchdogs.
cancel_future_event(timer_id)¶
| Signature | Returns |
|---|---|
cancel_future_event(&self, timer_id: &str) |
bool |
Cancels a scheduled delivery (Java po.cancelFutureEvent(id)). Returns whether the timer
was still pending.
my_trace_id() / my_trace_path()¶
| Signature | Returns |
|---|---|
my_trace_id(&self) / my_trace_path(&self) |
Option<String> |
The trace id and trace path of the current traced request (Java getTraceId /
getTracePath on the trace-aware PostOffice). None outside a trace.
my_correlation_id()¶
| Signature | Returns |
|---|---|
my_correlation_id(&self) |
Option<String> |
The business correlation-id of the current traced request (Java
getMyCorrelationId) — the end-to-end identifier captured at the HTTP edge (see
Reserved Names & Headers). None outside a trace or when
the incoming request carried none.
annotate_trace(key, value)¶
| Signature | Returns |
|---|---|
annotate_trace(&self, key: &str, value: impl Serialize) |
&Self |
Attaches business context to the distributed-trace dataset that flows to the telemetry
sink (Java annotateTrace) — it appears in the annotations block of this execution's
trace record. Silent no-op outside a trace.
update_context(key, value)¶
| Signature | Returns |
|---|---|
update_context(&self, key: &str, value: impl Serialize) |
Result<(), AppError> |
Attaches business context to the application log stream (Java updateContext): the
key-value appears in the context block of every subsequent structured log line of this
request (the log context is on by default — see the
Configuration Reference). A null value removes the key. The
reserved context keys (cid, traceId, tracePath, spanId, parentSpanId, service,
utc) are rejected with status 400; outside a trace the call is a silent no-op.
Errors: AppError¶
Both APIs (and every function) speak AppError — an HTTP-style status code plus a message:
use platform_core::AppError;
let err = AppError::new(404, "profile not found");
assert_eq!(404, err.status());
assert_eq!("profile not found", err.message());
Returning Err(AppError) from a function becomes an error reply (status >= 400) to the
caller; on a fire-and-forget delivery it is logged instead.
Utility: ManagedCache¶
The Java ManagedCache ported (design record: docs/design/managed-cache-port.md) — a
named, self-expiring (expire-after-write), size-bounded in-memory cache with a
process-wide registry. The engine (moka, Caffeine's Rust lineage) is an internal detail;
eviction under capacity pressure is deterministic LRU — a deliberate, documented
improvement over the Java original's approximate W-TinyLFU.
use platform_core::ManagedCache;
// idempotent by name — the first creation's parameters win
let cache = ManagedCache::create_cache("my.cache", 5000); // TTL ms (min 1000), max 2000 items
let sized = ManagedCache::create_cache_with_limit("big.cache", 60_000, 10_000);
cache.put("key", MyStruct { .. }); // any Any + Send + Sync value
let value = cache.get_as::<MyStruct>("key"); // Option<Arc<MyStruct>> — None if absent or wrong type
let exists = cache.exists("key");
cache.remove("key");
cache.clear();
let same = ManagedCache::get_instance("my.cache"); // resolve by name from any module
let all = ManagedCache::get_cache_collection(); // sorted snapshot for introspection
Values are type-erased Arc handles (the Rust carrier of Java's Object reference
semantics) — store one value shape per named cache. For a value that is already an
Arc, use put_arc (passing an Arc to put would nest it and get_as would miss).
Expired entries are reclaimed lazily on access plus a 10-minute housekeeper the
application lifecycle starts automatically; correctness never depends on the sweep.
Adapted from the mercury-composable guide docs/guides/api-overview.md; keys/APIs enumerated from this repository's source.