--- title: "Scheduled Deliveries (Cron)" description: "Cron-driven recurring reports to Telegram, Slack, and Discord" --- # 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. ## Quick Start ```bash # Add a daily summary to Telegram at 09:00 IST on weekdays opensre cron add --kind daily_summary --cron "0 9 * * 1-5" \ --tz Asia/Kolkata --provider telegram --chat-id # List configured tasks opensre cron list # Run a task immediately (for debugging) opensre cron run # Start the scheduler daemon opensre cron start ``` ## CLI Commands ### `opensre cron add` Create a new scheduled delivery task. | Option | Required | Description | |--------|----------|-------------| | `--kind` | Yes | Task kind: `daily_summary`, `weekly_audit`, `incident_window_replay`, `synthetic_run`, `custom_investigation` | | `--cron` | Yes | Cron expression (5 fields: minute hour day month day_of_week) | | `--tz` | No | IANA timezone (default: `UTC`). Examples: `Europe/London`, `US/Eastern`, `Asia/Kolkata` | | `--provider` | Yes | Messaging provider: `telegram`, `slack`, `discord` | | `--chat-id` | Yes | Target chat/channel ID for the provider | | `--window` | No | Lookback window in hours (default: `24`) | ### `opensre cron list` Display all configured scheduled tasks in a table. ### `opensre cron remove ` Delete a scheduled task by its ID. ### `opensre cron run ` Execute a task immediately (ad-hoc one-shot). Useful for debugging delivery without waiting for the next cron tick. ### `opensre cron logs ` 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: ``` ┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12) │ │ │ │ ┌───────────── day of week (0-6, Mon-Sun) │ │ │ │ │ * * * * * ``` Examples: - `0 9 * * 1-5` — weekdays at 09:00 - `0 8 * * 1` — Mondays at 08:00 - `*/30 * * * *` — every 30 minutes - `0 0 1 * *` — first day of each month at midnight Cron expressions are validated at `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 `--tz` option 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 a `UNIQUE(task_id, fire_time)` constraint: 1. When a cron tick fires, `EVENT_JOB_SUBMITTED` captures `scheduled_run_times[0]` and the job uses that UTC-normalized `fire_time` for the claim key 2. The executor attempts an `INSERT OR IGNORE` into the claim table 3. If the insert succeeds (rowcount = 1), this instance won the claim and delivers 4. If the insert is ignored (rowcount = 0), another instance already claimed it — skip This ensures exactly-once delivery even when multiple scheduler instances run concurrently (e.g., on a laptop and a hosted process). ## Credential Resolution Credentials are resolved lazily at delivery time in this priority order: 1. **Integration store** — `~/.opensre/integrations.json` (configured via `opensre integrations`) 2. **Environment variables** — `TELEGRAM_BOT_TOKEN`, `SLACK_BOT_TOKEN`, `DISCORD_BOT_TOKEN` 3. **Task params** — credentials stored in the task definition (not recommended) This means you don't need to pass credentials at `cron add` time — they're picked up from your existing integration configuration. ## Task Kinds | Kind | Behavior | |------|----------| | `daily_summary` | Runs the investigation pipeline with a daily summary source. Falls back to "no incidents" on empty result, or "pipeline unavailable" on failure. | | `weekly_audit` | Runs the pipeline with a weekly audit source. Same fallback behavior. | | `incident_window_replay` | Replays the investigation pipeline over the configured window. Raises on failure (operator should know). | | `synthetic_run` | Runs the pipeline with a synthetic source. Raises on failure. | | `custom_investigation` | Runs a custom investigation with user-provided params. Credential keys are stripped before forwarding to the pipeline. | ## Persistence - **Task definitions** are stored in `~/.opensre/scheduler_tasks.json` (JSON + filelock) - **Execution history** is stored in `~/.opensre/scheduler.db` (SQLite with WAL mode) - Both survive process restarts — `opensre cron list` and `opensre cron logs` read from disk ## REPL The `/cron` slash command in the interactive shell forwards to the CLI: ``` /cron list /cron add --kind daily_summary --cron "0 9 * * *" --provider telegram --chat-id /cron run ```