Scheduled Deliveries
OpenSRE can deliver recurring reports to messaging providers on a cron schedule. Use it for daily reliability digests, weekly alert audits, synthetic test summaries, and custom investigations — delivered automatically without manual CLI runs. 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.
Suggested loops on first launch
When you open the interactive shell (opensre) and no scheduled tasks exist yet, a picker offers three starter loops: a CI/CD check (weekdays 8:00 AM), a task-management sweep of your open issues and PRs (weekdays 9:00 AM), and a daily brief (weekdays 9:00 AM). Picking one runs the loop once so you can see the output, then offers to schedule it — confirm with a plain “yes”. Press Escape to skip; the picker returns on the next launch until you configure a task.
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:CronTrigger, where
0 = Monday and 6 = Sunday (not the Unix cron convention where
0 is Sunday). Prefer 1-5 for weekdays to avoid confusion.
Examples:
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 (for exampleEurope/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 are 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 - Operational breadcrumbs are appended to
~/.opensre/operations_log.jsonlwith loop lifecycle and run status metadata, not prompt or message bodies - Both survive process restarts —
opensre cron listandopensre cron logsread from disk
OPENSRE_OPERATIONS_LOG_PATH to write the operations log somewhere else, or
OPENSRE_OPERATIONS_LOG_DISABLED=1 to turn it off. Set
OPENSRE_OPERATIONS_LOG_MAX_BYTES to change the rotation threshold.
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 or when SLACK_BOT_TOKEN and SLACK_DEFAULT_CHAT_ID are configured, and the local interactive-shell inbox.
When a scheduled loop or skill finishes, the scheduler delivers the report body to those channels without you prompting — you do not need to be in the REPL or Slack chat at fire time. Loops created before Slack was in your default channel list stay inbox-only until you recreate them with Slack in --channels (or rely on the updated defaults for new loops).
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.