iii-core-primitives
All-time installs
2,213
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.
Other options
Summary
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.
Raw SKILL.md
9,228 bytes---
name: iii-core-primitives
description: >-
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.
---
# 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
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 an event source |
| `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` |
| 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, observability, sandbox, database, shell, console, and other capability
workers. 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:
```yaml
name: math-worker
runtime:
kind: python
package_manager: pip
entry: math_worker.py
scripts:
install: "pip install -r requirements.txt"
start: "python math_worker.py"
```
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 |
For topology changes, bind triggers to `engine::workers-available` or
`engine::functions-available`.
## Built-In Trigger Shapes
| 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`.
## 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.
## Code Examples
### TypeScript
```typescript
import { registerWorker, TriggerAction } from "iii-sdk";
const iii = registerWorker("ws://localhost:49134", { workerName: "orders-worker" });
iii.registerFunction("orders::validate", async (order) => {
if (!order.id) throw new Error("missing order id");
return { ...order, valid: true };
});
iii.registerFunction("orders::process", async (order) => {
const validated = await iii.trigger({ function_id: "orders::validate", payload: order });
await iii.trigger({
function_id: "orders::charge",
payload: validated,
action: TriggerAction.Enqueue({ queue: "payments" }),
});
return { accepted: true, orderId: validated.id };
});
iii.registerTrigger({
type: "http",
function_id: "orders::process",
config: { api_path: "/orders", http_method: "POST" },
});
```
### Python
```python
from iii import register_worker
iii = register_worker("ws://localhost:49134")
def validate(order):
if not order.get("id"):
raise ValueError("missing order id")
return {**order, "valid": True}
def process(order):
validated = iii.trigger({"function_id": "orders::validate", "payload": order})
iii.trigger({
"function_id": "orders::charge",
"payload": validated,
"action": {"type": "enqueue", "queue": "payments"},
})
return {"accepted": True, "orderId": validated["id"]}
iii.register_function("orders::validate", validate)
iii.register_function("orders::process", process)
iii.register_trigger({
"type": "http",
"function_id": "orders::process",
"config": {"api_path": "/orders", "http_method": "POST"},
})
```
### Rust
```rust
use iii_sdk::{register_worker, InitOptions, RegisterFunction, TriggerAction};
use iii_sdk::protocol::{RegisterTriggerInput, TriggerRequest};
use serde_json::json;
let iii = register_worker("ws://127.0.0.1:49134", InitOptions::default());
iii.register_function(RegisterFunction::new("orders::validate", |order: serde_json::Value| {
if order["id"].is_null() {
return Err("missing order id".into());
}
Ok(json!({ "valid": true, "order": order }))
}))?;
let process_client = iii.clone();
iii.register_function(RegisterFunction::new_async("orders::process", move |order: serde_json::Value| {
let iii = process_client.clone();
async move {
let validated = iii.trigger(TriggerRequest::new("orders::validate", order)).await?;
iii.trigger(TriggerRequest {
function_id: "orders::charge".into(),
payload: validated.clone(),
action: Some(TriggerAction::Enqueue { queue: "payments".into() }),
timeout_ms: None,
}).await?;
Ok(json!({ "accepted": true, "order": validated }))
}
}))?;
iii.register_trigger(RegisterTriggerInput {
trigger_type: "http".into(),
function_id: "orders::process".into(),
config: json!({ "api_path": "/orders", "http_method": "POST" }),
metadata: None,
})?;
```
## Advanced Primitive Patterns
- **Custom triggers**: use `registerTriggerType({ id, description }, handler)` when the event source is
not built in. Keep listener setup in `registerTrigger` and cleanup in `unregisterTrigger`.
- **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 this when a task spans TypeScript, Python, or Rust examples for the same iii primitive.
## Boundaries
- For engine ports, adapters, queue retry policy, worker manager, RBAC listeners, 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.
Security audits
SnykPASS
SocketWARN
Gen Agent Trust HubPASS

