cron
v0.21.0Schedule functions with cron expressions - registers the `cron` trigger type.
- macOS: arm64 · x64
- Linux: arm64 · armv7 · x64
- Windows: arm64 · x64 · x86
full markdown
/workers/cron.md. paste it into an llm prompt or pipe it through curl from a worker.install
configuration
- adapter:
name: localdependencies
readme
cron
Schedules registered functions with cron expressions. Any function bound to a
cron trigger is invoked by this worker at the next matching UTC time. The
worker replaces the built-in iii-cron worker while keeping the trigger type
and payload shape stable.
Install
iii worker add croniii worker add fetches the binary, writes a config block into
~/.iii/config.yaml, and the engine starts the worker on the next
iii start.
Quickstart
Register a function and bind it to this worker's trigger type (cron) with an
expression:
use iii_sdk::protocol::RegisterTriggerInput;
use iii_sdk::{errors::Error, register_worker, InitOptions, RegisterFunction};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let iii = register_worker("ws://localhost:49134", InitOptions::default());
iii.register_function(
"jobs::tick",
RegisterFunction::new_async(|payload: Value| async move {
println!("cron fired: {payload}");
Ok::<Value, Error>(json!({"ok": true}))
}),
);
iii.register_trigger(RegisterTriggerInput {
trigger_type: "cron".to_string(),
function_id: "jobs::tick".into(),
config: json!({ "expression": "*/5 * * * * *" }),
metadata: None,
})?;
tokio::signal::ctrl_c().await?;
Ok(())
}The function receives:
{
"trigger": "cron",
"job_id": "<trigger-id>",
"scheduled_time": "2026-07-03T12:00:00+00:00",
"actual_time": "2026-07-03T12:00:00.123456789+00:00"
}Configuration
| Field | Default | Description |
|---|---|---|
adapter.name |
local |
Lock backend. Use local for process-local locking or redis for multi-instance mutual exclusion. |
adapter.config.redis_url |
redis://localhost:6379 |
Redis URL when adapter.name is redis. |
Configuration is owned by the configuration worker - edit it from the
console (Configuration -> Workers -> cron) or seed it once via
--config on first boot. Changing the lock adapter hot-swaps the
scheduler under a serialized apply lock: existing jobs are stopped, re-created
with the new backend, and never run in two scheduler instances at once.
Trigger type
This worker always registers the cron trigger type. Bind a function to it
with:
| Field | Required | Default | Description |
|---|---|---|---|
expression |
yes | - | Cron expression parsed by the Rust cron crate. Six-field sec min hour day month weekday expressions are supported, and a seventh year field is accepted. |
condition_function_id |
no | - | Function invoked before the scheduled handler. Only an explicit JSON false return blocks the fire; missing/null/truthy returns allow it. Condition errors skip that fire. |
All schedules use UTC. Missed fires while the worker is stopped are skipped; there is no catch-up replay.
Requires removing the built-in iii-cron worker
The built-in iii-cron worker also owns the cron trigger type. Two owners
of the same trigger type on one engine collide - whichever registers last
wins - so this worker requires iii-cron to be absent: omit it from the
engine's config.yaml (a config that doesn't list a worker won't run it).
On boot, this worker queries the engine for connected workers and refuses to
start with a clear error if iii-cron is still active, so a stale config
fails loudly instead of silently racing the built-in worker for ownership of
cron.
Parity vs builtin
| Behavior | Builtin | This worker |
|---|---|---|
| Expressions | 6-7 field (cron crate) |
same |
| Timezone | UTC only | same |
| Missed runs | skipped, no catch-up | same |
| Condition | only explicit false blocks |
same |
| Lock TTL | 30s | same |
| Lock backends | kv (process-local), redis | local (process-local), redis |
| Service functions | none | none |
api reference (json)
{
"functions": [
{
"description": "Internal: reload cron configuration from the authoritative store on change.",
"metadata": {},
"name": "cron::on-config-change",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ConfigChangeRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"ok": {
"type": "boolean"
}
},
"required": [
"ok"
],
"title": "ConfigChangeAck",
"type": "object"
}
}
],
"triggers": [
{
"description": "Cron-based scheduled triggers",
"invocation_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Trigger config schema, field-parity with the engine's CronTriggerConfig.",
"properties": {
"condition_function_id": {
"description": "Optional function ID to evaluate before invoking the handler.",
"type": [
"string",
"null"
]
},
"expression": {
"description": "Cron expression (6-field format: sec min hour day month weekday; a 7th year field is accepted).",
"type": "string"
}
},
"required": [
"expression"
],
"title": "CronTriggerSpec",
"type": "object"
},
"metadata": {},
"name": "cron",
"return_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"actual_time": {
"description": "Actual execution time (RFC3339)",
"type": "string"
},
"job_id": {
"description": "Unique job identifier",
"type": "string"
},
"scheduled_time": {
"description": "Scheduled execution time (RFC3339)",
"type": "string"
},
"trigger": {
"description": "Always \"cron\"",
"type": "string"
}
},
"required": [
"actual_time",
"job_id",
"scheduled_time",
"trigger"
],
"title": "CronCallRequest",
"type": "object"
}
}
]
}