Skip to main content

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 /cron in 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 its stdout / 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.py every weekday at 9am" or "run this script in 30 minutes". The assistant uses the manage_cron tool. 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, and GET /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 <= now each 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

VariableDefaultDescription
CRON_ENABLEDfalseMaster switch for the whole feature.
CRON_TICK_SECONDS60Tick interval (clamped 5..3600).
CRON_JOBS_DIR.cronPer-job JSON files; run logs under output/.
CRON_GRACE_WINDOW_SECONDS120Missed-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_SECONDS900Per-run subprocess timeout.
CRON_OUTPUT_MAX_BYTES1048576Cap per captured stdout/stderr log file.

Cron execution also requires CODING_ENABLED and CODING_WORKSPACE_DIR (see Configuration).