iii-core-primitives
Use when registering iii functions, binding triggers, selecting sync/void/enqueue invocation, creating workers, inspecting the live worker registry, installing registry workers, authoring custom triggers, moving channel data, or adapting external HTTP functions across TypeScript, Python, and Rust.
By iii-hq · 2,266 installs
npx skills add iii-hq/iii --skill iii-core-primitives
Source repository · Upstream listing
Core Primitives
iii has three top level primitives:
Function : a named unit of work such as orders::validate
Trigger : an event source bound to a function
Worker : a process that connects to the engine and executes functions
Since 0.25 every function is also observable : iii.observe(source, target) invokes target
on each invocation of source that passes its filters. Observers replace trigger bindings over
time; the built in providers ( http , cron , queue, state , stream , pub/sub, database) still
use trigger types in 0.25, so keep registerTrigger for them. See iii observe .
Use :: in function IDs, leading slashes in HTTP api path , and expression for cron config.
Function Registration
Register local handlers when you control the implementation. Register HTTP invoked functions when
iii should call an existing external endpoint.
Shape Use for
registerFunction(id, handler, options?) Local worker code
registerFunction(id, HttpInvocationConfig, options?) Existing HTTP services
registerTrigger({ type, function id, config, metadata? }) Binding a built in event source (deprecated in 0.25, still required for providers that have not migrated)
observe({ function id, args?, filters? }, target, options?) Running target after another function's invocations, or on an engine event ( iii observe )
trigger({ function id, payload, action?, timeout? }) Calling any function by ID
Functions and triggers can carry metadata for ownership, discovery, and generated skills. Do not put
secrets in metadata.
Workers and Registry
A worker is any process that connects to the engine and registers functions or trigger types. There
are two common paths:
Task Use
Create your own worker Write SDK code that calls registerWorker , registerFunction , and registerTrigger or observe
Add an existing capability Browse https://workers.iii.dev/ , then call compose::add worker=<name
Pin a worker version compose::add worker=<name @<version
Declare a local worker Add worker: path://./workers/my worker under containers:
Reproduce a project Commit the exact versions in worker compose.yaml
The public worker registry at workers.iii.dev is for installable workers such as HTTP, state,
queue, pub/sub, cron, database, shell, console, rbac proxy, and other capability workers.
( iii sandbox and iii observability are engine owned and are not added through the registry.) Those workers may ship their own function level skills; do not duplicate every capability
as a top level iii skill.
Worker Manifest
Use iii.worker.yaml when iii should start a local worker project:
The manifest describes how to start the process. Once running, the WebSocket connection and function
registrations are what make the worker part of iii.
Live Engine Registry
The engine keeps a live registry of connected workers, registered functions, triggers, and trigger
types. Read it through the built in discovery functions:
Function Returns
engine::workers::list Connected workers and metrics
engine::functions::list Registered functions
engine::triggers::list Registered triggers
engine::trigger types::list Advertised trigger types and schemas
engine::observers::list Observers you may see ( function id , namespace , worker filters)
For topology changes, observe the engine hosted sources engine::on workers changed or
engine::on functions changed with deliver: 'input' (see iii observe ). The older
engine::workers available and engine::functions available trigger types still fire.
Built In Trigger Shapes
The workers behind these types have not moved to source functions in 0.25, so bind them with
registerTrigger . Each call logs a one time deprecation warning per process; it is informational
and the binding works as before. For log , the engine also hosts the observability::on log
source ( iii observe ).
Trigger type Registration config Handler payload
http { api path: "/orders/:id", http method: "POST" } { query params, path params, headers, path, method, body }
cron { expression: "0 0 9 " } { trigger, job id, scheduled time, actual time }
durable:subscriber { topic: "payments" } The queued message payload
subscribe { topic: "orders.created" } The published event payload
state { scope: "orders", key?: "order 123" } { event type, scope, key, old value, new value }
stream { stream name, group id, item id? } Stream event details
log { level: "warn" } OpenTelemetry style log data
Add condition function id to built in trigger config when the handler should only run if a boolean
condition function returns true . With observe , a condition you can state over the event becomes a
declarative filter; a condition that needs code stays on registerTrigger or moves into the target.
Invocation Modes
Mode Shape Use when
Sync trigger({ function id, payload }) The caller needs the result
Void TriggerAction.Void() Optional side effect, no result needed
Enqueue TriggerAction.Enqueue({ queue }) Reliable async work with queue policy
Use enqueue for work that must complete with retries. Use void for analytics, notifications, and
other non critical side effects.
Namespaces
Every worker registers in a namespace: the namespace init option, else III NAMESPACE , else the
engine's default . Compose sets III NAMESPACE to its daemon's namespace for every worker it
starts, so all workers in one worker compose.yaml share a namespace and call each other by bare
function id. Engine owned functions ( engine:: , configuration:: , stream:: ) live in default ;
engine:: resolves there implicitly, the others need namespace: "default" on the call from a
namespaced worker. Routing is strict and ids are never prefixed with a namespace. From the CLI,
iii trigger n <namespace <function selects it. Details and per language examples are in
iii sdk reference .
Code Examples
TypeScript
Python
Rust
Advanced Primitive Patterns
Custom event sources : since 0.25, register a function with an observe descriptor
( delivery: 'owner' , args format , onObserverAdded / onObserverRemoved ) and hand events to
observers with obs.emit or obs.call (see iii observe ). registerTriggerType({ id,
description }, handler) is deprecated: it still works and logs one warning per process. Keep
listener setup in registerTrigger and cleanup in unregisterTrigger if you maintain one.
Channels : use createChannel() for binary or streaming data that should not be serialized into
JSON payloads. Pass readerRef or writerRef through a function payload.
HTTP invoked functions : use HttpInvocationConfig for legacy APIs, third party endpoints, or
immutable services. Use environment variable names for auth fields, not raw secrets.
Schemas : Rust can derive request/response schemas with schemars::JsonSchema ; Python can use
type hints or Pydantic; Node can pass JSON Schema manually.
When to Use
Use this skill for function registration, trigger binding, trigger payload shapes, invocation mode
decisions, worker creation, worker registry access, trigger conditions, custom trigger types,
channels, and HTTP invoked functions.
Use it to decide between registerTrigger (built in providers in 0.25) and observe (any
function, engine events, migrated providers).
Use this when a task spans TypeScript, Python, or Rust examples for the same iii primitive.
Boundaries
For iii.observe , filters, observable functions, local dispatch, and migrating off
registerTrigger / registerTriggerType , use iii observe .
For engine ports, adapters, engine owned workers, RBAC ( rbac proxy ), and deployment config,
use iii engine config .
For SDK specific package exports and language caveats, use iii sdk reference .
For complete backend designs such as workflows, CQRS, agentic systems, and reactive apps, use
iii architecture patterns .
For failed invocations, timeouts, RBAC denials, and retryability, use iii error handling .
Worker backed capability details live with the worker docs, not as top level iii skills.