diff --git a/docs/advanced/adapters.mdx b/docs/advanced/adapters.mdx
index 7c5c256a7..0cbb23e94 100644
--- a/docs/advanced/adapters.mdx
+++ b/docs/advanced/adapters.mdx
@@ -62,7 +62,7 @@ adapter:
```
- For retry behavior, dead-letter queues, and full config reference, see the [Queue module](/workers/iii-queue).
+ For retry behavior, dead-letter queues, and full config reference, see the [Queue worker](/workers/iii-queue).
---
diff --git a/docs/advanced/architecture.mdx b/docs/advanced/architecture.mdx
index ee4a597b7..ff805652a 100644
--- a/docs/advanced/architecture.mdx
+++ b/docs/advanced/architecture.mdx
@@ -26,11 +26,11 @@ graph TD
Reg[Worker
Registry]
subgraph "Core Modules"
- API[RestApiModule]
- Stream[StreamModule]
- Log[OtelModule]
- Queue[QueueModule]
- Cron[CronModule]
+ API[iii-http]
+ Stream[iii-stream]
+ Log[iii-observability]
+ Queue[iii-queue]
+ Cron[iii-cron]
end
end
diff --git a/docs/advanced/protocol.mdx b/docs/advanced/protocol.mdx
index 5e1804a04..a2019a713 100644
--- a/docs/advanced/protocol.mdx
+++ b/docs/advanced/protocol.mdx
@@ -84,7 +84,7 @@ Configure a trigger that maps to a function:
// Trigger-specific configuration
// For 'http': { api_path: '/path', http_method: 'GET' }
// For 'durable:subscriber': { topic: 'topic.name' }
- // For 'cron': { expression: '0 * * * *' }
+ // For 'cron': { expression: '0 0 * * * * *' }
// For 'log': { level: 'error' }
}
}
diff --git a/docs/examples/cron.mdx b/docs/examples/cron.mdx
index 3c88bd00d..957a42a35 100644
--- a/docs/examples/cron.mdx
+++ b/docs/examples/cron.mdx
@@ -42,7 +42,7 @@ iii.registerFunction(
iii.registerTrigger({
type: 'cron',
function_id: 'cron.periodic_job',
- config: { expression: '* * * * *' }, // every minute
+ config: { expression: '0 * * * * * *' }, // every minute
})
```
@@ -76,7 +76,7 @@ iii.register_function("cron.periodic_job", periodic_job)
iii.register_trigger({
"type": "cron",
"function_id": "cron.periodic_job",
- "config": {"expression": "* * * * *"}, # every minute
+ "config": {"expression": "0 * * * * * *"}, # every minute
})
```
@@ -90,7 +90,7 @@ use serde_json::json;
let iii = register_worker("ws://127.0.0.1:49134", InitOptions::default());
iii.register_function((RegisterFunctionMessage::with_id("cron.periodic_job".into()), |_input| async move {
- let logger = Logger());
+ let logger = Logger::new();
logger.info("Periodic job fired", None);
@@ -106,7 +106,7 @@ iii.register_function((RegisterFunctionMessage::with_id("cron.periodic_job".into
});
iii.register_trigger(RegisterTriggerInput { trigger_type: "cron".into(), function_id: "cron.periodic_job".into(), config: json!({
- "expression": "* * * * *",
+ "expression": "0 * * * * * *",
}), metadata: None })?;
```
@@ -161,7 +161,7 @@ use iii_sdk::{Logger, RegisterFunctionMessage, RegisterTriggerInput};
use serde_json::json;
iii.register_function((RegisterFunctionMessage::with_id("job.handle_tick".into()), |input| async move {
- let logger = Logger());
+ let logger = Logger::new();
logger.info("Periodic job processed", Some(input));
Ok(json!(null))
@@ -224,7 +224,7 @@ iii.registerFunction(
iii.registerTrigger({
type: 'cron',
function_id: 'cron.orders_sweep',
- config: { expression: '*/5 * * * *' },
+ config: { expression: '0 */5 * * * * *' },
})
```
@@ -269,7 +269,7 @@ def orders_sweep(_data) -> None:
logger.info("Sweep complete", {"checked": len(orders), "swept": swept})
iii.register_function("cron.orders_sweep", orders_sweep)
-iii.register_trigger({"type": "cron", "function_id": "cron.orders_sweep", "config": {"expression": "*/5 * * * *"}})
+iii.register_trigger({"type": "cron", "function_id": "cron.orders_sweep", "config": {"expression": "0 */5 * * * * *"}})
```
@@ -280,7 +280,7 @@ use iii_sdk::{Logger, TriggerRequest, TriggerAction, RegisterFunctionMessage, Re
use serde_json::json;
iii.register_function((RegisterFunctionMessage::with_id("cron.orders_sweep".into()), |_input| async move {
- let logger = Logger());
+ let logger = Logger::new();
let orders_val = iii.trigger(TriggerRequest::new("state::list", json!({ "scope": "orders" }))).await?;
let orders = orders_val.as_array().cloned().unwrap_or_default();
@@ -315,7 +315,7 @@ iii.register_function((RegisterFunctionMessage::with_id("cron.orders_sweep".into
Ok(json!(null))
});
-iii.register_trigger(RegisterTriggerInput { trigger_type: "cron".into(), function_id: "cron.orders_sweep".into(), config: json!({ "expression": "*/5 * * * *" }), metadata: None })?;
+iii.register_trigger(RegisterTriggerInput { trigger_type: "cron".into(), function_id: "cron.orders_sweep".into(), config: json!({ "expression": "0 */5 * * * * *" }), metadata: None })?;
```
@@ -323,26 +323,29 @@ iii.register_trigger(RegisterTriggerInput { trigger_type: "cron".into(), functio
## Cron expression format
-iii uses a six-field extended cron format. The optional leading field is seconds.
+iii supports six- or seven-field cron expressions: `second minute hour day month weekday [year]` (year is optional).
```
-┌──────────── second (0-59, optional)
+┌──────────── second (0-59)
│ ┌────────── minute (0-59)
│ │ ┌──────── hour (0-23)
│ │ │ ┌────── day of month (1-31)
│ │ │ │ ┌──── month (1-12)
-│ │ │ │ │ ┌── day of week (0-7)
-│ │ │ │ │ │
-* * * * * *
+│ │ │ │ │ ┌── day of week (0-7, Sun=0 or 7)
+│ │ │ │ │ │ ┌ year (optional)
+│ │ │ │ │ │ │
+* * * * * * *
```
| Expression | Meaning |
|---|---|
-| `* * * * *` | Every minute |
-| `0 * * * *` | Every hour |
-| `0 9 * * 1-5` | 09:00 on weekdays |
-| `*/5 * * * *` | Every 5 minutes |
-| `0/5 * * * * *` | Every 5 seconds (with seconds field) |
+| `0 * * * * *` | Every minute (6-field) |
+| `0 0 * * * *` | Every hour (6-field) |
+| `0 * * * * * *` | Every minute (7-field) |
+| `0 0 * * * * *` | Every hour (7-field) |
+| `0 0 9 * * 1-5 *` | 09:00 on weekdays |
+| `0 */5 * * * * *` | Every 5 minutes |
+| `*/5 * * * * * *` | Every 5 seconds |
## Key concepts
diff --git a/docs/examples/multi-trigger.mdx b/docs/examples/multi-trigger.mdx
index dea6ffc16..544e8b2d9 100644
--- a/docs/examples/multi-trigger.mdx
+++ b/docs/examples/multi-trigger.mdx
@@ -116,7 +116,7 @@ iii.registerTrigger({
iii.registerTrigger({
type: 'cron',
function_id: 'orders.handle',
- config: { expression: '* * * * *' },
+ config: { expression: '* * * * * * *' },
})
```
@@ -212,7 +212,7 @@ iii.register_trigger({"type": "http", "function_id": "orders.handle",
iii.register_trigger({"type": "durable:subscriber", "function_id": "orders.handle",
"config": {"topic": "order.created"}})
iii.register_trigger({"type": "cron", "function_id": "orders.handle",
- "config": {"expression": "* * * * *"}})
+ "config": {"expression": "* * * * * * *"}})
```
@@ -331,7 +331,7 @@ iii.register_trigger(RegisterTriggerInput {
iii.register_trigger(RegisterTriggerInput {
trigger_type: "cron".into(),
function_id: "orders.handle".into(),
- config: json!({ "expression": "* * * * *" }),
+ config: json!({ "expression": "* * * * * * *" }),
metadata: None,
})?;
```
diff --git a/docs/examples/state-management.mdx b/docs/examples/state-management.mdx
index 0407b1434..b98bb3fc2 100644
--- a/docs/examples/state-management.mdx
+++ b/docs/examples/state-management.mdx
@@ -281,7 +281,7 @@ iii.registerFunction(
iii.registerTrigger({
type: 'cron',
function_id: 'cron.orders_audit',
- config: { expression: '*/5 * * * *' },
+ config: { expression: '0 */5 * * * * *' },
})
```
@@ -316,7 +316,7 @@ def orders_audit(_data) -> None:
iii.register_function("cron.orders_audit", orders_audit)
-iii.register_trigger({"type": "cron", "function_id": "cron.orders_audit", "config": {"expression": "*/5 * * * *"}})
+iii.register_trigger({"type": "cron", "function_id": "cron.orders_audit", "config": {"expression": "0 */5 * * * * *"}})
```
@@ -364,7 +364,7 @@ iii.register_function(
iii.register_trigger(RegisterTriggerInput {
trigger_type: "cron".into(),
function_id: "cron.orders_audit".into(),
- config: json!({ "expression": "*/5 * * * *" }),
+ config: json!({ "expression": "0 */5 * * * * *" }),
metadata: None,
})?;
```
diff --git a/docs/examples/todo-app.mdx b/docs/examples/todo-app.mdx
index dfe62ecac..1b322ebeb 100644
--- a/docs/examples/todo-app.mdx
+++ b/docs/examples/todo-app.mdx
@@ -14,7 +14,7 @@ graph LR
Browser["Browser (iii-browser-sdk)"] -->|"single WebSocket"| RBAC["RBAC Port :3111"]
RBAC -->|"auth + expose"| Engine["iii Engine"]
APIWorker["API Worker (iii-sdk)"] -->|"ws :49134"| Engine
- Engine -->|"stream ops"| Stream["StreamModule :3112 (file-based KvStore)"]
+ Engine -->|"stream ops"| Stream["iii-stream :3112 (file-based KvStore)"]
Engine -->|"stream change events"| Browser
```
@@ -61,10 +61,10 @@ workers:
| --- | --- | --- |
| **iii-worker-manager** | 49134 | Internal port. The API worker connects here to register functions. |
| **iii-worker-manager (RBAC)** | 3111 | Public-facing port. The browser connects here. RBAC controls which functions are exposed and runs an auth function on every new connection. |
-| **ExecModule** | — | Runs `pnpm dev` to start the API worker process. |
-| **StreamModule** | 3112 | Manages stream state with a file-based KvStore adapter. The engine routes `stream::get`, `stream::set`, `stream::delete`, and `stream::list` to this module. |
+| **iii-exec** | — | Runs `pnpm dev` to start the API worker process. |
+| **iii-stream** | 3112 | Manages stream state with a file-based KvStore adapter. The engine routes `stream::get`, `stream::set`, `stream::delete`, and `stream::list` to this module. |
-The RBAC configuration on port `3111` references `todo-project::auth-function` and explicitly lists the five functions the browser is allowed to call. See [Worker RBAC](/docs/how-to/worker-rbac) for the full reference.
+The RBAC configuration on port `3111` references `todo-project::auth-function` and explicitly lists the five functions the browser is allowed to call. See [Worker RBAC](/how-to/worker-rbac) for the full reference.
## Backend
@@ -381,6 +381,6 @@ All CRUD operations (`addTodo`, `toggleTodo`, `deleteTodo`) call `iii.trigger` w
- **Single connection** — The browser opens one WebSocket to the RBAC port. Function calls and real-time stream events flow over the same connection.
- **No HTTP routes** — The API worker registers plain iii functions. The browser invokes them directly via `iii.trigger`. There is no REST layer.
-- **RBAC** — The engine's iii-worker-manager supports auth functions, expose lists, and middleware. This example uses a simple auth function that creates a session. See [Worker RBAC](/docs/how-to/worker-rbac) for the full reference.
+- **RBAC** — The engine's iii-worker-manager supports auth functions, expose lists, and middleware. This example uses a simple auth function that creates a session. See [Worker RBAC](/how-to/worker-rbac) for the full reference.
- **Engine-managed streams** — The `iii-stream` worker handles persistence (file-based KvStore in this example). The API worker reads and writes through `stream::*` function triggers — no custom stream implementation required.
- **Session isolation** — The auth function returns a `function_registration_prefix`. The engine prefixes every function registered by that browser session, so multiple clients can register `ui::on-todo-change` without colliding.
diff --git a/docs/how-to/expose-http-endpoint.mdx b/docs/how-to/expose-http-endpoint.mdx
index 9dba23552..62f92074a 100644
--- a/docs/how-to/expose-http-endpoint.mdx
+++ b/docs/how-to/expose-http-endpoint.mdx
@@ -17,7 +17,7 @@ Make sure `iii-config.yaml` has the REST API worker enabled:
- name: iii-http
config:
port: 3111
- host: localhost
+ host: 127.0.0.1
default_timeout: 30000
concurrency_request_limit: 1024
cors:
@@ -142,7 +142,7 @@ iii.register_trigger(RegisterTriggerInput {
### 4. Try it
```bash
-curl -X POST http://localhost:3111/users \
+curl -X POST http://127.0.0.1:3111/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
```
diff --git a/docs/how-to/schedule-cron-task.mdx b/docs/how-to/schedule-cron-task.mdx
index 5f49383ac..147a6b5b1 100644
--- a/docs/how-to/schedule-cron-task.mdx
+++ b/docs/how-to/schedule-cron-task.mdx
@@ -114,7 +114,7 @@ iii.register_trigger(RegisterTriggerInput {
-This runs the Function every second. The `expression` field uses a 7-field cron format with seconds support (`second minute hour day month weekday year`).
+This runs the Function every second. The `expression` field uses a 7-field cron format (`second minute hour day month weekday year`).
### Common schedules
diff --git a/docs/how-to/stream-realtime-data.mdx b/docs/how-to/stream-realtime-data.mdx
index ec520c9cf..901ca4ddf 100644
--- a/docs/how-to/stream-realtime-data.mdx
+++ b/docs/how-to/stream-realtime-data.mdx
@@ -16,7 +16,7 @@ workers:
- name: iii-stream
config:
port: ${STREAM_PORT:3112}
- host: localhost
+ host: 127.0.0.1
adapter:
name: kv
config:
@@ -253,7 +253,7 @@ println!("Messages: {:?}", messages);
Clients connect to the stream WebSocket endpoint to receive live updates:
```javascript title="client.js"
-const ws = new WebSocket('ws://localhost:3112/stream/chat/room-123')
+const ws = new WebSocket('ws://127.0.0.1:3112/stream/chat/room-123')
ws.onmessage = (event) => {
const update = JSON.parse(event.data)
diff --git a/docs/how-to/trigger-actions.mdx b/docs/how-to/trigger-actions.mdx
index 1deb38d53..08e6f9ad8 100644
--- a/docs/how-to/trigger-actions.mdx
+++ b/docs/how-to/trigger-actions.mdx
@@ -644,7 +644,7 @@ iii.registerFunction('etl::extract', async () => {
iii.registerTrigger({
type: 'cron',
function_id: 'etl::extract',
- config: { expression: '0 * * * *' },
+ config: { expression: '0 0 * * * * *' },
})
```
@@ -681,7 +681,7 @@ fn = iii.register_function("etl::extract", extract)
iii.register_trigger({
"type": "cron",
"function_id": fn.id,
- "config": {"expression": "0 * * * *"},
+ "config": {"expression": "0 0 * * * * *"},
})
```
@@ -727,7 +727,7 @@ iii.register_function(reg);
iii.register_trigger(RegisterTriggerInput {
trigger_type: "cron".into(),
function_id: "etl::extract".into(),
- config: json!({ "expression": "0 * * * *" }),
+ config: json!({ "expression": "0 0 * * * * *" }),
metadata: None,
})?;
```
diff --git a/docs/how-to/use-functions-and-triggers.mdx b/docs/how-to/use-functions-and-triggers.mdx
index 574f814a3..c233af32e 100644
--- a/docs/how-to/use-functions-and-triggers.mdx
+++ b/docs/how-to/use-functions-and-triggers.mdx
@@ -481,7 +481,7 @@ iii.registerFunction('math::aggregation', async () => {
iii.registerTrigger({
type: 'cron',
function_id: 'math::aggregation',
- config: { expression: '0 */30 * * * *' }, // every 30 minutes
+ config: { expression: '0 */30 * * * * *' }, // every 30 minutes
});
```
@@ -502,7 +502,7 @@ iii.register_function("math::aggregation", aggregation)
iii.register_trigger({
"type": "cron",
"function_id": "math::aggregation",
- "config": {"expression": "0 */30 * * * *"}, # every 30 minutes
+ "config": {"expression": "0 */30 * * * * *"}, # every 30 minutes
})
```
@@ -532,7 +532,7 @@ iii.register_function(reg);
iii.register_trigger(RegisterTriggerInput {
trigger_type: "cron".into(),
function_id: "math::aggregation".into(),
- config: json!({ "expression": "0 */30 * * * *" }), // every 30 minutes
+ config: json!({ "expression": "0 */30 * * * * *" }), // every 30 minutes
metadata: None,
})?;
```
@@ -620,7 +620,7 @@ async fn main() -> Result<(), Box> {
Node `trigger({ function_id: 'iii::durable::publish', payload: { topic: 'user.created', data: {...} } })`,
Python `trigger({'function_id': 'iii::durable::publish', 'payload': {'topic': 'user.created', 'data': {...}}})`,
Rust `trigger(TriggerRequest::new("iii::durable::publish", json!({"topic": "user.created", "data": {...}})))`.
- See the [Queue module](/workers/iii-queue) for details.
+ See the [Queue worker](/workers/iii-queue) for details.
## Trigger Types
diff --git a/docs/how-to/use-iii-in-the-browser.mdx b/docs/how-to/use-iii-in-the-browser.mdx
index 3019e0bb9..a87ff9971 100644
--- a/docs/how-to/use-iii-in-the-browser.mdx
+++ b/docs/how-to/use-iii-in-the-browser.mdx
@@ -112,7 +112,7 @@ iii.registerFunction('metrics::collect', async () => {
iii.registerTrigger({
type: 'cron',
function_id: 'metrics::collect',
- config: { expression: '*/5 * * * * *' },
+ config: { expression: '*/5 * * * * * *' },
})
```
diff --git a/docs/how-to/use-named-queues.mdx b/docs/how-to/use-named-queues.mdx
index f99ae1efe..1d96bf553 100644
--- a/docs/how-to/use-named-queues.mdx
+++ b/docs/how-to/use-named-queues.mdx
@@ -11,7 +11,7 @@ Enqueue jobs to a specific function by name with configurable retries, concurren
Named queues use the `Enqueue` trigger action. Refer to [Trigger Actions](./trigger-actions) to learn more.
-## Enable the Queue module
+## Enable the Queue worker
```yaml title="iii-config.yaml"
workers:
@@ -30,7 +30,7 @@ workers:
```
- For complete configuration options please refer to [Queue module reference](/workers/iii-queue#configuration).
+ For complete configuration options please refer to [Queue worker reference](/workers/iii-queue#configuration).
## Steps
@@ -68,7 +68,7 @@ workers:
FIFO queues enforce ordering in a queue and they require a `message_group_field` to order on. Queues can also set `backoff_ms` for exponential retry delays. See more on this in the steps below.
- For full configuration options refer to the [Queue module reference](/workers/iii-queue#queue-configuration).
+ For full configuration options refer to the [Queue worker reference](/workers/iii-queue#queue-configuration).
### 2. Enqueue work via trigger action
@@ -294,7 +294,7 @@ Use low concurrency to protect rate-limited APIs. Use high concurrency for embar
Jobs are enqueued and acknowledged immediately — the caller receives a `messageReceiptId` without waiting for processing. The engine delivers each job to the target function, retries failures with exponential backoff, and routes exhausted jobs to the dead-letter queue. Standard queues process jobs concurrently; FIFO queues guarantee per-group ordering.
- For a detailed comparison of standard and FIFO queue behavior — including processing model, ordering guarantees, and flow diagrams — see the [Queue module reference](/workers/iii-queue#standard-vs-fifo-queues). For retry and dead-letter flow, see [Retry and dead-letter flow](/workers/iii-queue#retry-and-dead-letter-flow).
+ For a detailed comparison of standard and FIFO queue behavior — including processing model, ordering guarantees, and flow diagrams — see the [Queue worker reference](/workers/iii-queue#standard-vs-fifo-queues). For retry and dead-letter flow, see [Retry and dead-letter flow](/workers/iii-queue#retry-and-dead-letter-flow).
---
@@ -828,7 +828,7 @@ iii.register_function(reg);
With `concurrency: 3`, at most three emails are in-flight at any time. Failed sends retry with exponential backoff (5s, 10s, 20s, 40s, 80s), protecting the SMTP provider from overload.
- For adapter options (builtin, RabbitMQ, Redis), scenario-based recommendations, and the full queue configuration reference, see the [Queue module reference](/workers/iii-queue#adapter-comparison).
+ For adapter options (builtin, RabbitMQ, Redis), scenario-based recommendations, and the full queue configuration reference, see the [Queue worker reference](/workers/iii-queue#adapter-comparison).
## Remember
diff --git a/docs/how-to/use-topic-queues.mdx b/docs/how-to/use-topic-queues.mdx
index 2c6c62b1c..86e7b31a1 100644
--- a/docs/how-to/use-topic-queues.mdx
+++ b/docs/how-to/use-topic-queues.mdx
@@ -7,7 +7,7 @@ description: 'Subscribe multiple functions to a topic so every published message
Subscribe multiple functions to a topic so that every published message fans out to all subscribers, with each function processing its copy independently. For help deciding between topic-based and named queues, see [When to use which](/workers/iii-queue#when-to-use-which).
-## Enable the Queue module
+## Enable the Queue worker
```yaml title="iii-config.yaml"
workers:
@@ -26,7 +26,7 @@ workers:
```
- For complete configuration options please refer to [Queue module reference](/workers/iii-queue#configuration).
+ For complete configuration options please refer to [Queue worker reference](/workers/iii-queue#configuration).
## Steps
@@ -485,7 +485,7 @@ iii.register_function(RegisterFunction::new_async("orders::create", move |req: V
All three functions receive every `order.created` event independently. If `inventory::reserve` fails and retries, it does not affect `notify::email` or `analytics::track`.
- For adapter options (builtin, RabbitMQ, Redis), scenario-based recommendations, and the full queue configuration reference, see the [Queue module reference](/workers/iii-queue#adapter-comparison).
+ For adapter options (builtin, RabbitMQ, Redis), scenario-based recommendations, and the full queue configuration reference, see the [Queue worker reference](/workers/iii-queue#adapter-comparison).
## Remember
@@ -501,7 +501,7 @@ Producers publish to a topic and return immediately. The engine fans out each me
Handle and redrive failed queue messages
-
+
Full configuration reference for queues and adapters
diff --git a/docs/index.mdx b/docs/index.mdx
index 2a5b726c4..c0c6258ec 100644
--- a/docs/index.mdx
+++ b/docs/index.mdx
@@ -79,7 +79,7 @@ iii --version
Give your AI coding agent full context on iii:
```bash
-npx skills add iii-hq/iii
+npx skillkit add iii-hq/iii/skills
```
Skills covering every iii primitive — works with Claude Code, Cursor, Gemini CLI, Codex, and [30+ other agents](https://agentskills.io).
diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx
index 88323e7ec..3affa4895 100644
--- a/docs/quickstart.mdx
+++ b/docs/quickstart.mdx
@@ -96,42 +96,11 @@ Then open your web browser to: http://localhost:3113/
Give your AI coding agent full context on every iii primitive:
```bash
-npx skills add iii-hq/iii
+npx skillkit add iii-hq/iii/skills
```
This installs skills covering HTTP endpoints, cron scheduling, queues, state management, streams, custom triggers, and more. Works with Claude Code, Cursor, Gemini CLI, Codex, and [30+ other agents](https://agentskills.io).
-
-
-
-```bash
-npx skills add iii-hq/iii
-```
-
-
-
-
-```bash
-npx skillkit install iii-hq/iii --agent cursor
-```
-
-
-
-
-```bash
-npx skillkit install iii-hq/iii --agent gemini-cli
-```
-
-
-
-
-```bash
-npx skillkit install iii-hq/iii --agent codex
-```
-
-
-
-
## Next Steps
diff --git a/docs/workers/iii-cron.mdx b/docs/workers/iii-cron.mdx
index 9e91dc50f..1460434bf 100644
--- a/docs/workers/iii-cron.mdx
+++ b/docs/workers/iii-cron.mdx
@@ -74,19 +74,22 @@ This Worker adds a new Trigger Type: `cron`.
- Standard cron expression defining the schedule. Supports the following format:
+ Cron expression defining the schedule. Accepts 6-field (`second minute hour day month weekday`) or 7-field (`second minute hour day month weekday year`) format — the year field is optional.
```
- * * * * * *
- │ │ │ │ │ │
- │ │ │ │ │ └─── Day of week (0–6, Sun=0)
- │ │ │ │ └───── Month (1–12)
- │ │ │ └─────── Day of month (1–31)
- │ │ └───────── Hour (0–23)
- │ └─────────── Minute (0–59)
- └─────────── Second (0–59)
+ * * * * * * [*]
+ │ │ │ │ │ │ │
+ │ │ │ │ │ │ └── Year (optional, * for any)
+ │ │ │ │ │ └──── Day of week (0–7, Sun=0 or 7)
+ │ │ │ │ └────── Month (1–12)
+ │ │ │ └──────── Day of month (1–31)
+ │ │ └────────── Hour (0–23)
+ │ └──────────── Minute (0–59)
+ └──────────── Second (0–59)
```
+ Both `"0 0 * * * *"` (6-field) and `"0 0 * * * * *"` (7-field) are valid and equivalent.
+
Function ID for conditional execution. The engine invokes it with the cron event; if it returns `false`, the handler function is not called.
@@ -126,7 +129,7 @@ const fn = iii.registerFunction(
iii.registerTrigger({
type: 'cron',
function_id: fn.id,
- config: { expression: '0 0 2 * * *' },
+ config: { expression: '0 0 2 * * * *' },
})
```
@@ -153,7 +156,7 @@ iii.register_function(
iii.register_trigger(RegisterTriggerInput {
trigger_type: "cron".into(),
function_id: "jobs::cleanupOldData".into(),
- config: json!({ "expression": "0 0 2 * * *" }),
+ config: json!({ "expression": "0 0 2 * * * *" }),
metadata: None,
})?;
```
@@ -164,13 +167,15 @@ iii.register_trigger(RegisterTriggerInput {
| Expression | Description |
| -------------------- | ---------------------------------------------- |
-| `0 * * * * *` | Every minute |
-| `0 0 * * * *` | Every hour |
-| `0 0 0 * * *` | Every day at midnight |
-| `0 0 0 * * 0` | Every Sunday at midnight |
-| `0 0 2 * * *` | Every day at 2 AM |
-| `0 */5 * * * *` | Every 5 minutes |
-| `0 0 9-17 * * 1-5` | Every hour from 9 AM to 5 PM, Monday to Friday |
+| `0 * * * * *` | Every minute (6-field) |
+| `0 0 * * * *` | Every hour (6-field) |
+| `0 0 2 * * *` | Every day at 2 AM (6-field) |
+| `0 * * * * * *` | Every minute (7-field) |
+| `0 0 * * * * *` | Every hour (7-field) |
+| `0 0 0 * * * *` | Every day at midnight |
+| `0 0 0 * * 0 *` | Every Sunday at midnight |
+| `0 */5 * * * * *` | Every 5 minutes |
+| `0 0 9-17 * * 1-5 *` | Every hour from 9 AM to 5 PM, Monday to Friday |
## Distributed Execution
diff --git a/docs/workers/iii-queue.mdx b/docs/workers/iii-queue.mdx
index 400d54327..d85a3501e 100644
--- a/docs/workers/iii-queue.mdx
+++ b/docs/workers/iii-queue.mdx
@@ -1,6 +1,6 @@
---
title: 'Queue'
-description: 'Queue module for async job processing with named queues, retries, and dead-letter support.'
+description: 'Queue worker for async job processing with named queues, retries, and dead-letter support.'
---
A module for asynchronous job processing. It supports two modes: **topic-based queues** (register a consumer per topic, emit events) and **named queues** (enqueue function calls via `TriggerAction.Enqueue`, no trigger registration).
diff --git a/docs/workers/iii-stream.mdx b/docs/workers/iii-stream.mdx
index 6c50a216a..fdefcb591 100644
--- a/docs/workers/iii-stream.mdx
+++ b/docs/workers/iii-stream.mdx
@@ -13,7 +13,7 @@ iii-stream
```mermaid
graph LR
- Client[Client] -->|WebSocket| Stream[StreamModule]
+ Client[Client] -->|WebSocket| Stream[iii-stream]
Stream -->|Auth| Engine[Engine]
Engine -->|Context| Stream
Stream -->|Connected| Client
diff --git a/skills/README.md b/skills/README.md
index 3ec22f32c..164613a40 100644
--- a/skills/README.md
+++ b/skills/README.md
@@ -6,23 +6,14 @@ Works with Claude Code, Cursor, Gemini CLI, OpenCode, Amp, Goose, Roo Code, GitH
## Install
-### One command
-
```bash
-npx skills add iii-hq/iii
+npx skillkit add iii-hq/iii/skills
```
-### SkillKit
+### Install a single skill
```bash
-# Install all iii skills
-npx skillkit install iii-hq/iii
-
-# Install a single skill
-npx skillkit install iii-hq/iii --skills=iii-http-endpoints
-
-# Sync skills across all your agents
-npx skillkit sync
+npx skillkit add iii-hq/iii/skills --skills=iii-http-endpoints
```
### Git clone
@@ -43,8 +34,7 @@ git clone https://github.com/iii-hq/iii.git /tmp/iii && cp -r /tmp/iii/skills/ii
If you use multiple agents, SkillKit keeps skills in sync across all of them:
```bash
-# Install once, sync to Claude Code + Cursor + Gemini CLI
-npx skillkit install iii-hq/iii
+npx skillkit add iii-hq/iii/skills
npx skillkit sync --agent claude-code
npx skillkit sync --agent cursor
npx skillkit sync --agent gemini-cli
diff --git a/skills/iii-cron-scheduling/SKILL.md b/skills/iii-cron-scheduling/SKILL.md
index 892f8c8be..cd18ad551 100644
--- a/skills/iii-cron-scheduling/SKILL.md
+++ b/skills/iii-cron-scheduling/SKILL.md
@@ -17,14 +17,15 @@ Comparable to: node-cron, APScheduler, crontab
Use the concepts below when they fit the task. Not every scheduled job needs all of them.
- Cron expressions use a **7-field format**: `second minute hour day month weekday year`
-- **CronModule** evaluates expressions and fires triggers on schedule
+- The cron parser also accepts 5-field (no seconds) and 6-field (no year) expressions, but 7-field is the standard.
+- **iii-cron** evaluates expressions and fires triggers on schedule
- Handlers should be **fast** — enqueue heavy work to a queue instead of blocking the cron handler
- Each cron trigger binds one expression to one function
- Overlapping schedules are fine; each trigger fires independently
## Architecture
- CronModule timer tick
+ iii-cron timer tick
→ registerTrigger type:'cron' expression match
→ registerFunction handler
→ (optional) TriggerAction.Enqueue for heavy work
@@ -35,7 +36,7 @@ Use the concepts below when they fit the task. Not every scheduled job needs all
| ----------------------------------------- | ---------------------------------------- |
| `registerFunction` | Define the handler for the scheduled job |
| `registerTrigger({ type: 'cron' })` | Bind a cron expression to a function |
-| `config: { expression: '0 0 9 * * * *' }` | Cron schedule in 7-field format |
+| `config: { expression: '0 0 9 * * * *' }` | Cron schedule in 7-field format |
## Reference Implementation
@@ -66,7 +67,7 @@ Use the adaptations below when they apply to the task.
## Engine Configuration
-CronModule must be enabled in iii-config.yaml. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
+iii-cron must be enabled in iii-config.yaml. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
## Pattern Boundaries
diff --git a/skills/iii-engine-config/SKILL.md b/skills/iii-engine-config/SKILL.md
index f65f12da3..74df366e5 100644
--- a/skills/iii-engine-config/SKILL.md
+++ b/skills/iii-engine-config/SKILL.md
@@ -1,7 +1,7 @@
---
name: iii-engine-config
description: >-
- Configures the iii engine via iii-config.yaml — modules, adapters, queue
+ Configures the iii engine via iii-config.yaml — workers, adapters, queue
configs, ports, and environment variables. Use when deploying, tuning, or
customizing the engine.
---
@@ -12,46 +12,47 @@ Comparable to: Infrastructure as code, Docker Compose configs
## Key Concepts
-Use the concepts below when they fit the task. Not every deployment needs all modules or adapters.
+Use the concepts below when they fit the task. Not every deployment needs all workers or adapters.
-- **iii-config.yaml** defines the engine port, modules, workers, adapters, and queue configs
+- **iii-config.yaml** defines the engine workers, adapters, and queue configs
- **Environment variables** use `${VAR:default}` syntax (default is optional)
-- **Modules** are the building blocks — each enables a capability (API, state, queue, cron, etc.)
-- **Workers** are external binary modules managed via `iii.toml` and the `iii worker` CLI commands
-- **Adapters** swap storage backends per module: in_memory, file_based, Redis, RabbitMQ
+- **Workers** are the building blocks — each enables a capability (API, state, queue, cron, etc.)
+- **External workers** are binary modules managed via `iii.toml` and the `iii worker` CLI commands
+- **Adapters** swap storage backends per worker: in_memory, file_based, Redis, RabbitMQ
- **Queue configs** control retry count, concurrency, ordering, and backoff per named queue
- The engine listens on port **49134** (WebSocket) for SDK/worker connections
## Architecture
-The iii-config.yaml file is loaded by the iii engine binary at startup. Modules are initialized in order, adapters connect to their backends, and the engine begins accepting worker connections over WebSocket on port 49134. External workers defined in the `workers` section are spawned as child processes automatically.
+The iii-config.yaml file is loaded by the iii engine binary at startup. Workers are initialized in order, adapters connect to their backends, and the engine begins accepting worker connections over WebSocket on port 49134. External workers defined in the config are spawned as child processes automatically.
## iii Primitives Used
-| Primitive | Purpose |
-| ---------------------------------------------- | -------------------------------------- |
-| `modules::api::RestApiModule` | HTTP API server (port 3111) |
-| `modules::stream::StreamModule` | WebSocket streams (port 3112) |
-| `modules::state::StateModule` | Persistent key-value state storage |
-| `modules::queue::QueueModule` | Background job processing with retries |
-| `modules::pubsub::PubSubModule` | In-process event fanout |
-| `modules::cron::CronModule` | Time-based scheduling |
-| `modules::observability::OtelModule` | OpenTelemetry traces, metrics, logs |
-| `modules::http_functions::HttpFunctionsModule` | Outbound HTTP call security |
-| `modules::shell::ExecModule` | Spawn external processes |
-| `modules::bridge_client::BridgeClientModule` | Distributed cross-engine invocation |
-| `modules::telemetry::TelemetryModule` | Anonymous product analytics |
-| `workers` section in iii-config.yaml | External binary workers (worker modules)|
-| `iii.toml` | Worker manifest (name → version) |
-| `iii worker add NAME[@VERSION]` | Install a worker from the registry |
-| `iii worker remove NAME` | Uninstall a worker |
-| `iii worker list` | List installed workers |
-| `iii worker info NAME` | Show registry info for a worker |
+| Primitive | Purpose |
+| -------------------------------- | -------------------------------------- |
+| `iii-http` | HTTP API server (port 3111) |
+| `iii-stream` | WebSocket streams (port 3112) |
+| `iii-state` | Persistent key-value state storage |
+| `iii-queue` | Background job processing with retries |
+| `iii-pubsub` | In-process event fanout |
+| `iii-cron` | Time-based scheduling |
+| `iii-observability` | OpenTelemetry traces, metrics, logs |
+| `iii-http-functions` | Outbound HTTP call security |
+| `iii-exec` | Spawn external processes |
+| `iii-bridge` | Distributed cross-engine invocation |
+| `iii-telemetry` | Anonymous product analytics |
+| `iii-worker-manager` | Worker connection lifecycle |
+| `iii-engine-functions` | Built-in engine functions |
+| `iii.toml` | Worker manifest (name → version) |
+| `iii worker add NAME[@VERSION]` | Install a worker from the registry |
+| `iii worker remove NAME` | Uninstall a worker |
+| `iii worker list` | List installed workers |
+| `iii worker info NAME` | Show registry info for a worker |
## Reference Implementation
See [../references/iii-config.yaml](../references/iii-config.yaml) for the full working example — a complete
-engine configuration with all modules, adapters, queue configs, and environment variable patterns.
+engine configuration with all workers, adapters, queue configs, and environment variable patterns.
## Common Patterns
@@ -64,10 +65,67 @@ Code using this pattern commonly includes, when relevant:
- Prod queues: RabbitMQ adapter with `amqp_url: ${AMQP_URL}` and `queue_mode: quorum`
- Queue config: `queue_configs` with `max_retries`, `concurrency`, `type`, `backoff_ms` per queue name
- Env var with fallback: `port: ${III_PORT:49134}`
-- Health check: `curl http://localhost:3111/health`
+- Health check: `curl http://127.0.0.1:3111/health`
- Ports: 3111 (API), 3112 (streams), 49134 (engine WS), 9464 (Prometheus)
-### Worker Module System
+### Worker Config Format (v0.11+)
+
+Workers in `iii-config.yaml` use `name:` and optional `config:`:
+
+```yaml
+workers:
+ - name: iii-http
+ config:
+ port: 3111
+ host: 127.0.0.1
+
+ - name: iii-state
+ config:
+ adapter:
+ name: kv
+ config:
+ store_method: file_based
+ file_path: ./data/state_store.db
+
+ - name: iii-queue
+ config:
+ adapter:
+ name: builtin
+ config:
+ store_method: file_based
+ file_path: ./data/queue_store
+
+ - name: iii-stream
+ config:
+ port: 3112
+ host: 127.0.0.1
+ adapter:
+ name: kv
+ config:
+ store_method: file_based
+ file_path: ./data/stream_store
+
+ - name: iii-cron
+ config:
+ adapter:
+ name: kv
+
+ - name: iii-pubsub
+ config:
+ adapter:
+ name: local
+
+ - name: iii-observability
+ config:
+ enabled: true
+ service_name: my-service
+ exporter: memory
+ sampling_ratio: 1.0
+ metrics_enabled: true
+ logs_enabled: true
+```
+
+### External Worker System
External workers are installed via the CLI and configured in `iii-config.yaml`:
@@ -87,13 +145,13 @@ Worker config blocks in `iii-config.yaml` use marker comments for automatic mana
```yaml
workers:
# === iii:pdfkit BEGIN ===
- - class: workers::pdfkit::PdfKitWorker
+ - name: pdfkit
config:
output_dir: ./output
# === iii:pdfkit END ===
```
-At startup, the engine resolves each worker class, finds the binary in `iii_workers/`, and spawns it as a child process. Worker binaries are stored in the `iii_workers/` directory.
+At startup, the engine resolves each worker name, finds the binary in `iii_workers/`, and spawns it as a child process.
## Adapting This Pattern
@@ -102,10 +160,11 @@ Use the adaptations below when they apply to the task.
- Start with file_based adapters for development, switch to Redis/RabbitMQ for production
- Define queue configs per workload: high-concurrency for parallel jobs, FIFO for ordered processing
- Use environment variables with defaults for all deployment-sensitive values (URLs, ports, credentials)
-- Enable only the modules you need — unused modules can be omitted from the config
+- Enable only the workers you need — unused workers can be omitted from the config
- Use `iii worker add` to install external workers and auto-generate their config blocks
- Set `max_retries` and `backoff_ms` based on your failure tolerance and SLA requirements
-- Configure `OtelModule` with your collector endpoint and sampling ratio for observability
+- Configure `iii-observability` with your collector endpoint and sampling ratio
+- Use `host: 127.0.0.1` instead of `host: localhost` to avoid IPv4/IPv6 mismatches on macOS
## Pattern Boundaries
diff --git a/skills/iii-getting-started/SKILL.md b/skills/iii-getting-started/SKILL.md
index f6bfc6fc3..4405642d7 100644
--- a/skills/iii-getting-started/SKILL.md
+++ b/skills/iii-getting-started/SKILL.md
@@ -38,7 +38,7 @@ The quickstart includes TypeScript, Python, and Rust workers. If you don't have
iii --config iii-config.yaml
```
-The engine starts and listens for worker connections on `ws://localhost:49134`. The console is available at `http://localhost:3000`.
+The engine starts and listens for worker connections on `ws://localhost:49134`. The REST API is available at `http://localhost:3111`. The console is available at `http://localhost:3113`.
## Step 4: Install the SDK
@@ -128,7 +128,7 @@ iii.register_trigger(RegisterTriggerInput {
## Step 6: Test It
```bash
-curl -X POST http://localhost:3000/hello \
+curl -X POST http://localhost:3111/hello \
-H "Content-Type: application/json" \
-d '{"name": "iii"}'
```
@@ -141,16 +141,10 @@ Expected response:
## Install Agent Skills
-Get all 24 iii skills for your AI coding agent:
+Get all iii skills for your AI coding agent:
```bash
-npx skills add iii-hq/iii
-```
-
-Or with SkillKit:
-
-```bash
-npx skillkit install iii-hq/iii
+npx skillkit add iii-hq/iii/skills
```
Skills teach your agent how to use every iii primitive — HTTP endpoints, cron scheduling, queues, state management, streams, channels, and more. Available for Claude Code, Cursor, Codex, Gemini CLI, and 30+ other agents.
@@ -159,7 +153,7 @@ Skills teach your agent how to use every iii primitive — HTTP endpoints, cron
- Add more functions to the same worker — each gets its own `registerFunction` + `registerTrigger` calls
- Use `::` separator for function IDs to namespace them: `orders::create`, `orders::validate`
-- Add cron triggers with `{ type: 'cron', config: { expression: '0 0 9 * * * *' } }` (7-field, includes seconds)
+- Add cron triggers with `{ type: 'cron', config: { expression: '0 0 9 * * * *' } }` (7-field: sec min hour day month weekday year)
- Add queue triggers with `{ type: 'durable:subscriber', config: { topic: 'my-queue' } }`
- Use `iii.trigger()` to invoke other functions from within a function
- Use `state::get` / `state::set` to persist data across function calls
diff --git a/skills/iii-http-endpoints/SKILL.md b/skills/iii-http-endpoints/SKILL.md
index aa1582d73..594d393b6 100644
--- a/skills/iii-http-endpoints/SKILL.md
+++ b/skills/iii-http-endpoints/SKILL.md
@@ -16,14 +16,14 @@ Use the concepts below when they fit the task. Not every HTTP endpoint needs all
- Each route is a **registered function** bound to a path and method via an HTTP trigger
- The handler receives an **ApiRequest** object containing `body`, `path_params`, `headers`, and `method`
- Handlers return `{ status_code, body, headers }` to shape the HTTP response
-- **RestApiModule** serves all registered routes on port 3111
+- **iii-http** serves all registered routes on port 3111
- Path parameters use colon syntax (e.g. `/users/:id`) and arrive in `path_params`
- **Middleware** can run before handlers via `middleware_function_ids` in the trigger config — see `iii-http-middleware` for details
## Architecture
HTTP request
- → RestApiModule (port 3111)
+ → iii-http (port 3111)
→ registerTrigger route match (method + path)
→ registerFunction handler (receives ApiRequest)
→ { status_code, body, headers } response
diff --git a/skills/iii-http-middleware/SKILL.md b/skills/iii-http-middleware/SKILL.md
index d657125b0..c1a2b8efb 100644
--- a/skills/iii-http-middleware/SKILL.md
+++ b/skills/iii-http-middleware/SKILL.md
@@ -24,7 +24,7 @@ Use the concepts below when they fit the task. Not every middleware setup needs
## Architecture
HTTP request
- → RestApiModule (port 3111)
+ → iii-http (port 3111)
→ Middleware 1 (continue / respond)
→ Middleware 2 (continue / respond)
→ registerFunction handler
diff --git a/skills/iii-observability/SKILL.md b/skills/iii-observability/SKILL.md
index 2c782a59c..66a5b4d7a 100644
--- a/skills/iii-observability/SKILL.md
+++ b/skills/iii-observability/SKILL.md
@@ -69,16 +69,16 @@ Use the adaptations below when they apply to the task.
- Add custom spans around expensive operations (DB queries, LLM calls, external APIs)
- Create domain-specific metrics (orders processed, payment failures, queue depth)
- Use `currentTraceId()` to correlate iii traces with external system logs
-- Configure `OtelModule` in iii-config.yaml for engine-side exporter, sampling ratio, and alerts
+- Configure `iii-observability` in iii-config.yaml for engine-side exporter, sampling ratio, and alerts
- Point the OTLP endpoint at your collector (Jaeger, Grafana Tempo, Datadog Agent)
## Engine Configuration
-OtelModule must be enabled in iii-config.yaml for engine-side traces, metrics, and logs. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
+iii-observability must be enabled in iii-config.yaml for engine-side traces, metrics, and logs. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
## Pattern Boundaries
-- For engine-side OtelModule YAML configuration, prefer `iii-engine-config`.
+- For engine-side iii-observability YAML configuration, prefer `iii-engine-config`.
- For SDK init options and function registration, prefer `iii-functions-and-triggers`.
- Stay with `iii-observability` when the primary problem is SDK-level telemetry: spans, metrics, logs, and trace propagation.
diff --git a/skills/iii-realtime-streams/SKILL.md b/skills/iii-realtime-streams/SKILL.md
index 6d6b2e4b5..1c966e595 100644
--- a/skills/iii-realtime-streams/SKILL.md
+++ b/skills/iii-realtime-streams/SKILL.md
@@ -13,7 +13,7 @@ Comparable to: Socket.io, Pusher, Firebase Realtime
Use the concepts below when they fit the task. Not every stream setup needs all of them.
-- **StreamModule** serves WebSocket connections on the configured stream port (default 3112)
+- **iii-stream** serves WebSocket connections on the configured stream port (default 3112)
- Clients connect at `ws://host:{stream_port}/stream/{stream_name}/{group_id}`
- **stream::set** / **stream::get** / **stream::list** / **stream::delete** provide CRUD for stream items
- **stream::send** pushes events to all connected clients in a stream group
@@ -25,7 +25,7 @@ Use the concepts below when they fit the task. Not every stream setup needs all
Function
→ trigger('stream::set', { stream_name, group_id, item_id, data })
→ trigger('stream::send', { stream_name, group_id, data })
- → StreamModule
+ → iii-stream
→ WebSocket push
→ Connected clients at /stream/{stream_name}/{group_id}
@@ -74,7 +74,7 @@ Use the adaptations below when they apply to the task.
## Engine Configuration
-StreamModule must be enabled in iii-config.yaml with a port and adapter (KvStore or Redis). See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
+iii-stream must be enabled in iii-config.yaml with a port and adapter (KvStore or Redis). See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
## Pattern Boundaries
diff --git a/skills/iii-state-management/SKILL.md b/skills/iii-state-management/SKILL.md
index 86e0209e7..5d662dffa 100644
--- a/skills/iii-state-management/SKILL.md
+++ b/skills/iii-state-management/SKILL.md
@@ -31,7 +31,7 @@ Use the concepts below when they fit the task. Not every state operation needs a
→ trigger('state::update', { scope, key, ops })
→ trigger('state::delete', { scope, key })
→ trigger('state::list', { scope })
- → StateModule → KvStore / Redis adapter
+ → iii-state → KvStore / Redis adapter
## iii Primitives Used
@@ -74,7 +74,7 @@ Use the adaptations below when they apply to the task.
## Engine Configuration
-StateModule must be enabled in iii-config.yaml with a KvStore adapter (file-based or Redis). See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
+iii-state must be enabled in iii-config.yaml with either the `kv` adapter (file-based or in-memory) or the separate `redis` adapter. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
## Pattern Boundaries
diff --git a/skills/iii-state-reactions/SKILL.md b/skills/iii-state-reactions/SKILL.md
index 488a26513..408ffc554 100644
--- a/skills/iii-state-reactions/SKILL.md
+++ b/skills/iii-state-reactions/SKILL.md
@@ -25,7 +25,7 @@ Use the concepts below when they fit the task. Not every state reaction needs al
## Architecture
state::set, state::update, or state::delete
- → StateModule emits change event
+ → iii-state emits change event
→ registerTrigger type:'state' (scope match)
→ condition_function_id check (if configured)
→ registerFunction handler ({ new_value, old_value, key, event_type })
@@ -70,7 +70,7 @@ Use the adaptations below when they apply to the task.
## Engine Configuration
-StateModule must be enabled in iii-config.yaml for state triggers to fire. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
+iii-state must be enabled in iii-config.yaml for state triggers to fire. See [../references/iii-config.yaml](../references/iii-config.yaml) for the full annotated config reference.
## Pattern Boundaries
diff --git a/skills/iii-workflow-orchestration/SKILL.md b/skills/iii-workflow-orchestration/SKILL.md
index b9f48662e..c6e8379cf 100644
--- a/skills/iii-workflow-orchestration/SKILL.md
+++ b/skills/iii-workflow-orchestration/SKILL.md
@@ -77,7 +77,7 @@ Use the adaptations below when they apply to the task.
- The `trackStep` helper pattern (state update + stream event) is reusable for any pipeline
- Failed jobs exhaust retries and move to a DLQ — see the [dead-letter-queues HOWTO](https://iii.dev/docs/how-to/dead-letter-queues)
- DLQ support for named queues is provided by the Builtin and RabbitMQ adapters (Redis is pub/sub only)
-- Cron expressions use 7-position numeric format: `0 0 * * * * *` (every hour)
+- Cron expressions use 7-field format: `0 0 * * * * *` (every hour)
## Engine Configuration
diff --git a/skills/references/dead-letter-queues.py b/skills/references/dead-letter-queues.py
index ccecdff18..4da6b6293 100644
--- a/skills/references/dead-letter-queues.py
+++ b/skills/references/dead-letter-queues.py
@@ -180,7 +180,7 @@ async def auto_redrive(data):
iii.register_trigger({
"type": "cron",
"function_id": "admin::auto-redrive",
- "config": {"expression": "0 0 * * * *"},
+ "config": {"expression": "0 0 * * * * *"},
})
diff --git a/skills/references/functions-and-triggers.py b/skills/references/functions-and-triggers.py
index 238d8405b..df50ffc6a 100644
--- a/skills/references/functions-and-triggers.py
+++ b/skills/references/functions-and-triggers.py
@@ -73,7 +73,7 @@ async def daily_summary(_data):
iii.register_trigger({
"type": "cron",
"function_id": "reports::daily-summary",
- "config": {"expression": "0 9 * * *"},
+ "config": {"expression": "0 0 9 * * * *"},
})
# ---------------------------------------------------------------------------
diff --git a/skills/references/functions-and-triggers.rs b/skills/references/functions-and-triggers.rs
index ee14d64dd..9e381b6cc 100644
--- a/skills/references/functions-and-triggers.rs
+++ b/skills/references/functions-and-triggers.rs
@@ -121,7 +121,7 @@ async fn main() -> Result<(), Box> {
);
iii.register_trigger(
- IIITrigger::Cron(CronTriggerConfig::new("0 0 9 * * *"))
+ IIITrigger::Cron(CronTriggerConfig::new("0 0 9 * * * *"))
.for_function("reports::daily-summary"),
)
.expect("failed to register cron trigger");
diff --git a/skills/references/iii-config.yaml b/skills/references/iii-config.yaml
index 48f193d2c..b395522a3 100644
--- a/skills/references/iii-config.yaml
+++ b/skills/references/iii-config.yaml
@@ -7,13 +7,13 @@ port: 49134
# Config blocks between BEGIN/END markers are auto-managed by the CLI.
# workers:
# # === iii:pdfkit BEGIN ===
-# - class: workers::pdfkit::PdfKitWorker
+# - name: pdfkit
# config:
# output_dir: ./output
# format: pdf
# # === iii:pdfkit END ===
# # === iii:image-processor BEGIN ===
-# - class: workers::image_processor::ImageProcessorWorker
+# - name: image-processor
# config:
# max_width: 2048
# output_format: webp
@@ -31,10 +31,10 @@ port: 49134
# iii worker list # Show installed workers
# iii worker info pdfkit # Show registry details
-modules:
+workers:
# REST API module - exposes HTTP endpoints for triggers and the core API surface.
# Functions with HTTP triggers are served through this module.
- - class: modules::api::RestApiModule
+ - name: iii-http
config:
# TCP port for the HTTP server. Default: 3111
port: 3111
@@ -61,7 +61,7 @@ modules:
# Stream module - real-time WebSocket pub/sub for live data streaming to clients.
# Clients connect via WebSocket to receive pushed updates on subscribed channels.
- - class: modules::stream::StreamModule
+ - name: iii-stream
config:
# TCP port for WebSocket connections. Default: 3112
port: ${STREAM_PORT:3112}
@@ -73,7 +73,7 @@ modules:
# Storage backend for stream state (subscriptions, message history).
adapter:
# KvStore adapter - file or memory-based local storage.
- class: modules::stream::adapters::KvStore
+ name: kv
config:
# Storage mode. Options: file_based (persists to disk), in_memory (lost on restart)
store_method: file_based
@@ -84,22 +84,22 @@ modules:
# Alternative adapters:
# adapter:
# # Redis adapter - distributed storage via Redis server.
- # class: modules::stream::adapters::RedisAdapter
+ # name: redis
# config:
# redis_url: redis://localhost:6379
# adapter:
# # Bridge adapter - connects to another iii engine instance for distributed streaming.
- # class: modules::stream::adapters::Bridge
+ # name: bridge
# config:
# bridge_url: ws://localhost:49134
# State module - persistent key-value storage for function state across invocations.
# Functions use ctx.state.get/set to read/write stateful data.
- - class: modules::state::StateModule
+ - name: iii-state
config:
adapter:
# KvStore adapter - file or memory-based local storage.
- class: modules::state::adapters::KvStore
+ name: kv
config:
# Storage mode. Options: file_based (persists to disk), in_memory (lost on restart)
store_method: file_based
@@ -110,18 +110,18 @@ modules:
# Alternative adapters:
# adapter:
# # Redis adapter - distributed state via Redis server.
- # class: modules::state::adapters::RedisAdapter
+ # name: redis
# config:
# redis_url: redis://localhost:6379
# adapter:
# # Bridge adapter - forwards state operations to another iii engine instance.
- # class: modules::state::adapters::Bridge
+ # name: bridge
# config:
# bridge_url: ws://localhost:49134
# Queue module - background job processing with retries, dead-letter queues, and concurrency control.
# Functions enqueue jobs; subscribers process them asynchronously.
- - class: modules::queue::QueueModule
+ - name: iii-queue
config:
queue_configs:
default:
@@ -130,7 +130,7 @@ modules:
type: standard
adapter:
# Built-in queue adapter - local job queue with persistence options.
- class: modules::queue::BuiltinQueueAdapter
+ name: builtin
config:
# Maximum delivery attempts before moving to dead-letter queue. Default: 3
max_attempts: 3
@@ -151,17 +151,17 @@ modules:
# Alternative adapters:
# adapter:
# # Redis adapter - distributed job queue via Redis.
- # class: modules::queue::RedisAdapter
+ # name: redis
# config:
# redis_url: redis://localhost:6379
# adapter:
# # Bridge adapter - forwards queue operations to another iii engine instance.
- # class: modules::queue::adapters::Bridge
+ # name: bridge
# config:
# bridge_url: ws://localhost:49134
# adapter:
# # RabbitMQ adapter - enterprise message broker integration.
- # class: modules::queue::RabbitMQAdapter
+ # name: rabbitmq
# config:
# amqp_url: amqp://localhost:5672
# max_attempts: 3
@@ -172,27 +172,27 @@ modules:
# PubSub module - in-process event fanout for decoupled function communication.
# Functions publish events; multiple subscribers receive them immediately.
- - class: modules::pubsub::PubSubModule
+ - name: iii-pubsub
config:
adapter:
# Local adapter - in-process pub/sub, events don't cross engine instances.
- class: modules::pubsub::LocalAdapter
+ name: local
# Alternative adapter:
# adapter:
# # Redis adapter - distributed pub/sub across multiple engine instances.
- # class: modules::pubsub::RedisAdapter
+ # name: redis
# config:
# redis_url: redis://localhost:6379
# Cron module - time-based job scheduling using cron expressions.
# Functions with cron triggers execute on schedule.
- - class: modules::cron::CronModule
+ - name: iii-cron
config:
adapter:
# KV-based cron adapter - uses local KV store for distributed lock coordination.
# Warning: Local locks only prevent duplicates within same process; multiple engine
# instances may execute the same cron job. Use RedisCronAdapter for true distributed locking.
- class: modules::cron::KvCronAdapter
+ name: kv
config:
# Lock timeout (ms). Job re-executes if lock holder crashes and lock expires.
lock_ttl_ms: 30000
@@ -207,13 +207,13 @@ modules:
# Alternative adapter:
# adapter:
# # Redis cron adapter - true distributed locking across engine instances.
- # class: modules::cron::RedisCronAdapter
+ # name: redis
# config:
# redis_url: redis://localhost:6379
# Observability module - OpenTelemetry tracing, metrics, logs, and alerting.
# Provides visibility into function execution, performance, and errors.
- - class: modules::observability::OtelModule
+ - name: iii-observability
config:
# Master switch for all observability features.
enabled: ${OTEL_ENABLED:true}
@@ -323,7 +323,7 @@ modules:
# HTTP Functions module - enables HTTP-invoked functions (outbound HTTP calls from the engine).
# Required for functions registered with HttpInvocationConfig.
# The engine makes the HTTP request on behalf of the function and enforces URL security policies.
- - class: modules::http_functions::HttpFunctionsModule
+ - name: iii-http-functions
config:
security:
# URL patterns allowed for outbound requests. Use '*' to allow all URLs.
@@ -347,7 +347,7 @@ modules:
# Useful for setups where workers need to run alongside the engine on the same host.
# Also useful from frameworks that support this style of operation such as Motia.
# # Rust worker:
- # - class: modules::shell::ExecModule
+ # - name: iii-exec
# config:
# watch:
# - workers/**/*.rs
@@ -356,7 +356,7 @@ modules:
# - cargo run --build my-rust-worker
#
# # Python worker:
- # - class: modules::shell::ExecModule
+ # - name: iii-exec
# config:
# watch:
# - workers/**/*.py
@@ -365,7 +365,7 @@ modules:
# - uv run ./src/my_python_worker.py
#
# # Node/TypeScript worker:
- # - class: modules::shell::ExecModule
+ # - name: iii-exec
# config:
# watch:
# - workers/**/*.ts
@@ -376,7 +376,7 @@ modules:
# - pnpm dev:my-node-worker
#
# # Motia worker:
- # - class: modules::shell::ExecModule
+ # - name: iii-exec
# config:
# watch:
# - steps/**/*.ts
@@ -386,7 +386,7 @@ modules:
# Bridge Client module - connects this engine to a remote iii instance for cross-instance
# function invocation. Enables distributed architectures and function federation.
- # - class: modules::bridge_client::BridgeClientModule
+ # - name: iii-bridge
# config:
# # WebSocket URL of the remote iii engine to connect to.
# url: ws://localhost:49134
@@ -409,7 +409,7 @@ modules:
# Telemetry module - anonymous product usage analytics for iii development.
# Helps the team understand usage patterns and prioritize features.
- - class: modules::telemetry::TelemetryModule
+ - name: iii-telemetry
config:
# Enable/disable anonymous telemetry. Set to false to opt out.
enabled: true
diff --git a/skills/references/trigger-conditions.py b/skills/references/trigger-conditions.py
index d7e819a4d..8ed0eac8c 100644
--- a/skills/references/trigger-conditions.py
+++ b/skills/references/trigger-conditions.py
@@ -170,7 +170,7 @@ async def weekday_digest(data):
"type": "cron",
"function_id": "reports::weekday-digest",
"config": {
- "expression": "0 8 * * *",
+ "expression": "0 0 8 * * * *",
"condition_function_id": "conditions::is-weekday",
},
})