Scheduled Deliveries
OpenSRE can deliver recurring reports to messaging providers on a cron schedule. This enables daily reliability digests, weekly alert audits, synthetic test summaries, and custom investigations — all delivered automatically without manual CLI invocations. For human task reminders and proactive work check-ins, useopensre work add --remind-at ... or opensre work schedule-checkin .... Those commands create
the right scheduler entries, support repeated --target provider:chat_id fan-out
delivery, and keep task metadata in the work-item store.
Quick Start
CLI Commands
opensre cron add
Create a new scheduled delivery task.
opensre cron list
Display all configured scheduled tasks in a table, including the loop name, enabled state, next cron fire time, and last run.
opensre cron remove <task_id>
Delete a scheduled task by its ID.
opensre cron run <task_id>
Execute a task immediately (ad-hoc one-shot). Useful for debugging delivery without waiting for the next cron tick.
opensre cron logs <task_id>
Show execution history for a task (newest first). Displays start time, status, message ID, and any errors.
opensre cron start
Start the blocking scheduler daemon. Loads all enabled tasks and fires them according to their cron schedules. Blocks until SIGINT or SIGTERM.
Cron Syntax
Standard 5-field cron expressions:0 9 * * 1-5— weekdays at 09:000 8 * * 1— Mondays at 08:00*/30 * * * *— every 30 minutes0 0 1 * *— first day of each month at midnight
cron add time using APScheduler’s CronTrigger. Invalid expressions are rejected immediately.
Timezone Behavior
- All fire times are internally converted to UTC for dedup consistency
- The
--tzoption accepts any IANA timezone (e.g.,Europe/London,US/Eastern) - DST transitions are handled correctly — the UTC-normalized dedup key ensures no duplicate or missed deliveries across clock changes
Dedup Semantics
The scheduler uses a SQLite-backed claim store with aUNIQUE(task_id, fire_time) constraint:
- When a cron tick fires,
EVENT_JOB_SUBMITTEDcapturesscheduled_run_times[0]and the job uses that UTC-normalizedfire_timefor the claim key - The executor attempts an
INSERT OR IGNOREinto the claim table - If the insert succeeds (rowcount = 1), this instance won the claim and delivers
- If the insert is ignored (rowcount = 0), another instance already claimed it — skip
Credential Resolution
Credentials are resolved lazily at delivery time in this priority order:- Task params — credentials stored in the task definition (not recommended)
- Integration store —
~/.opensre/integrations.json(configured viaopensre integrations) - Environment variables —
TELEGRAM_BOT_TOKEN,SLACK_BOT_TOKEN,DISCORD_BOT_TOKEN,ROCKETCHAT_*
cron add time — they’re picked up from your existing integration configuration.
Task Kinds
Sentry morning digests use a separate CLI (
opensre sentry digest schedule …),
not opensre cron add. See Sentry.
Persistence
- Task definitions are stored in
~/.opensre/scheduler_tasks.json(JSON + filelock) - Execution history is stored in
~/.opensre/scheduler.db(SQLite with WAL mode) - Interactive-shell loop messages are stored in
~/.opensre/scheduler_loop_messages.jsonl - Both survive process restarts —
opensre cron listandopensre cron logsread from disk
REPL
Prefer/loops for user-facing recurring prompt loops:
/loops add sends to every configured default handle it can reach: Telegram when TELEGRAM_BOT_TOKEN and TELEGRAM_DEFAULT_CHAT_ID are configured, Slack when SLACK_WEBHOOK_URL is configured, and the local interactive-shell inbox.
The lower-level /cron slash command forwards to the CLI:
/loops to see named loops with active/draft state, execution time, channels, last run, and next fire time. Use /loops stop <loop_id> to pause a loop without losing it, /loops start <loop_id> to re-enable it, and /loops delete <loop_id> to remove it. Onboarding seeds a few draft starter loops, such as a weekday morning report, so you have concrete examples before creating an active delivery.