How workers expand iii
Workers add capability to an iii system. Each one contributes functions and triggers the engine can route to. This page covers deploying and wiring workers into a project. Once connected, a worker exposes:- Functions, callable by
function_idfrom anywhere in the system (see Using iii / Functions). - Triggers it advertises, which other workers can bind their functions to (see Using iii / Triggers).
Scaffold a new worker
iii worker init creates a new standalone worker from scratch. The command writes a
language-specific project directory with the iii SDK installed, an iii.worker.yaml manifest, and
example function and trigger registrations you can replace with your own.
typescript (ts), javascript (js), python (py), rust (rs).
The positional NAME is the target directory; pass --directory to override it.
Re-running iii worker init on a directory that already holds an iii worker (ie. has
.iii/worker.ini) will make no changes to the worker.
Worker init will fail by default when targeting a non-empty directory, use --allow-non-empty to
scaffold into any other non-empty directory.
To install an existing worker from the registry instead of scaffolding a new one, use
iii worker add. See Using iii / Workers for the registry surface.Connecting to the engine
A worker connects to the engine over WebSocket. The convention is to set the engine URL via theIII_URL environment variable, but it can also be passed explicitly to register_worker. The
connection string is the only coupling between a worker and the iii instance it joins, so the worker
process can be deployed anywhere reachable on the network.
- Node / TypeScript
- Python
- Rust
Worker lifecycle
States
Workers transition through a small set of states after connecting:connecting → connected → available / busy → disconnected. connecting is the WebSocket handshake.
connected means the Worker has joined the Engine’s registry. available and busy describe
whether the Worker is currently handling invocations. disconnected is the terminal state when the
WebSocket closes. The Engine tracks these transitions and surfaces them to other Workers and tooling
through its discovery functions, so the rest of the system can react.
Inspecting the live registry
To see what’s currently connected to the Engine, invoke one of theengine::*::list Functions to
get the current state of the registry. Each returns a list:
Example: list registry contents
Example: list registry contents
- Node / TypeScript
- Python
- Rust
Handling Worker disconnects
When a Worker’s WebSocket closes, the Engine cleans up after it automatically. Its Functions and Triggers leave the live registry, and any in-flight invocations of those Functions are cancelled.In flight requests
In flight requests will get ainvocation_stopped error, catch these errors and treat them like a
cancellation. Retrying will fail until the Worker that owns this function reconnects.
Example: catch `invocation_stopped`
Example: catch `invocation_stopped`
- Node / TypeScript
- Python
- Rust
Subscribe to changes
You can register a Trigger against one of the engine’s discovery events to react to topology changes as they happen. This is particularly useful for continuing work when a Worker comes back online.Example: subscribe to discovery events
Example: subscribe to discovery events
- Node / TypeScript
- Python
- Rust
Worker manifest
iii.worker.yaml is the manifest at the worker’s root that tells iii how to install dependencies,
run the worker, and pass through configuration. This applies to both the iii worker CLI commands
(e.g. start, stop, restart) and to workers
that iii starts automatically when they’re specified in iii’s
config.yaml.
description is an optional one-line, human/LLM-readable summary of what the worker does; the
iii CLI displays it when installing or inspecting the worker.
scripts.install runs once to fetch dependencies and scripts.start launches the worker. Here,
watchfiles reloads it whenever you edit a source file. runtime.base_image selects the OCI image
iii uses as the worker’s rootfs.
The manifest is metadata about starting the Worker. Once the Worker is running iii treats them all
the same. A Worker started by iii via its config.yaml, via iii worker start, or a manually run
process that uses the iii SDK all behave identically with the Engine.
If a worker isn’t starting correctly then make sure to check its manifest and
iii worker logs.Shutting down a worker
Call the SDK’sshutdown to close the WebSocket cleanly. The engine removes the worker’s Functions
and Triggers from the registry, fires engine::workers-available with worker_disconnected, and
cancels in-flight invocations targeting them with invocation_stopped.
Without shutdown, an abrupt process exit reaches the same state once the engine notices the
dropped socket; graceful shutdown makes it deterministic and faster.
- Node / TypeScript
- Python
- Rust
Shutdown is useful for One-shot / ephemeral workers. Kubernetes Jobs, serverless
containers, or scheduled scripts can connect like any other Worker, do their work, and
shutdown() (shutdown_async().await in Rust).