Documentation Index
Fetch the complete documentation index at: https://iii.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
This page documents the wire-level protocol the engine and SDK workers exchange. Most projects use
a language SDK (Node, Python,
Rust, Browser) and never touch the
protocol directly. The shapes below are the source of truth those SDKs serialize to.
Observability introspection (traces, logs, metrics, sampling rules, alerts, rollups) is owned
end-to-end by the iii-observability worker.
Connection ports
The engine binds three ports of its own and runs alongside one more from the observability worker:
| Port | Bound by | Surface |
|---|
3111 | engine | REST API. |
3112 | engine | Stream API (WebSocket; consumer-side stream subscriptions). |
49134 | engine | SDK WebSocket; this is what iii_sdk::register_worker opens. |
9464 | iii-observability worker | Prometheus metrics endpoint (typically exposed from the same container as the engine). |
The console UI runs on 3113 and is launched separately by iii console.
Connection flow
A worker opens the SDK WebSocket (default ws://127.0.0.1:49134) and sends the registrations it
holds in memory: each RegisterFunction, RegisterTrigger, and RegisterTriggerType it intends to
expose. The worker then calls engine::workers::register to publish its own metadata (runtime,
version, OS, PID, isolation), and the engine answers with a WorkerRegistered { worker_id } frame
carrying the assigned UUID.
The connection is bidirectional from that point on: the engine pushes InvokeFunction frames at the
worker, and the worker pushes InvocationResult, additional registrations, or unregistrations back.
Message types
Every frame is a JSON object discriminated by message_type. The full set, defined on Message in
engine/src/protocol.rs:
| Frame | Direction | Purpose |
|---|
RegisterFunction | worker -> engine | Make a function callable by function_id. |
UnregisterFunction | worker -> engine | Drop a previously registered function. |
RegisterTrigger | worker -> engine | Bind a function to a trigger instance. |
UnregisterTrigger | worker -> engine | Drop a trigger binding. |
TriggerRegistrationResult | engine -> worker | Ack / error for a RegisterTrigger. |
RegisterTriggerType | worker -> engine | Declare a new trigger type the worker advertises. |
RegisterService | worker -> engine | Group related functions under a service id. |
InvokeFunction | engine -> worker | Call a registered function with a payload. |
InvocationResult | worker -> engine | Carry the function’s result or error back. |
WorkerRegistered | engine -> worker | Acknowledge the worker, with the assigned worker_id. |
Ping / Pong | bidirectional | Liveness; keeps idle connections from timing out. |
RegisterFunction
{
"message_type": "register_function",
"id": "math::add",
"description": "Add two numbers.",
"request_format": {
"type": "object",
"properties": { "a": { "type": "number" }, "b": { "type": "number" } }
},
"response_format": { "type": "object", "properties": { "c": { "type": "number" } } },
"metadata": { "owner": "math-team" },
"invocation": null
}
id is required. description, request_format, response_format, and metadata are optional
and feed the iii console and the agent-readable skills. invocation is reserved for external HTTP
functions (HttpInvocationRef); leave it null for in-process handlers.
RegisterTrigger
{
"message_type": "register_trigger",
"id": "math::add@http",
"trigger_type": "http",
"function_id": "math::add",
"config": { "api_path": "/math/add", "http_method": "POST" },
"metadata": null
}
config is the per-trigger-type configuration; the shape is defined by whatever worker advertised
that trigger_type (e.g. iii-http for http triggers). The engine responds with a
TriggerRegistrationResult carrying an optional error: ErrorBody.
RegisterTriggerType
{
"message_type": "register_trigger_type",
"id": "webhook",
"description": "HTTP webhook trigger",
"trigger_request_format": { "type": "object", ... },
"call_request_format": { "type": "object", ... }
}
trigger_request_format is the JSON Schema for the trigger’s per-binding config.
call_request_format is the JSON Schema for the payload delivered to bound functions when the
trigger fires.
InvokeFunction
{
"message_type": "invoke_function",
"invocation_id": "9f3c…",
"function_id": "math::add",
"data": { "a": 2, "b": 3 },
"traceparent": "00-…",
"baggage": "k=v,…",
"action": { "type": "void" }
}
invocation_id is omitted on Void invocations (the worker has no result channel to reply on).
traceparent and baggage carry W3C trace context. action is the routing flag (see
Trigger actions below); absent / null means synchronous.
InvocationResult
Success:
{
"message_type": "invocation_result",
"invocation_id": "9f3c…",
"function_id": "math::add",
"result": { "c": 5 },
"error": null,
"traceparent": "00-…",
"baggage": "k=v,…"
}
Failure:
{
"message_type": "invocation_result",
"invocation_id": "9f3c…",
"function_id": "math::add",
"result": null,
"error": {
"code": "invocation_failed",
"message": "boom",
"stacktrace": "TraceError: …"
}
}
ErrorBody.code values the engine emits today include invocation_failed (handler threw),
invocation_stopped (the owning worker disconnected mid-flight, so the engine cancels the in-flight
call and surfaces this code to the caller), function_not_found, function_not_invokable,
TIMEOUT (client-side timeout), FORBIDDEN (RBAC denial).
Trigger actions
InvokeFunction.action is tagged by type and lowercase-encoded on the wire:
| Wire shape | Meaning |
|---|
omitted / null | Synchronous; the worker replies with InvocationResult. |
{ "type": "void" } | Fire-and-forget; no invocation_id, no reply. |
{ "type": "enqueue", "queue": "math" } | Route through the named queue (provided by iii-queue). |
Invocation lifecycle
For synchronous calls the engine assigns an invocation_id, forwards the InvokeFunction to the
owning worker, and waits for the matching InvocationResult. For Void actions the engine forwards
without an invocation_id and never expects a reply. For Enqueue the engine hands the invocation
to the queue worker, which persists it and re-invokes the target function on a subscriber according
to the queue’s retry policy.
Engine discovery functions
The engine registers a built-in set of functions under the engine::* namespace for introspection
and worker lifecycle. Defined in
engine/src/workers/engine_fn/mod.rs:
| Function | Purpose |
|---|
engine::channels::create | Create a streaming-channel reader / writer pair. |
engine::functions::list | List every registered function (filterable by include_internal). |
engine::workers::list | List every connected worker with metrics. |
engine::triggers::list | List every registered trigger (filterable by include_internal). |
engine::trigger-types::list | List every registered trigger type with its config and call request schemas. |
engine::workers::register | Publish the calling worker’s metadata (runtime, version, OS, PID, isolation). |
Engine discovery triggers
| Trigger | Fires when |
|---|
engine::functions-available | A function is registered or unregistered. |
engine::workers-available | A worker connects or disconnects. |
Engine-collected metrics
These metrics are emitted by the engine regardless of which language SDK a worker uses. Names and
units come from
engine/src/workers/observability/metrics.rs.
Invocations
| Metric | Instrument | Unit |
|---|
iii.invocations.total | counter | invocations |
iii.invocation.duration | histogram | seconds |
iii.invocation.errors.total | counter | errors |
Workers
| Metric | Instrument | Unit |
|---|
iii.workers.active | gauge | workers |
iii.workers.spawns.total | counter | workers |
iii.workers.deaths.total | counter | workers |
iii.workers.by_status | gauge | workers |
Per-worker
| Metric | Instrument | Unit |
|---|
iii.worker.memory.heap.bytes | gauge | bytes |
iii.worker.memory.rss.bytes | gauge | bytes |
iii.worker.cpu.percent | gauge | % |
iii.worker.event_loop.lag.ms | gauge | ms |
iii.worker.uptime.seconds | gauge | s |