Cron Scheduler
The Cron Scheduler runs scripts on a schedule -- a crontab expression, a fixed
interval, or once after a delay -- without relying on the host OS crontab or a
separate daemon. It is a small, file-backed scheduler built into ChatWalaʻau: an
in-process tick loop checks for due jobs (every 60 seconds by default) and runs them.
The feature is opt-in and off by default. Turn it on with CRON_ENABLED=true.
Enabling it
Cron runs are script-only and execute inside the coding workspace, so they reuse the coding tools' security boundary. You need both:
CRON_ENABLED=true
CODING_ENABLED=true
CODING_WORKSPACE_DIR=/path/to/your/workspace
When CRON_ENABLED is off, the scheduler never starts, the agent tool is not
registered, and the portal icon is hidden -- the runtime is unchanged.
Managing jobs
There are three ways to manage jobs; all of them write to the same store.
- Portal UI -- click the clock icon in the sidebar footer (next to the App Info
icon), or type
/cronin the chat input. The portal lists your jobs, lets you create / edit / delete them, and shows a chronological run timeline; click a run to see itsstdout/stderr. A Refresh button re-polls. Deleting a job asks for confirmation before it is removed. - The agent -- ask the assistant in natural language, e.g. "run
scripts/backup.pyevery weekday at 9am" or "run this script in 30 minutes". The assistant uses themanage_crontool. Jobs it creates are enabled by default. - REST API --
GET/POST /api/cron/jobs,GET/PUT/DELETE /api/cron/jobs/{id},GET /api/cron/jobs/{id}/runs, andGET /api/cron/runs/{run_id}. Mutating endpoints require authentication (loopback is exempt).
Each job has a category, a description, an enabled flag, a schedule, and a script (a path inside the workspace, with an optional interpreter and arguments).
Schedule types
- Cron expression -- standard crontab syntax (5 or more fields), e.g.
0 9 * * MON. - Recurring interval -- run every N seconds.
- One-shot -- run once at an absolute time (the agent's "in N minutes" maps to this).
Times are stored in ISO-8601 with timezone. Cron expressions are evaluated in
CRON_TIMEZONE (an IANA name such as Pacific/Honolulu); leave it empty to use the
server's local timezone.
How runs work
- The scheduler stores each job's next run time and only checks
next_run_at <= noweach tick -- it does not re-scan every expression continuously. - It advances the next run time before executing, so if the process crashes mid-run the same job is not re-fired on restart (no crash loop). Like cron, a missed run is not retried.
- If the app was down and a run is overdue by more than the grace window
(
CRON_GRACE_WINDOW_SECONDS, default 120s), the job is fast-forwarded to its next future time instead of firing a backlog of missed runs. - Only one tick runs at a time (a cross-platform lock), so multiple workers do not double-fire a job.
Execution and logs
Scripts run with the workspace as the working directory. The interpreter is taken from
the job's interpreter field, or inferred from the file extension (.py -> Python,
.sh -> sh, .ps1 -> pwsh, .js -> node, ...). Each run has a wall-clock timeout
(CRON_RUN_TIMEOUT_SECONDS) and its stdout / stderr are captured (capped by
CRON_OUTPUT_MAX_BYTES) under CRON_JOBS_DIR/output/{job_id}/{run}/.
Security
Cron runs are unattended, so they are not routed through the per-call tool-approval
prompt. The protections are: the feature is off by default (CRON_ENABLED), execution
requires CODING_ENABLED, every script path is confined to CODING_WORKSPACE_DIR, and
the management API is authenticated. Note that a scheduled job runs your script on a
timer with no human in the loop -- only schedule scripts you trust.
Configuration reference
| Variable | Default | Description |
|---|---|---|
CRON_ENABLED | false | Master switch for the whole feature. |
CRON_TICK_SECONDS | 60 | Tick interval (clamped 5..3600). |
CRON_JOBS_DIR | .cron | Per-job JSON files; run logs under output/. |
CRON_GRACE_WINDOW_SECONDS | 120 | Missed-run tolerance (clamped 120..7200); beyond it a due job fast-forwards. |
CRON_TIMEZONE | (empty) | IANA timezone for cron expressions; empty = system local. |
CRON_RUN_TIMEOUT_SECONDS | 900 | Per-run subprocess timeout. |
CRON_OUTPUT_MAX_BYTES | 1048576 | Cap per captured stdout/stderr log file. |
Cron execution also requires CODING_ENABLED and CODING_WORKSPACE_DIR (see
Configuration).