Cron overview & expressions
Scheduling concepts, the three schedule types, the cron job types, and crontab expression syntax with timezone support.
Revka’s cron subsystem runs tasks for you automatically — repeating on a cron expression, repeating on a fixed interval, or firing once at a future moment. Each scheduled job runs as a shell command, an agent prompt, or a workflow trigger. Jobs are persisted in a SQLite database and fired by a scheduler loop inside the daemon.
This page is the conceptual entry point: it explains the three schedule types, the three job types, how to turn the whole subsystem on or off with cron.enabled, where jobs are stored, and the full crontab expression syntax — including normalization, weekday renumbering, and IANA timezone support. For the command-line workflow see revka cron; for agent-specific behavior see Agent jobs & delivery; for config-file jobs and scheduler tuning see Declarative jobs & scheduler config.
When to schedule a job
Section titled “When to schedule a job”Reach for cron when you want work to happen on a clock or a timer without you being present — a nightly report, an hourly health check, a one-time reminder, or a recurring workflow run. For one-off work you are about to run yourself, just run the command or prompt directly. The whole subsystem can be surfaced through four interfaces, all backed by the same database:
- The
revka cronCLI - The agent tools
cron_add,cron_list,cron_remove,cron_update,cron_run, andcron_runs(plus the legacyscheduletool) - The
/api/crongateway REST API - The dashboard Cron page
Schedule types
Section titled “Schedule types”Every job uses exactly one of three mutually exclusive schedule shapes, selected by a kind discriminator. In the tool and config surfaces the schedule is a JSON/TOML object; the CLI and gateway API expose each shape through dedicated subcommands or fields.
kind | Behavior | Repeats? | Timezone-aware? | Required field |
|---|---|---|---|---|
cron | Fires on a crontab expression | Yes | Yes (tz) | expr |
every | Fires every N milliseconds | Yes | No | every_ms |
at | Fires once at an absolute UTC time | No | No (always UTC) | at |
// Cron: weekdays at 09:00 New York time{"kind": "cron", "expr": "0 9 * * 1-5", "tz": "America/New_York"}
// Every: once an hour{"kind": "every", "every_ms": 3600000}
// At: once, then auto-removed{"kind": "at", "at": "2026-12-31T23:59:00Z"}Field reference:
| Field | Applies to | Type / rules |
|---|---|---|
expr | cron (required) | 5-, 6-, or 7-field crontab expression |
tz | cron (optional) | IANA timezone name; defaults to UTC |
every_ms | every (required) | Positive integer milliseconds (must be > 0) |
at | at (required) | RFC 3339 UTC datetime; must be in the future at creation |
One-shot jobs and auto-delete
Section titled “One-shot jobs and auto-delete”An at schedule is a one-shot. After it fires successfully it is removed from the database (for at jobs, delete_after_run defaults to true). If a one-shot job fails, it is disabled rather than deleted so its output is preserved for debugging — remove it manually once you have looked. If you set delete_after_run = false on an at job, it is disabled (not deleted) after running, to stop it re-triggering with a past fire time; you handle the disabled state yourself.
Job types
Section titled “Job types”Independent of its schedule, every job has one of three execution backends.
job_type | Runs | Notes |
|---|---|---|
shell | A shell command via sh -c <command> in the workspace directory | 120 s timeout; validated against the security policy |
agent | The Revka agent with a prompt | Injects memory context; blocked in read-only mode; honors session target, model override, and tool allowlist |
workflow | A named Kumiho workflow via POST /api/workflows/run/:name | Managed automatically by workflow YAML triggers — not creatable through cron_add |
# Shell jobrevka cron add '*/15 * * * *' 'echo ok'
# Agent job (trailing argument is a prompt)revka cron add '*/15 * * * *' --agent 'Check server health'workflow jobs exist only because a workflow YAML declared a cron trigger. They are synced into the cron store automatically and carry IDs like __wf_cron_<slug>_<index>. For everything about authoring those, see Variables, expressions & triggers. For session targets, model overrides, tool allowlists, and delivering output to a chat channel, see Agent jobs & delivery.
Cron expression syntax
Section titled “Cron expression syntax”You write standard 5-field crontab expressions:
┌───────────── minute (0–59)│ ┌─────────── hour (0–23)│ │ ┌───────── day of month (1–31)│ │ │ ┌─────── month (1–12 or JAN–DEC)│ │ │ │ ┌───── day of week (0–6, 0 = Sunday — see below)│ │ │ │ │* * * * *All the usual field syntax is supported:
| Syntax | Meaning | Example |
|---|---|---|
* / ? | Any value | * * * * * (every minute) |
a-b | Range | 1-5 (Mon–Fri in the weekday field) |
a,b,c | List | 0,6 (Sun and Sat) |
*/n | Step over the whole range | */5 (every 5th) |
a-b/n | Step within a range | 1-5/2 |
| Named values | Months and days by name | MON-FRI, JAN |
Normalization
Section titled “Normalization”Internally Revka stores cron schedules in a 6-field format (sec min hour day month weekday), so a 5-field expression is normalized before it is saved. This is why the expression printed back by revka cron add and shown in revka cron list differs from what you typed. Two transformations apply to 5-field input:
- A seconds field is prepended.
0 9 * * *becomes0 0 9 * * *— fire at second 0. - The weekday column is renumbered (see below).
You write: 0 9 * * 1-5 (weekdays at 09:00)Stored as: 0 0 9 * * 2-66- and 7-field expressions (which already include seconds, and optionally a year) are assumed to be in the internal format already and are passed through unchanged — they are not renumbered.
Weekday renumbering
Section titled “Weekday renumbering”Standard vixie-cron uses 0 or 7 for Sunday, 1 for Monday, … 6 for Saturday. Revka’s internal cron engine uses a different convention: 1 = Sunday, 2 = Monday, … 7 = Saturday. The normalizer shifts your 5-field weekday values accordingly, which is why 1-5 (Mon–Fri) is stored as 2-6.
Examples
Section titled “Examples”| You write | Meaning |
|---|---|
*/5 * * * * | Every 5 minutes |
0 9 * * * | Every day at 09:00 |
0 9 * * 1-5 | Weekdays at 09:00 |
0 9 * * MON-FRI | Weekdays at 09:00 (named days) |
0 0,12 * * * | Midnight and noon every day |
0 */2 * * * | Every 2 hours, on the hour |
0 2 * * 0 | Sundays at 02:00 |
30 8 1 * * | The 1st of every month at 08:30 |
Timezone support
Section titled “Timezone support”Add a tz field (CLI: --tz) to localize a cron expression to any IANA timezone. The scheduler computes the next fire time in that zone — correctly across daylight-saving transitions — and converts it to UTC for storage. So 0 9 * * * with tz = "America/Los_Angeles" fires at 09:00 Los Angeles time year-round, not at a fixed UTC offset.
revka cron add '0 9 * * *' --tz America/Los_Angeles 'echo morning'{"kind": "cron", "expr": "0 9 * * *", "tz": "America/Los_Angeles"}The timezone name is validated by chrono-tz at creation time; an unknown name is rejected.
The cron.enabled flag
Section titled “The cron.enabled flag”cron.enabled is the global on/off switch for the entire subsystem. When it is false, the scheduler loop does not execute jobs and the cron tools and API endpoints return an error. It defaults to true.
[cron]enabled = true # default; set false to disable all schedulingYou can also toggle it at runtime over the API:
PATCH /api/cron/settingsAuthorization: Bearer <pairing-token>Content-Type: application/json
{"enabled": false}Where jobs are stored
Section titled “Where jobs are stored”All cron jobs and their run history live in a SQLite database at:
<workspace_dir>/cron/jobs.dbIt uses two tables — cron_jobs (definitions) and cron_runs (execution records) — and is created automatically on first use. Schema migrations are applied automatically when the database is opened (newer columns such as allowed_tools and source are added to older databases on open), so upgrading Revka does not require a manual migration. Deleting a job cascades to its cron_runs rows via ON DELETE CASCADE.
Because every surface writes to this one database, jobs created from the CLI, the agent tools, the gateway API, and declarative config all appear together in revka cron list and GET /api/cron.