Skip to main content
This page documents MobKit v0.8.34 (mirrored from v0.8.34). MobKit separates route selection from message dispatch. The runtime owns trusted routing resolutions, rate limiting, bounded delivery history, and optional idempotency. The router and delivery modules provide the external MCP boundaries.

Delivery flow

1

Resolve

Call mobkit/routing/resolve with a recipient and optional channel, retry, backoff, and rate-limit values. The runtime calls the router module’s routing.resolve MCP tool, applies any matching runtime-route override, mints a route_id, and retains the resulting RoutingResolution as trusted state.
2

Send

Call mobkit/delivery/send with the complete trusted resolution, a JSON payload, and an optional idempotency_key.
3

Validate

The runtime requires the supplied resolution to equal the retained resolution for its route_id. Unknown or modified resolutions are rejected before dispatch.
4

Dispatch and record

Unless keyed idempotency replays an existing record, the runtime applies the resolved rate limit, invokes the delivery module’s delivery.send MCP tool, and appends a DeliveryRecord to bounded history.

Routing contracts

A mutable route override and a resolved route are different types:
The request defaults are channel = "notification", retry_max = 1, backoff_ms = 250, and rate_limit_per_minute = 2. Before module and runtime-route overrides, the runtime infers email for recipients containing @, sms for recipients starting with +, and webhook otherwise. These sink strings are routing defaults, not a promise that MobKit ships an SMTP, SMS, or webhook adapter. The router module can override the sink and return a target_module; a matching runtime route can override both again. The current mobkit/delivery/send contract dispatches only a trusted resolution whose target_module is exactly delivery. Any other target is rejected before the delivery MCP boundary call, so alternate target values are representable for routing inspection but are not dispatchable. The delivery module owns actual dispatch. Runtime routes can be listed, added, and deleted through:
  • mobkit/routing/routes/list
  • mobkit/routing/routes/add
  • mobkit/routing/routes/delete
Runtime routes may use * recipient and channel matches. A matching route is applied after the router MCP response.

Mob-member routes

When the unified runtime reconciles its active roster and the router module is loaded, it manages one route per member:
The route key has no channel suffix.

Optional idempotency

DeliverySendRequest.idempotency_key is Option<String>:
When a non-empty key is present, the runtime scopes it as {route_id}:{idempotency_key}. A repeat with the same payload and canonical resolution returns the existing DeliveryRecord without another boundary call. Reusing the scoped key with a different payload is rejected. This protection is conditional and bounded:
  • No key means no idempotency lookup.
  • The index is local to the runtime instance.
  • Delivery history retains at most 200 records.
  • Evicting a record also removes its idempotency entries, so a later retry can dispatch again.
Idempotency therefore protects a keyed retry only while the corresponding record remains in the current runtime’s bounded history.

Rate limiting

Each resolution carries rate_limit_per_minute. The implementation uses fixed 60-second windows keyed by:
  • route_id
  • recipient
  • sink
  • the window start timestamp
The runtime retains counters for the current and previous window. It charges the counter only after the MCP boundary call succeeds. A request at the limit is rejected with DeliverySendError::RateLimited.

Delivery records and adapters

DeliveryRecord contains the delivery and route IDs, recipient, sink, target module, original payload, status, attempt records, first and final attempt timestamps, optional idempotency key, and optional sink_adapter. The sink comes from routing. The optional sink_adapter is recorded only when the delivery module returns a non-empty adapter string. MobKit does not maintain a fixed built-in adapter inventory in this contract. Delivery dispatch emits a unified module event with:
  • module: "delivery"
  • event_type: "send"
  • payload fields delivery_id, route_id, recipient, sink, status, and attempts
Query bounded history with mobkit/delivery/history. The request supports optional recipient and sink filters plus a capped limit.

Clock ordering

Routing and delivery timestamps use a runtime-relative monotonic floor that is also advanced to the latest merged-event timestamp. A route or delivery inserted after an existing merged event therefore does not receive an earlier timestamp.

See also

  • Gating - evaluate policy before dispatch
  • Modules - router and delivery MCP boundaries
  • Events - exact resolved and send event names
  • JSON-RPC API - routing and delivery methods